diff --git a/README.md b/README.md index 8b02a59e..4dbae8ea 100644 --- a/README.md +++ b/README.md @@ -6843,7 +6843,7 @@ This is equivalent to C++ construct: void type() : field1(), field2() { rest of constructor } -M_CHAIN_INIT shall be the first instructions of the constructor function. +M_CHAIN_INIT / M_CHAIN_OBJ shall be the first instructions of the constructor function. Example: @@ -6856,6 +6856,44 @@ Example: } +##### M\_CHAIN\_OBJ(name, oplist, var [, value]) + +This macro executes the initialization method if 'var' to initialize it then +registers the execution of its CLEAR method if an exception is triggered +(and if the property NOCLEAR of the oplist is not defined) +until the further closing brace of the next block of instruction. +If exception are not enabled, it simply executes the initialization method. + +The initialization method being used is : + +* INIT method if there is no value +* INIT\_SET method if there is a value which is not between parenthesis +* INIT\_WITH method if there is a value which is between parenthesis. + +name shall a unique identifier in the current block. +It can be chained multiple times to register multiple registrations. + +Therefore it enables support for chaining +initialization at the begining of a constructor for the fields of the constructed +object so that even if the constructor failed and throw an exception, +the fields of the constructed object are properly cleared. + +This is equivalent to C++ construct: + + void type() : field1(), field2() { rest of constructor } + +M_CHAIN_INIT / M_CHAIN_OBJ shall be the first instructions of the constructor function. + +Example: + + void struct_init_set(struct_t d, struct_t s) + { + M_CHAIN_OBJ(s1, STRING_OPLIST, d->s1, s->s1) + M_CHAIN_OBJ(s2, STRING_OPLIST, d->s2, s->s2) + M_CHAIN_OBJ(nu, M_BASIC_OPLIST, d->num, s->num) { } + } + + #### Memory / Error macros All these macro can be overridden before including the header m-core.h diff --git a/m-core.h b/m-core.h index 03c9df51..09a0c95d 100644 --- a/m-core.h +++ b/m-core.h @@ -4387,9 +4387,9 @@ m_core_parse2_enum (const char str[], const char **endptr) the fields of the constructed object are properly cleared. This is equivalent to C++ construct: void type() : field1(), field2() { rest of constructor } M_CHAIN_INIT shall be the first instruction of the init function. - First argument is the initialization code. - Second argument is the clear code. - The clear code is only executed on exception. + Second argument is the initialization code. + Third argument is the clear code. + The clear code is *only* executed on exception. USAGE: void init_function(struct_t s) { M_CHAIN_INIT(name, string_init(s->str), string_clear(s->str)) { @@ -4408,6 +4408,61 @@ m_core_parse2_enum (const char str[], const char **endptr) for( (void) 0; cont; cont = false) +/* If exceptions are activated, M_CHAIN_OBJ enables support for chaining + initialization at the begining of a constructor for the fields of the constructed + object so that even if the constructor failed and throw an exception, + the fields of the constructed object are properly cleared. + This is equivalent to M_CHAIN_INIT except it takes an oplist. + This is equivalent to C++ construct: void type() : field1(), field2() { rest of constructor } + M_CHAIN_OBJ shall be the first instruction of the init function except + it can be freely mixed with M_CHAIN_INIT + First argument is a name identifier. + Second argument is the oplist of the variable to initialize. + Third argument is the variable to initialize. + Optional fourth argument is the value to use for initialization. + If there is no value, it will initialize using the INIT method. + Otherwise, if there is no parenthesis around the value, it will use the INIT_SET method. + Otherwise the INIT_WITH method. + The clear code is *only* executed on exception. + If the property NOCLEAR is defined for the object, + it won't generate an exception handler at all even if exceptions are activated, + and will only initialize the object. + USAGE: + void init_function(struct_t s) { + M_CHAIN_OBJ(name, STRING_OPLIST, s->str ) { + // Rest of initialization code + } + } + */ +#define M_CHAIN_OBJ(name, oplist, ...) \ + M_BY_NARGS( M_IF(M_GET_PROPERTY(oplist, NOCLEAR))(M_CHAIN_OBJ_A, M_CHAIN_OBJ_B), __VA_ARGS__) \ + (name, oplist, __VA_ARGS__) +// Need to define an exception handler. Use of M_CHAIN_INIT +#define M_CHAIN_OBJ_B__NARGS_1(name, oplist, var) \ + M_CHAIN_INIT( name, M_CALL_INIT(oplist, var), M_CALL_CLEAR(oplist, var) ) +#define M_CHAIN_OBJ_B__NARGS_2(name, oplist, var, value) \ + M_IF(M_PARENTHESIS_P(value))(M_CHAIN_OBJ_B__NARGS_2_WITH, M_CHAIN_OBJ_B__NARGS_2_SET) \ + (name, oplist, var, value) +#define M_CHAIN_OBJ_B__NARGS_2_WITH(name, oplist, var, value) \ + M_CHAIN_INIT( name, M_CALL_INIT_WITH(oplist, var, value), M_CALL_CLEAR(oplist, var) ) +#define M_CHAIN_OBJ_B__NARGS_2_SET(name, oplist, var, value) \ + M_CHAIN_INIT( name, M_CALL_INIT_SET(oplist, var, value), M_CALL_CLEAR(oplist, var) ) +// No need to define an exception handler. Just call the INIT function. +#define M_CHAIN_OBJ_A__NARGS_1(name, oplist, var) \ + M_CHAIN_FOR(name, M_CALL_INIT(oplist, var) ) +#define M_CHAIN_OBJ_A__NARGS_2(name, oplist, var, value) \ + M_CHAIN_FOR(name, \ + M_IF(M_PARENTHESIS_P(value))(M_CALL_INIT_WITH, M_CALL_INIT_SET) \ + (oplist, var, value) ) +// Execute the 'init' function in a for loop construct +#define M_CHAIN_FOR(name, init ) \ + M_CHAIN_FOR_B(M_C(m_var_, name), init) +#define M_CHAIN_FOR_B(cont, init) \ + for(bool cont = true; cont; cont = false) \ + for( init; cont ; cont = false) \ + 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 and execute else_code if defined. diff --git a/tests/test-mtry.c b/tests/test-mtry.c index 881d2301..7a72b09a 100644 --- a/tests/test-mtry.c +++ b/tests/test-mtry.c @@ -488,6 +488,27 @@ static void struct_init_set(struct_t x, const struct_t y) } } +static void struct_initb(struct_t x) +{ + M_CHAIN_OBJ(o1, TESTOBJ_OPLIST, x->o1) + M_CHAIN_OBJ(o2, TESTOBJ_OPLIST, x->o2) + M_CHAIN_OBJ(o3, M_BASIC_OPLIST, x->num) { + if (++g_flow == g_throw) M_THROW(1); + } +} + +static void struct_init_setb(struct_t x, const struct_t y) +{ + M_CHAIN_OBJ(o1, TESTOBJ_OPLIST, x->o1, y->o1) { + // Throw within the constructor (object partially constructed) + if (++g_flow == g_throw) M_THROW(1); + M_CHAIN_OBJ(o2, TESTOBJ_OPLIST, x->o2, y->o2) + M_CHAIN_OBJ(o3, M_BASIC_OPLIST, x->num, y->num) { + if (++g_flow == g_throw) M_THROW(1); + } + } +} + static void struct_clear(struct_t x) { testobj_clear(x->o1); @@ -496,7 +517,7 @@ static void struct_clear(struct_t x) #define M_OPL_struct_t() M_CLASSIC_OPLIST(struct) -static void test4(void) +static void test4a(void) { g_flow = 0; g_throw = 4; @@ -552,6 +573,65 @@ static void test4(void) } } +#define STRUCTB_OPLIST \ + (INIT(struct_initb), INIT_SET(struct_init_setb), CLEAR(struct_clear), TYPE(struct_t)) + +static void test4b(void) +{ + g_flow = 0; + g_throw = 4; + M_TRY(main) { + assert(++g_flow == 1); + M_LET(a, STRUCTB_OPLIST) { + assert(++g_flow == 3); + M_LET( (b, a), STRUCTB_OPLIST) { + assert(false); + } + } + } M_CATCH(main, 0) { + assert(++g_flow == 5); + } + + g_flow = 0; + g_throw = 5; + M_TRY(main) { + assert(++g_flow == 1); + M_LET(a, STRUCTB_OPLIST) { + assert(++g_flow == 3); + M_LET( (b, a), STRUCTB_OPLIST) { + assert(false); + } + } + } M_CATCH(main, 0) { + assert(++g_flow == 6); + } + + g_flow = 0; + g_throw = 2; + M_TRY(main) { + assert(++g_flow == 1); + M_LET(a, STRUCTB_OPLIST) { + assert(false); + } + } M_CATCH(main, 0) { + assert(++g_flow == 3); + } + + g_flow = 0; + g_throw = 0; + M_TRY(main) { + assert(++g_flow == 1); + M_LET(a, STRUCTB_OPLIST) { + assert(++g_flow == 3); + M_LET( (b, a), STRUCTB_OPLIST) { + assert(++g_flow == 6); + } + } + } M_CATCH(main, 0) { + assert(false); + } +} + static void test_final(void) { // Throw without a try block shall raise the M_RAISE_FATAL macro @@ -568,7 +648,8 @@ int main(void) test1(); test2(); test3(); - test4(); + test4a(); + test4b(); testobj_final_check(); test_final(); exit(1); // Shall not be reached.