Adding macro M_CHAIN_OBJ
This commit is contained in:
1 parent
19e61467d5
commit
5f4303624f
3 files changed
+180
-6
No files matched your search
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)) {
|
||||
@@ -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.
|
||||
|
||||
+83
-2
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -568,7 +648,8 @@ int main(void)
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
test4();
|
||||
test4a();
|
||||
test4b();
|
||||
testobj_final_check();
|
||||
test_final();
|
||||
exit(1); // Shall not be reached.
|
||||
|
||||
Reference in new issue
Block a user