diff --git a/README.md b/README.md index 4bfe985d..37b1a615 100644 --- a/README.md +++ b/README.md @@ -5913,14 +5913,65 @@ then the variable 'v1' will be initialized with the contains of 'va_list...' using the specialized initializer operator INIT\_WITH and not the empty initializer INIT operator. +There shall be at most one M\_LET macro per line of source code. + Example: M_LET(a, STRING_OPLIST) { do something with a } or M_LET(a, b, c, STRING_OPLIST) { do something with a, b & c } -NOTE: The user code can not perform a return or a goto outside the {} +NOTE: The user code shall not perform a return or a goto (or longjmp) outside the {} or a call to an exit function otherwise the clear code of the object won't be called . -However, you can use the break instruction to quit the block. +However, you can use the break instruction to quit the block (the clear function will be exexcuted), +and you can chain the M\_LET macro to create several different variables. + + +##### M\_LET\_IF(init_code, test_code, clear_code) + +This macro defines the variable(s) in 'init_code', +executes the next block of instructions where the variable(s) is(are) used if the initialization succeeds by testing 'test_code', +then it executes the 'clear_code'. + +'test_code' returns a boolean indicating if the initialization succeeds (true) or not. +If the initialization fails, it won't call the 'clear_code'. + +The syntax is the same as a for loop. + +There shall be at most one M\_LET\_IF macro per line of source code. + +Example: + + M_LET_IF(int *p = malloc(100), p!=0, free(p)) { + M_LET_IF( /* nothing */ , mtx_lock(&mut)!=thrd_error, mtx_unlock(&mut)) { + printf ("OK! Let's use p in a mutex section\n"); + } + } + +NOTE: +The user code shall not perform a return or a goto (or longjmp) outside the {} or a call to an exit function +otherwise the clear_code won't be called . +However, you can use the break instruction to quit the block (the clear_code will be exexcuted), +and you can chain the M\_LET macro to create several different variables. + +##### M\_DEFER(clear_code) + +This macro registers the execution of 'clear_code' when reaching the further closing brace +of the next block of instruction. + +There shall be at most one M\_DEFER macro per line of source code. + +Example: + + m_mutex_lock(mut); + M_DEFER(m_mutex_unlock(mut)) { + // Use of the critical section. + } + +NOTE: +The user code shall not perform a return or a goto (or longjmp) outside the {} or a call to an exit function +otherwise the clear_code won't be called . +You can use the break instruction to quit the block (the clear_code will be exexcuted), +and you can chain the M\_LET macro to create several different variables. #### Memory / Error macros diff --git a/m-core.h b/m-core.h index c6901b34..1f297b36 100644 --- a/m-core.h +++ b/m-core.h @@ -3117,14 +3117,50 @@ m_core_parse2_enum (const char str[], const char **endptr) /* Transform the va list by adding their number as the first argument of the list. - Example: M_VA(a,b,c,d,e) ==> 5,a,b,c,d,e */ + Example: M_VA(a,b,c,d,e) ==> 5,a,b,c,d,e + TODO: Use of a structure instead of an integer to ensure limited syntax control. +*/ #define M_VA(...) M_NARGS(__VA_ARGS__), __VA_ARGS__ -/* Initialize the container 'dest' as per 'oplist' - and fill it with the given VA arguments. - NOTE: If the REVERSE operator exists, it is a list, - so reverse the final order. +/* Defer the evaluation of the given expression until the closing brace. + M_DEFER(code) { } + Example: + M_DEFER(free(p)) { + // code using p + } // Here p is free +*/ +#define M_DEFER(clear) \ + M_DEFER_INTERNAL(clear, m_var_ ## __LINE__) + +#define M_DEFER_INTERNAL(clear, cont) \ + for(bool cont = true; cont; cont = false) \ + for( (void) 0; cont ; (clear)) \ + for( (void) 0; cont; cont = false) \ + + +/* Declare a variable, initialize it, continue if the initialization succeeds, + and clears the variable afterwards. + Otherwise, stop the execution. + M_LET_IF(init code, test code, clear code) { code using the variable } + Example: + M_LET_IF(void * p = malloc(100), p!=0, free(p)) { + // code using p + } // Here p is free +*/ +#define M_LET_IF(init, test, clear) \ + M_LET_IF_INTERNAL(init, test, clear, m_var_ ## __LINE__) + +#define M_LET_IF_INTERNAL(init, test, clear, cont) \ + for(bool cont = true; cont; cont = false) \ + for( init ; cont && (test) ; (clear)) \ + for( (void) 0; cont; cont = false) \ + + +/* Initialize the container 'dest' as per 'oplist' INIT operator + and fill it with the given VA arguments with the PUSH operator. + NOTE: If the REVERSE operator exists, it assumes a list, + so it reverses the final order. */ #define M_INIT_VAI(oplist, dest, ...) \ (void)(M_GET_INIT oplist (dest) , \ @@ -3134,8 +3170,11 @@ m_core_parse2_enum (const char str[], const char **endptr) #define M_INIT_VAI_FUNC(d, a) \ M_PAIR_2 d (M_PAIR_1 d, a) -/* Initialize the container 'dest' as per 'oplist' - and fill it with the given VA arguments which are pair of (key,value) */ + +/* Initialize the container 'dest' as per 'oplist' INIT operator + and fill it with the given VA argument + assumed to be pair (key,value) with the SET_KEY operator. +*/ #define M_INIT_KEY_VAI(oplist, dest, ...) \ (void)(M_GET_INIT oplist (dest) , \ M_MAP2_C(M_INIT_KEY_VAI_FUNC, (dest, M_GET_SET_KEY oplist) , __VA_ARGS__)) diff --git a/tests/test-mcore.c b/tests/test-mcore.c index 750152a1..593cdb0c 100644 --- a/tests/test-mcore.c +++ b/tests/test-mcore.c @@ -192,6 +192,27 @@ static void test_let(void) assert(0); } assert(b); + + int c = 0; + M_LET_IF( assert(c++ == 0), (assert(c++ == 1), true), assert(c++ == 3)) { + assert(c++ == 2); + } + M_LET_IF( assert(c++ == 4), (assert(c++ == 5), false), assert(0)) { + assert(0); + } + assert(c++ == 6); + M_LET_IF(int *p = (int*) malloc(sizeof(int)), p!=0, free(p)) { + assert(p != NULL); + *p = 4; + } + + int *p = (int*)malloc(sizeof(int)); + assert(p != NULL); + M_DEFER ( (assert(c++ == 8), free(p)) ) { + *p = 4; + assert(c++ == 7); + } + assert(c++ == 9); } static void test_va(void)