From 60399f25ec2bd21f6d538573724f6bb0fe480453 Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Fri, 24 Feb 2017 23:28:12 +0100 Subject: [PATCH] Document some macros of 'm-core' --- README.md | 182 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- m-core.h | 64 ++++++++++--------- 2 files changed, 212 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index cc16f807..b372b1fa 100644 --- a/README.md +++ b/README.md @@ -96,10 +96,11 @@ Other headers offering other functionality are: Each containers define their iterators. -External Sources ----------------- +External Reference +------------------ -Many other implementation of generic container libraries exist in C. For example: +Many other implementation of generic container libraries exist in C. +For example, a non exhaustive list can be: * [KLIB](https://github.com/attractivechaos/klib) * [SGLIB](http://sglib.sourceforge.net/) @@ -127,6 +128,14 @@ M\*LIB's category is the last one. Some macros are also defined to access struct M\*LIB main added value compared to other libraries is its op-list support allowing for proper recursive definition of function (list of array of dictionary are perfectly supported by M\*LIB). +For the macro-preprocessing part, other libraries also exist, like: + +* [P99](http://p99.gforge.inria.fr/p99-html/) +* [C99 Lambda](https://github.com/Leushenko/C99-Lambda) +* [MAP MACRO](https://github.com/swansontec/map-macro) +* [C Preprocessor Tricks, Tips and Idioms](https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms) +* [CPP MAGIC](http://jhnet.co.uk/articles/cpp_magic) + How to use ---------- @@ -1394,7 +1403,172 @@ Example: The following methods are available: -TODO: document the API. +##### M\_ASSUME(cond) + +M\_ASSUME is equivalent to assert, but gives hints to compiler +about how to optimize the code if NDEBUG is defined. + +##### M\_LIKELY(cond) / M\_UNLIKELY(cond) + +M\_LIKELY / M\_UNLIKELY gives hints on the compiler of the likehood +of the given condition. + +##### M\_MAX\_NB\_ARGUMENT + +Maximum number of argument which can be handled by this header. + +##### M\_C(a,b), M\_C3(a,b,c), M\_C4(a,b,c,d) + +Return a symbol corresponding to the concatenation of the input arguments. + +##### M\_INC(number) + +Increment the number given as argument (from [0..29[) and return +a pre-processing token corresponding to this value (meaning it is evaluated +at macro processing stage, not at compiler stage). +If number is not within the range, the behavior is undefined. + +##### M\_DEC(number) + +Decrement the number given as argument (from [0..29[) and return +a pre-processing token corresponding to this value (meaning it is evaluated +at macro processing stage, not at compiler stage). +If number is not within the range, the behavior is undefined. + +##### M\_RET\_ARG1(arglist,...) / M\_RET\_ARG2(...) / M\_RET\_ARG3(...) / M\_RET\_ARG27(...) + +Return the argument 1 of the given arglist (respectively 2, 3, and 27). +The argument shall exist in the arglist. + +##### M\_BOOL(cond) + +Convert an integer or a symbol into 0 (if 0) or 1 (if not 0 or symbol unknown). +Return a pre-processing token corresponding to this value (meaning it is evaluated +at macro processing stage, not at compiler stage). + +##### M\_INV(cond) + +Inverse 0 into 1 and 1 into 0. It is undefined if cond is not 0 or 1 +(use M\_BOOL to convert). +Return a pre-processing token corresponding to this value (meaning it is evaluated +at macro processing stage, not at compiler stage). + +##### M\_AND(cond1, cond2) + +Perform a logical 'and' between cond1 and cond2. +cond1 and cond2 shall be 0 or 1 otherwise it is undefined. +Return a pre-processing token corresponding to this value (meaning it is evaluated +at macro processing stage, not at compiler stage). + +##### M\_OR(cond1, cond2) + +Perform a logical 'or' between cond1 and cond2. +cond1 and cond2 shall be 0 or 1 otherwise it is undefined. +Return a pre-processing token corresponding to this value (meaning it is evaluated +at macro processing stage, not at compiler stage). + +##### M\_IF(cond)(action\_if\_true, action\_if\_false) + +Return the pre-processing token 'action_if_true' if 'cond' is true, action\_if\_false otherwise (meaning it is evaluated +at macro processing stage, not at compiler stage). +cond shall be 0 or 1 otherwise it is undefined. + +##### M\_COMMA\_P(arglist) + +Return 1 if there is a comma inside the argument list, 0 otherwise. +Return a pre-processing token corresponding to this value (meaning it is evaluated +at macro processing stage, not at compiler stage). + +##### M\_EMPTY\_P(expression) + +Return 1 if the argument 'expression' is 'empty', 0 otherwise. +Return a pre-processing token corresponding to this value (meaning it is evaluated +at macro processing stage, not at compiler stage). + +##### M\_DEFERRED\_COMMA + +Return a comma ',' at a later phase of the macro processing steps. + +##### M\_IF\_EMPTY(cond)(action\_if\_true, action\_if\_false) + +Return the pre-processing token 'action_if_true' if 'cond' is empty, action\_if\_false otherwise (meaning it is evaluated +at macro processing stage, not at compiler stage). +cond shall be 0 or 1 otherwise it is undefined. + +##### M\_PARENTHESIS\_P(expression) + +Return 1 if the argument 'expression' starts a parenthesis and ends it +(like '(...)'), 0 otherwise. +Return a pre-processing token corresponding to this value (meaning it is evaluated +at macro processing stage, not at compiler stage). + +##### M\_DELAY1(expr) / M\_DELAY2(expr) / M\_DELAY3(expr) / M\_DELAY4(expr) / M\_ID + +Delay the evaluation by 1, 2, 3 or 4 steps. +This is necessary to write macros which are recursive. +The argument is a macro-function which has to be deferred. +M\_ID is an equivalent of M\_DELAY1. + +##### M\_EVAL(expr) + +Perform a complete stage evaluation of the given expression, +removing recursive expression within it. +Only ONE M\_EVAL expression is expected in the evaluation chain. +Can not be chained. + +##### M\_APPLY(func, args...) + +Apply 'func' to '(args...) ensuring +that a() isn't evaluated until all 'args' have been also evaluated. +It is used to delay evaluation. + +##### M\_APPLY\_FUNC(func, arg1) + +Apply 'func' to 'arg1' if 'arg1' is not empty. + +##### M\_APPLY\_FUNC2(func, arg1, arg2) + +Apply 'func' to 'arg1, arg2' if 'arg2' is not empty. + +##### M\_MAP(func, args...) + +Apply 'func' to each argument of the 'args...' list of argument. + +##### M\_MAP2(func, data, args...) + +Apply 'func' to each couple '(data, argument)' +with argument an element of the 'args...' list. + +##### M\_MAP\_PAIR(func, args...) + +Map a macro to all given pair of arguments (Using recursivity). +Can not be chained. + +##### M\_REDUCE(func1, func2, args...) + +Map the macro func1 to all given arguments +and reduce all theses computation with the macro 'func2' (using recursivity) +Can not be chained. + +##### M\_REDUCE2(func1, func2, data, args...) + +Map the macro func1 to all pair (data, arg) of the given argument list +and reduce all theses computation with the macro 'func2'. +Do not use recursivity. + +##### M\_SEQ(init, end, macro, data) + +Generate a sequence of number from 'init' to 'end' +and apply to the macro the pair '(data, num)' for each number 'num' +of the sequence (using recursivity). + +##### M\_NARGS(args...) + +Return the number of argument of the given list. +Doesn't work for empty argument. + + +TODO: Document the API. ###M-MUTEX diff --git a/m-core.h b/m-core.h index 060a0a77..94a5fb22 100644 --- a/m-core.h +++ b/m-core.h @@ -32,6 +32,8 @@ /************************ Compiler Macro ***********************/ /***************************************************************/ +/* M_ASSUME is equivalent to assert, but gives hints to compiler + about how to optimize the code if NDEBUG is defined. */ #if !defined(NDEBUG) # define M_ASSUME(x) assert(x) #elif defined(__GNUC__) \ @@ -45,6 +47,8 @@ # define M_ASSUME(x) assert(x) #endif +/* M_LIKELY / M_UNLIKELY gives hints on the compiler of the likehood + of the given condition */ #ifdef __GNUC__ # define M_LIKELY(cond) __builtin_expect(!!(cond), 1) # define M_UNLIKELY(cond) __builtin_expect(!!(cond), 0) @@ -72,7 +76,7 @@ #define M_C4I(a, b, c, ...) a ## b ## c ## __VA_ARGS__ #define M_C4(a, b, c, ...) M_C4I(a ,b, c, __VA_ARGS__) -/* Increment the number given in argument (from [0..29[) */ +/* Increment the number given as argument (from [0..29[) */ #define M_INC(x) M_C(M_INC_, x) #define M_INC_0 1 #define M_INC_1 2 @@ -159,45 +163,45 @@ /* Convert an integer or a symbol into 0 (if 0) or 1 (if not 0). 1 if symbol unknown */ -#define M_TOBOOL_0 1, 0, -#define M_BOOL(x) M_RET_ARG2(M_C(M_TOBOOL_, x), 1, useless) +#define M_TOBOOLI_0 1, 0, +#define M_BOOL(x) M_RET_ARG2(M_C(M_TOBOOLI_, x), 1, useless) /* Inverse 0 into 1 and 1 into 0 */ -#define M_INV_0 1 -#define M_INV_1 0 -#define M_INV(x) M_C(M_INV_, x) +#define M_INVI_0 1 +#define M_INVI_1 0 +#define M_INV(x) M_C(M_INVI_, x) /* Perform a AND between the inputs */ -#define M_AND_00 0 -#define M_AND_01 0 -#define M_AND_10 0 -#define M_AND_11 1 -#define M_AND(x,y) M_C3(M_AND_, x, y) +#define M_ANDI_00 0 +#define M_ANDI_01 0 +#define M_ANDI_10 0 +#define M_ANDI_11 1 +#define M_AND(x,y) M_C3(M_ANDI_, x, y) /* Perform a OR between the inputs */ -#define M_OR_00 0 -#define M_OR_01 1 -#define M_OR_10 1 -#define M_OR_11 1 -#define M_OR(x,y) M_C3(M_OR_, x, y) +#define M_ORI_00 0 +#define M_ORI_01 1 +#define M_ORI_10 1 +#define M_ORI_11 1 +#define M_OR(x,y) M_C3(M_ORI_, x, y) /* M_IF Macro : M_IF(42)(Execute if true, execute if false) Example: M_IF(0)(true_action, false_action) --> false_action */ -#define M_IF_0(true_macro, ...) __VA_ARGS__ -#define M_IF_1(true_macro, ...) true_macro -#define M_IF(c) M_C(M_IF_, M_BOOL(c)) +#define M_IFI_0(true_macro, ...) __VA_ARGS__ +#define M_IFI_1(true_macro, ...) true_macro +#define M_IF(c) M_C(M_IFI_, M_BOOL(c)) /* Return 1 if comma inside the argument list, 0 otherwise */ #define M_COMMA_P(...) M_RET_ARG27(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, useless) /* Return 1 if the argument is 'empty', 0 otherwise. Handle: EMPTY_P(), EMPTY_P(x), EMPTY_P(()) and EMPTY_P(,) and EMPTY_P(f) with #define f() 2,3 */ -#define M_EMPTY_DETECT(...) 0, 1, -#define M_EMPTY_P_C1(...) M_COMMA_P(M_EMPTY_DETECT __VA_ARGS__ () ) -#define M_EMPTY_P_C2(...) M_COMMA_P(M_EMPTY_DETECT __VA_ARGS__) -#define M_EMPTY_P_C3(...) M_COMMA_P(__VA_ARGS__ () ) -#define M_EMPTY_P(...) M_AND(M_EMPTY_P_C1(__VA_ARGS__), M_INV(M_OR(M_OR(M_EMPTY_P_C2(__VA_ARGS__), M_COMMA_P(__VA_ARGS__)),M_EMPTY_P_C3(__VA_ARGS__)))) +#define M_EMPTYI_DETECT(...) 0, 1, +#define M_EMPTYI_P_C1(...) M_COMMA_P(M_EMPTYI_DETECT __VA_ARGS__ () ) +#define M_EMPTYI_P_C2(...) M_COMMA_P(M_EMPTYI_DETECT __VA_ARGS__) +#define M_EMPTYI_P_C3(...) M_COMMA_P(__VA_ARGS__ () ) +#define M_EMPTY_P(...) M_AND(M_EMPTYI_P_C1(__VA_ARGS__), M_INV(M_OR(M_OR(M_EMPTYI_P_C2(__VA_ARGS__), M_COMMA_P(__VA_ARGS__)),M_EMPTYI_P_C3(__VA_ARGS__)))) /* Generate a comma later in the next evaluation pass. */ #define M_DEFERRED_COMMA , @@ -207,8 +211,8 @@ #define M_IF_EMPTY(...) M_IF(M_EMPTY_P(__VA_ARGS__)) /* Return 1 if argument is "()" or "(x)" */ -#define M_PARENTHESIS_DETECT(...) 0, 1, -#define M_PARENTHESIS_P(x) M_RET_ARG2(M_PARENTHESIS_DETECT x, 0, useless) +#define M_PARENTHESISI_DETECT(...) 0, 1, +#define M_PARENTHESIS_P(x) M_RET_ARG2(M_PARENTHESISI_DETECT x, 0, useless) /* Necessary macros to handle recursivity */ /* Delay the evaluation by one level or two or three or ... */ @@ -248,18 +252,18 @@ /* Map a macro to all given arguments (non recursive version) */ /* Example: M_MAP(f,a, b, c) ==> f(a) f(b) f(c) */ -#define M_MAP_0(func, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, ...) \ +#define M_MAPI_0(func, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, ...) \ M_APPLY_FUNC(func, a) M_APPLY_FUNC(func, b) M_APPLY_FUNC(func, c) M_APPLY_FUNC(func, d) M_APPLY_FUNC(func, e) \ M_APPLY_FUNC(func, f) M_APPLY_FUNC(func, g) M_APPLY_FUNC(func, h) M_APPLY_FUNC(func, i) M_APPLY_FUNC(func, j) \ M_APPLY_FUNC(func, k) M_APPLY_FUNC(func, l) M_APPLY_FUNC(func, m) M_APPLY_FUNC(func, n) M_APPLY_FUNC(func, o) \ M_APPLY_FUNC(func, p) M_APPLY_FUNC(func, q) M_APPLY_FUNC(func, r) M_APPLY_FUNC(func, s) M_APPLY_FUNC(func, t) \ M_APPLY_FUNC(func, u) M_APPLY_FUNC(func, v) M_APPLY_FUNC(func, w) M_APPLY_FUNC(func, x) M_APPLY_FUNC(func, y) \ M_APPLY_FUNC(func, z) -#define M_MAP(f, ...) M_MAP_0(f, __VA_ARGS__, , , , , , , , , , , , , , , , , , , , , , , , , , ) +#define M_MAP(f, ...) M_MAPI_0(f, __VA_ARGS__, , , , , , , , , , , , , , , , , , , , , , , , , , ) /* Map a macro to all given arguments with one additional fixed data*/ /* Example: M_MAP2(f,data,a, b, c) ==> f(data,a) f(data,b) f(data,c) */ -#define M_MAP2_0(func, data, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, ...) \ +#define M_MAP2I_0(func, data, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, ...) \ M_APPLY_FUNC2(func, data, a) M_APPLY_FUNC2(func, data, b) M_APPLY_FUNC2(func, data, c) \ M_APPLY_FUNC2(func, data, d) M_APPLY_FUNC2(func, data, e) M_APPLY_FUNC2(func, data, f) \ M_APPLY_FUNC2(func, data, g) M_APPLY_FUNC2(func, data, h) M_APPLY_FUNC2(func, data, i) \ @@ -269,7 +273,7 @@ M_APPLY_FUNC2(func, data, s) M_APPLY_FUNC2(func, data, t) M_APPLY_FUNC2(func, data, u) \ M_APPLY_FUNC2(func, data, v) M_APPLY_FUNC2(func, data, w) M_APPLY_FUNC2(func, data, x) \ M_APPLY_FUNC2(func, data, y) M_APPLY_FUNC2(func, data, z) -#define M_MAP2(f, ...) M_MAP2_0(f, __VA_ARGS__, , , , , , , , , , , , , , , , , , , , , , , , , , , ) +#define M_MAP2(f, ...) M_MAP2I_0(f, __VA_ARGS__, , , , , , , , , , , , , , , , , , , , , , , , , , , ) /* Duplicate of macros for GET_METHOD as it may be called in context where a M_MAP2 is in progress */ #define M_APPLY_FUNC2B(func, arg1, arg2) \