Skip to content

Commit

Permalink
Add macro M_CHAIN_INIT for support of partially constructed object.
Browse files Browse the repository at this point in the history
  • Loading branch information
P-p-H-d committed Sep 9, 2023
1 parent 5dba526 commit bf68340
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 0 deletions.
27 changes: 27 additions & 0 deletions m-core.h
Original file line number Diff line number Diff line change
Expand Up @@ -4381,6 +4381,33 @@ m_core_parse2_enum (const char str[], const char **endptr)
#define M_DEFER_TRY_INJECT_POST(cont, clear) /* Nothing */


/* If exceptions are activated, M_CHAIN_INIT 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 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.
USAGE:
void init_function(struct_t s) {
M_CHAIN_INIT(string_init(s->str), string_clear(s->str)) {
// Rest of initialization code
}
}
*/
#define M_CHAIN_INIT(init, clear) \
M_CHAIN_INIT_INTERNAL(M_C(m_var_, __LINE__), init, clear)

#define M_CHAIN_INIT_INTERNAL(cont, init, clear) \
for(bool cont = true; cont; cont = false) \
M_DEFER_TRY_INJECT_PRE(cont, clear) \
for( init; cont ; cont = false) \
M_DEFER_TRY_INJECT_POST(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 and execute else_code if defined.
Expand Down
97 changes: 97 additions & 0 deletions tests/test-mtry.c
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,102 @@ static void test3(void)
assert(init == false);
}

typedef struct {
testobj_t o1;
testobj_t o2;
int num;
} struct_t[1];

// Pseudo execution counter
volatile int g_flow = 0;
// Throw execution when g_flow == g_throw
volatile int g_throw = 0;

static void struct_init(struct_t x)
{
M_CHAIN_INIT(testobj_init(x->o1), testobj_clear(x->o1) )
M_CHAIN_INIT(testobj_init(x->o2), testobj_clear(x->o2) ) {
x->num = 0;
if (++g_flow == g_throw) M_THROW(1);
}
}

static void struct_init_set(struct_t x, const struct_t y)
{
M_CHAIN_INIT(testobj_init_set(x->o1, y->o1), testobj_clear(x->o1) ) {
// Throw within the constructor (object partially constructed)
if (++g_flow == g_throw) M_THROW(1);
M_CHAIN_INIT(testobj_init_set(x->o2, y->o2), testobj_clear(x->o2) ) {
x->num = y->num;
if (++g_flow == g_throw) M_THROW(1);
}
}
}

static void struct_clear(struct_t x)
{
testobj_clear(x->o1);
testobj_clear(x->o2);
}

#define M_OPL_struct_t() M_CLASSIC_OPLIST(struct)

static void test4(void)
{
g_flow = 0;
g_throw = 4;
M_TRY(main) {
assert(++g_flow == 1);
M_LET(a, struct_t) {
assert(++g_flow == 3);
M_LET( (b, a), struct_t) {
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, struct_t) {
assert(++g_flow == 3);
M_LET( (b, a), struct_t) {
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, struct_t) {
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, struct_t) {
assert(++g_flow == 3);
M_LET( (b, a), struct_t) {
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 @@ -472,6 +568,7 @@ int main(void)
test1();
test2();
test3();
test4();
testobj_final_check();
test_final();
exit(1); // Shall not be reached.
Expand Down

0 comments on commit bf68340

Please sign in to comment.