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 */
|
#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,
|
/* Declare a variable, initialize it, continue if the initialization succeeds,
|
||||||
and clears the variable afterwards.
|
and clears the variable afterwards.
|
||||||
Otherwise, stop the execution and execute else_code if defined.
|
Otherwise, stop the execution and execute else_code if defined.
|
||||||
|
|||||||
@@ -456,6 +456,102 @@ static void test3(void)
|
|||||||
assert(init == false);
|
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)
|
static void test_final(void)
|
||||||
{
|
{
|
||||||
// Throw without a try block shall raise the M_RAISE_FATAL macro
|
// Throw without a try block shall raise the M_RAISE_FATAL macro
|
||||||
@@ -472,6 +568,7 @@ int main(void)
|
|||||||
test1();
|
test1();
|
||||||
test2();
|
test2();
|
||||||
test3();
|
test3();
|
||||||
|
test4();
|
||||||
testobj_final_check();
|
testobj_final_check();
|
||||||
test_final();
|
test_final();
|
||||||
exit(1); // Shall not be reached.
|
exit(1); // Shall not be reached.
|
||||||
|
|||||||
Reference in new issue
Block a user