Skip to content

Commit

Permalink
Adding macro M_CHAIN_OBJ
Browse files Browse the repository at this point in the history
  • Loading branch information
P-p-H-d committed Oct 14, 2023
1 parent 19e6146 commit 5f43036
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 6 deletions.
40 changes: 39 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand Down
61 changes: 58 additions & 3 deletions m-core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand All @@ -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.
Expand Down
85 changes: 83 additions & 2 deletions tests/test-mtry.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -568,7 +648,8 @@ int main(void)
test1();
test2();
test3();
test4();
test4a();
test4b();
testobj_final_check();
test_final();
exit(1); // Shall not be reached.
Expand Down

0 comments on commit 5f43036

Please sign in to comment.