From 097d282b324df663e275e1f65ea7b6c65bab87fc Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 16 Apr 2012 17:23:15 +0100 Subject: [PATCH] Add _COGL_STATIC_ASSERT macro This adds a _COGL_STATIC_ASSERT macro that can be used for compile time assertions in C code. If supported by the compiler this macro uses _Static_assert so that a message can be printed out if the assertion fails. Reviewed-by: Neil Roberts (cherry picked from commit 465b39a764f2720e77678cafa56acb0e69007ffd) --- cogl/cogl-bitmask.c | 5 +++- cogl/cogl-pipeline.c | 14 ++++++++-- cogl/cogl-util.h | 51 ++++++++++++++++++++++++++++++++++++ configure.ac | 13 +++++++++ tests/conform/test-bitmask.c | 2 +- 5 files changed, 81 insertions(+), 4 deletions(-) diff --git a/cogl/cogl-bitmask.c b/cogl/cogl-bitmask.c index 1d2f2f800..73c9d7cde 100644 --- a/cogl/cogl-bitmask.c +++ b/cogl/cogl-bitmask.c @@ -37,7 +37,10 @@ /* This code assumes that we can cast an unsigned long to a pointer and back without losing any data */ -G_STATIC_ASSERT (sizeof (unsigned long) <= sizeof (void *)); +_COGL_STATIC_ASSERT (sizeof (unsigned long) <= sizeof (void *), + "This toolchain breaks Cogl's assumption that it can " + "safely cast an unsigned long to a pointer without " + "loosing data"); #define ARRAY_INDEX(bit_num) \ ((bit_num) / (sizeof (unsigned long) * 8)) diff --git a/cogl/cogl-pipeline.c b/cogl/cogl-pipeline.c index a5f938cb9..0b37695c2 100644 --- a/cogl/cogl-pipeline.c +++ b/cogl/cogl-pipeline.c @@ -2590,8 +2590,13 @@ _cogl_pipeline_init_layer_state_hash_functions (void) layer_state_hash_functions[_index] = _cogl_pipeline_layer_hash_fragment_snippets_state; + { /* So we get a big error if we forget to update this code! */ - g_assert (COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT == 10); + _COGL_STATIC_ASSERT (COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT == 10, + "Don't forget to install a hash function for new " + "pipeline state and update assert at end of " + "_cogl_pipeline_init_state_hash_functions"); + } } static gboolean @@ -2697,8 +2702,13 @@ _cogl_pipeline_init_state_hash_functions (void) state_hash_functions[COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS_INDEX] = _cogl_pipeline_hash_fragment_snippets_state; + { /* So we get a big error if we forget to update this code! */ - g_assert (COGL_PIPELINE_STATE_SPARSE_COUNT == 16); + _COGL_STATIC_ASSERT (COGL_PIPELINE_STATE_SPARSE_COUNT == 16, + "Make sure to install a hash function for " + "newly added pipeline state and update assert " + "in _cogl_pipeline_init_state_hash_functions"); + } } unsigned int diff --git a/cogl/cogl-util.h b/cogl/cogl-util.h index 199a11b35..5cd6897f4 100644 --- a/cogl/cogl-util.h +++ b/cogl/cogl-util.h @@ -34,6 +34,11 @@ #include #endif +/* Double check that config.h has been included */ +#if !defined (GETTEXT_PACKAGE) && !defined (_COGL_IN_TEST_BITMASK) +#error "config.h must be included before including cogl-util.h" +#endif + /* When compiling with Visual Studio, symbols that represent data that are exported out of the DLL need to be marked with the dllexport attribute. */ @@ -227,4 +232,50 @@ _cogl_util_pixel_format_from_masks (unsigned long r_mask, int depth, int bpp, int byte_order); +/* Since we can't rely on _Static_assert always being available for + * all compilers we have limited static assert that can be used in + * C code but not in headers. + */ +#define _COGL_TYPEDEF_ASSERT(EXPRESSION) \ + typedef struct { char Compile_Time_Assertion[(EXPRESSION) ? 1 : -1]; } \ + G_PASTE (_GStaticAssert_, __LINE__) + +/* _COGL_STATIC_ASSERT: + * @expression: An expression to assert evaluates to true at compile + * time. + * @message: A message to print to the console if the assertion fails + * at compile time. + * + * Allows you to assert that an expression evaluates to true at + * compile time and aborts compilation if not. If possible message + * will also be printed if the assertion fails. + * + * Note: Only Gcc >= 4.6 supports the c11 _Static_assert which lets us + * print a nice message if the compile time assertion fails. + * + * Note: this assertion macro can only be used in C code where it is + * valid to use a typedef. This macro should not be used in headers + * because we can't guarantee a unique name for the typedef due to + * the name being based on the line number of the file it's used in. + * + * Although we can remove this limitation if the compiler supports + * _Static_assert we currently choose to maintain the limitation in + * any case to help ensure we don't accidentally create code that + * doesn't compile on some toolchains because we forgot about this + * limitation. + */ +#ifdef HAVE_STATIC_ASSERT +#define _COGL_STATIC_ASSERT(EXPRESSION, MESSAGE) \ + _Static_assert (EXPRESSION, MESSAGE); \ + _COGL_TYPEDEF_ASSERT(EXPRESSION) +#else +#define _COGL_STATIC_ASSERT(EXPRESSION, MESSAGE) \ + _COGL_TYPEDEF_ASSERT(EXPRESSION) + +/* So that we can safely use _Static_assert() if we want to add + * assertions to internal headers we define it to a NOP here + * if it's not supported by the compiler. */ +#define _Static_assert(EXPRESSION, MESSAGE) +#endif + #endif /* __COGL_UTIL_H */ diff --git a/configure.ac b/configure.ac index 3f0a992d6..c8c785501 100644 --- a/configure.ac +++ b/configure.ac @@ -935,6 +935,19 @@ AM_PROG_CC_C_O AC_ISC_POSIX AC_C_CONST +dnl ============================================================ +dnl Compiler features +dnl ============================================================ +AC_TRY_COMPILE([], +[ +_Static_assert (1, ""); +int +main (int argc, char **argv) +{ + return 0; +} +], +[AC_DEFINE([HAVE_STATIC_ASSERT], [1], [Whether _Static_assert can be used or not])]) dnl ================================================================ dnl Libtool stuff. diff --git a/tests/conform/test-bitmask.c b/tests/conform/test-bitmask.c index 3e5e17373..fdf756648 100644 --- a/tests/conform/test-bitmask.c +++ b/tests/conform/test-bitmask.c @@ -9,9 +9,9 @@ within Cogl. Cogl doesn't export the symbols for this data type so we just directly include the source instead */ +#define _COGL_IN_TEST_BITMASK #include #include -#define _COGL_IN_TEST_BITMASK #include typedef struct