Add macro M_CHAIN_INIT for support of partially constructed object.
This commit is contained in:
1 parent
5dba526e29
commit
bf68340106
2 files changed
+124
No files matched your search
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in new issue
Block a user