Add macro M_CHAIN_INIT for support of partially constructed object.

This commit is contained in:
Patrick Pelissier committed 2023-09-09 18:40:32 +02:00
1 parent 5dba526e29
commit bf68340106
2 files changed
+124

No files matched your search

+27
View File
@@ -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.
+97
View File
@@ -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
@@ -472,6 +568,7 @@ int main(void)
test1();
test2();
test3();
test4();
testobj_final_check();
test_final();
exit(1); // Shall not be reached.