Add macros M_LET_IF and M_DEFER to help handling with runtime errors

This commit is contained in:
Patrick Pelissier committed 2020-12-14 21:26:07 +01:00
1 parent a4ff7c71f2
commit e893560a48
3 files changed
+120 -9

No files matched your search

+53 -2
View File
@@ -5913,14 +5913,65 @@ then the variable 'v1' will be initialized with the
contains of 'va_list...' using the specialized initializer operator INIT\_WITH
and not the empty initializer INIT operator.
There shall be at most one M\_LET macro per line of source code.
Example:
M_LET(a, STRING_OPLIST) { do something with a } or
M_LET(a, b, c, STRING_OPLIST) { do something with a, b & c }
NOTE: The user code can not perform a return or a goto outside the {}
NOTE: The user code shall not perform a return or a goto (or longjmp) outside the {} or a call to an exit function
otherwise the clear code of the object won't be called .
However, you can use the break instruction to quit the block.
However, you can use the break instruction to quit the block (the clear function will be exexcuted),
and you can chain the M\_LET macro to create several different variables.
##### M\_LET\_IF(init_code, test_code, clear_code)
This macro defines the variable(s) in 'init_code',
executes the next block of instructions where the variable(s) is(are) used if the initialization succeeds by testing 'test_code',
then it executes the 'clear_code'.
'test_code' returns a boolean indicating if the initialization succeeds (true) or not.
If the initialization fails, it won't call the 'clear_code'.
The syntax is the same as a for loop.
There shall be at most one M\_LET\_IF macro per line of source code.
Example:
M_LET_IF(int *p = malloc(100), p!=0, free(p)) {
M_LET_IF( /* nothing */ , mtx_lock(&mut)!=thrd_error, mtx_unlock(&mut)) {
printf ("OK! Let's use p in a mutex section\n");
}
}
NOTE:
The user code shall not perform a return or a goto (or longjmp) outside the {} or a call to an exit function
otherwise the clear_code won't be called .
However, you can use the break instruction to quit the block (the clear_code will be exexcuted),
and you can chain the M\_LET macro to create several different variables.
##### M\_DEFER(clear_code)
This macro registers the execution of 'clear_code' when reaching the further closing brace
of the next block of instruction.
There shall be at most one M\_DEFER macro per line of source code.
Example:
m_mutex_lock(mut);
M_DEFER(m_mutex_unlock(mut)) {
// Use of the critical section.
}
NOTE:
The user code shall not perform a return or a goto (or longjmp) outside the {} or a call to an exit function
otherwise the clear_code won't be called .
You can use the break instruction to quit the block (the clear_code will be exexcuted),
and you can chain the M\_LET macro to create several different variables.
#### Memory / Error macros
+46 -7
View File
@@ -3117,14 +3117,50 @@ m_core_parse2_enum (const char str[], const char **endptr)
/* Transform the va list by adding their number as the first argument of
the list.
Example: M_VA(a,b,c,d,e) ==> 5,a,b,c,d,e */
Example: M_VA(a,b,c,d,e) ==> 5,a,b,c,d,e
TODO: Use of a structure instead of an integer to ensure limited syntax control.
*/
#define M_VA(...) M_NARGS(__VA_ARGS__), __VA_ARGS__
/* Initialize the container 'dest' as per 'oplist'
and fill it with the given VA arguments.
NOTE: If the REVERSE operator exists, it is a list,
so reverse the final order.
/* Defer the evaluation of the given expression until the closing brace.
M_DEFER(code) { }
Example:
M_DEFER(free(p)) {
// code using p
} // Here p is free
*/
#define M_DEFER(clear) \
M_DEFER_INTERNAL(clear, m_var_ ## __LINE__)
#define M_DEFER_INTERNAL(clear, cont) \
for(bool cont = true; cont; cont = false) \
for( (void) 0; 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.
M_LET_IF(init code, test code, clear code) { code using the variable }
Example:
M_LET_IF(void * p = malloc(100), p!=0, free(p)) {
// code using p
} // Here p is free
*/
#define M_LET_IF(init, test, clear) \
M_LET_IF_INTERNAL(init, test, clear, m_var_ ## __LINE__)
#define M_LET_IF_INTERNAL(init, test, clear, cont) \
for(bool cont = true; cont; cont = false) \
for( init ; cont && (test) ; (clear)) \
for( (void) 0; cont; cont = false) \
/* Initialize the container 'dest' as per 'oplist' INIT operator
and fill it with the given VA arguments with the PUSH operator.
NOTE: If the REVERSE operator exists, it assumes a list,
so it reverses the final order.
*/
#define M_INIT_VAI(oplist, dest, ...) \
(void)(M_GET_INIT oplist (dest) , \
@@ -3134,8 +3170,11 @@ m_core_parse2_enum (const char str[], const char **endptr)
#define M_INIT_VAI_FUNC(d, a) \
M_PAIR_2 d (M_PAIR_1 d, a)
/* Initialize the container 'dest' as per 'oplist'
and fill it with the given VA arguments which are pair of (key,value) */
/* Initialize the container 'dest' as per 'oplist' INIT operator
and fill it with the given VA argument
assumed to be pair (key,value) with the SET_KEY operator.
*/
#define M_INIT_KEY_VAI(oplist, dest, ...) \
(void)(M_GET_INIT oplist (dest) , \
M_MAP2_C(M_INIT_KEY_VAI_FUNC, (dest, M_GET_SET_KEY oplist) , __VA_ARGS__))
+21
View File
@@ -192,6 +192,27 @@ static void test_let(void)
assert(0);
}
assert(b);
int c = 0;
M_LET_IF( assert(c++ == 0), (assert(c++ == 1), true), assert(c++ == 3)) {
assert(c++ == 2);
}
M_LET_IF( assert(c++ == 4), (assert(c++ == 5), false), assert(0)) {
assert(0);
}
assert(c++ == 6);
M_LET_IF(int *p = (int*) malloc(sizeof(int)), p!=0, free(p)) {
assert(p != NULL);
*p = 4;
}
int *p = (int*)malloc(sizeof(int));
assert(p != NULL);
M_DEFER ( (assert(c++ == 8), free(p)) ) {
*p = 4;
assert(c++ == 7);
}
assert(c++ == 9);
}
static void test_va(void)