Adding macro M_CHAIN_OBJ

This commit is contained in:
Patrick Pelissier committed 2023-10-14 15:55:59 +02:00
1 parent 19e61467d5
commit 5f4303624f
3 files changed
+180 -6

No files matched your search

+39 -1
View File
@@ -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
+58 -3
View File
@@ -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
View File
@@ -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.