Rewrite M_STATIC_ASSERT to include an error code and message

Add more control on the default expanded macros to check if arguments are compatible
This commit is contained in:
Patrick Pelissier committed 2020-05-15 14:51:20 +02:00
1 parent c531f805b0
commit 0bedcba32b
1 file changed
+72 -29
+72 -29
View File
@@ -1925,19 +1925,6 @@ m_core_hash (const void *str, size_t length)
/******************** METHODS handling **********************/
/************************************************************/
/* Terminate the compilation of the current unit with an error message.
Either use C11 to get a proper message, or at least a good hint in C99
error shall be a C name, msg a string.
Quite usefull to terminate with a proper error message rather than
a garbage of error due to incorrect code generation in the methods
expansion.
*/
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
# define M_STATIC_FAILURE(error, msg) static_assert(false, #error ": " msg);
#else
# define M_STATIC_FAILURE(error, msg) struct error { int error[-1];};
#endif
/* Helper macros to make M_GET_METHOD works.
List of supported methods for an oplist */
@@ -2211,9 +2198,33 @@ m_core_hash (const void *str, size_t length)
#define M_NO_DEF_TYPE \
M_STATIC_FAILURE(M_LIB_MISSING_METHOD, "The requested operator has no type/subtype/suboplist registered in the given OPLIST. ")
/* Define the default method. */
/* Test if the given variable is a basic C variable:
int, float, enum, bool or compatible.
NOTE: Not perfect, but catch some errors */
#define M_CHECK_DEFAULT_TYPE(a) \
M_STATIC_ASSERT(sizeof (a) <= M_MAX(sizeof(long long), \
M_MAX(sizeof (long double), \
sizeof (uintmax_t))), \
M_LIB_NOT_A_DEFAULT_TYPE, \
"The given variable is too big to be a default type," \
"but the used macro can only be used with such one." \
"It is likely the given oplist is not right.")
/* Check if both variables are of the same type.
The test compare their size.
NOTE: Not perfect but catch some errors */
#define M_CHECK_SAME(a, b) \
M_STATIC_ASSERT(sizeof(a) == sizeof(b), \
M_LIB_NOT_SAME_TYPE, \
"The variable " M_AS_STR(a) " and " M_AS_STR(b) \
" are not of same type.")
/* Define the default method.
NOTE: M_SET_DEFAULT may be called in conditions where a and b
are different but compatible type.
*/
#define M_INIT_DEFAULT(a) ((a) = 0)
#define M_SET_DEFAULT(a,b) ((a) = (b))
#define M_SET_DEFAULT(a,b) (M_CHECK_DEFAULT_TYPE(a), (a) = (b))
#define M_NOTHING_DEFAULT(...) ((void)(__VA_ARGS__))
#define M_EMPTY_DEFAULT(...) ((void)1)
#define M_TRUE_DEFAULT(...) true
@@ -2233,13 +2244,14 @@ m_core_hash (const void *str, size_t length)
/* NOTE: Theses operators are NOT compatible with the '[1]' tricks
if the variable is defined as a parameter of a function
(sizeof (a) is not portable). */
#define M_MOVE_DEFAULT(a,b) (M_STATIC_ASSERT(sizeof(a)==sizeof(b)), M_MEMCPY_DEFAULT(a, b), memset(&(b), 0, sizeof (a)))
#define M_MEMCPY_DEFAULT(a,b) (M_STATIC_ASSERT(sizeof(a)==sizeof(b)), memcpy(&(a), &(b), sizeof (a)))
#define M_MOVE_DEFAULT(a,b) (M_CHECK_SAME(a, b), M_MEMCPY_DEFAULT(a, b), memset(&(b), 0, sizeof (a)))
#define M_MEMCPY_DEFAULT(a,b) (M_CHECK_SAME(a, b), memcpy(&(a), &(b), sizeof (a)))
#define M_MEMSET_DEFAULT(a) (memset(&(a), 0, sizeof (a)))
#define M_MEMCMP1_DEFAULT(a,b) (M_STATIC_ASSERT(sizeof(a)==sizeof(b)), memcmp(&(a), &(b), sizeof (a)) == 0)
#define M_MEMCMP2_DEFAULT(a,b) (M_STATIC_ASSERT(sizeof(a)==sizeof(b)), memcmp(&(a), &(b), sizeof (a)))
#define M_MEMCMP1_DEFAULT(a,b) (M_CHECK_SAME(a, b), memcmp(&(a), &(b), sizeof (a)) == 0)
#define M_MEMCMP2_DEFAULT(a,b) (M_CHECK_SAME(a, b), memcmp(&(a), &(b), sizeof (a)))
#define M_SWAP_DEFAULT(el1, el2) do { \
char _tmp[sizeof (el1)]; \
M_CHECK_SAME(el1, el2); \
memcpy(&_tmp, &(el1), sizeof (el1)); \
memcpy(&(el1), &(el2), sizeof (el1)); \
memcpy(&(el2), &_tmp, sizeof (el1)); \
@@ -2248,11 +2260,11 @@ m_core_hash (const void *str, size_t length)
/* NOTE: Theses operators are to be used with the '[1]' tricks
if the variable is defined as a parameter of a function
(sizeof (a) is not portable). */
#define M_MOVE_A1_DEFAULT(a,b) (M_STATIC_ASSERT(sizeof(a[0])==sizeof(b[0])), M_MEMCPY_A1_DEFAULT(a, b), M_MEMSET_A1_DEFAULT(b))
#define M_MEMCPY_A1_DEFAULT(a,b) (M_STATIC_ASSERT(sizeof(a[0])==sizeof(b[0])), memcpy(&(a[0]), &(b[0]), sizeof (a[0])))
#define M_MOVE_A1_DEFAULT(a,b) (M_CHECK_SAME(a[0], b[0]), M_MEMCPY_A1_DEFAULT(a, b), M_MEMSET_A1_DEFAULT(b))
#define M_MEMCPY_A1_DEFAULT(a,b) (M_CHECK_SAME(a[0], b[0]), memcpy(&(a[0]), &(b[0]), sizeof (a[0])))
#define M_MEMSET_A1_DEFAULT(a) (memset(&(a[0]), 0, sizeof (a[0])))
#define M_MEMCMP1_A1_DEFAULT(a,b) (M_STATIC_ASSERT(sizeof(a[0])==sizeof(b[0])), memcmp(&(a[0]), &(b[0]), sizeof (a[0])) == 0)
#define M_MEMCMP2_A1_DEFAULT(a,b) (M_STATIC_ASSERT(sizeof(a[0])==sizeof(b[0])), memcmp(&(a[0]), &(b[0]), sizeof (a[0])))
#define M_MEMCMP1_A1_DEFAULT(a,b) (M_CHECK_SAME(a[0],b[0]), memcmp(&(a[0]), &(b[0]), sizeof (a[0])) == 0)
#define M_MEMCMP2_A1_DEFAULT(a,b) (M_CHECK_SAME(a[0], b[0]), memcmp(&(a[0]), &(b[0]), sizeof (a[0])))
#define M_HASH_A1_DEFAULT(a) (m_core_hash((const void*) &(a[0]), sizeof (a[0])) )
/* Default oplist for plain structure */
@@ -2661,7 +2673,12 @@ m_core_hash (const void *str, size_t length)
} while (0)
#endif
/* Always perform a runtime check of the condition */
/************************************************************/
/********************* ERROR handling **********************/
/************************************************************/
/* Always perform a runtime check of the given condition */
#ifndef M_ASSERT_INIT
#define M_ASSERT_INIT(expr, object) { \
if (!(expr)) { \
@@ -2671,12 +2688,38 @@ m_core_hash (const void *str, size_t length)
} } while (0)
#endif
/* Force a compilation error if condition is false
In this case, it raises "error: negative width in bit-field <anonymous>" */
#if defined(__cplusplus)
#define M_STATIC_ASSERT(_e) (assert(_e))
/* Terminate the compilation of the current unit with an error message.
The error is classidied as error
with an optional message detailling the error.
Either use C11 to get a proper message, or at least a good hint in C99
error shall be a C name, msg a string.
Quite usefull to terminate with a proper error message rather than
a garbage of error due to incorrect code generation in the methods
expansion.
*/
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
# define M_STATIC_FAILURE(error, msg) static_assert(false, #error ": " msg);
#else
#define M_STATIC_ASSERT(_e) ((void) sizeof(struct { int a:1; int :-!(_e); }))
# define M_STATIC_FAILURE(error, msg) struct error { int error : 0;};
#endif
/* Test at compile time if the given condition is true.
The error is classidied as error
with an optional message detailling the error.
NOTE: Use bitfield to be compatible with most compilers
(so that it properly displays 'error' on the command line
NOTE: Cannot use C11 Static Assert as is not usable in expression.
NOTE: In C++, use of lambda to encapsulate static_assert in
an expression.
*/
#if defined(__cplusplus)
# define M_STATIC_ASSERT(cond, error, msg) \
([] { static_assert(cond, #error ": " msg); } ())
#else
# define M_STATIC_ASSERT(cond, error, msg) \
((void) sizeof(struct { int error : !!(cond);}))
#endif