Refactoring & implement support for RAII with M_DEFER
This commit is contained in:
1 parent
e8322215fd
commit
0798480ad5
1 file changed
+81
-50
@@ -382,13 +382,13 @@ m_try_clear(m_try_t state)
|
||||
}
|
||||
}
|
||||
|
||||
// Implement the M_LET injection macros, so that the CLEAR operator is called on exception
|
||||
//
|
||||
#if M_USE_TRY_MECHANISM == 1
|
||||
# error M*LIB: Internal error. C++ back-end requested within C implementation.
|
||||
|
||||
#elif M_USE_TRY_MECHANISM == 2 || M_USE_TRY_MECHANISM == 3
|
||||
// Use of CLANG blocks or GCC nested functions.
|
||||
// Implement the M_LET injection macros, so that the CLEAR operator is called on exception
|
||||
// Helper functions
|
||||
// Each mechanisme provide 3 helper functions:
|
||||
// * pre: which is called before the constructor
|
||||
// * post: which is called after the constructor
|
||||
// * final: which is called before the destructor.
|
||||
|
||||
// We register a call to the CLEAR callback.
|
||||
// We don't modify m_global_error_list until we have successfully called the INIT operator
|
||||
@@ -397,7 +397,7 @@ m_try_clear(m_try_t state)
|
||||
// of the object (if the INIT operator of the object calls other INIT operators of composed fields),
|
||||
// since partial initialization will be unstacked naturally by the composing object.
|
||||
static inline bool
|
||||
m_try_init_pre(m_try_t state)
|
||||
m_try_cb_pre(m_try_t state)
|
||||
{
|
||||
state->kind = M_STATE_CLEAR_CB;
|
||||
state->next = m_global_error_list;
|
||||
@@ -406,7 +406,7 @@ m_try_init_pre(m_try_t state)
|
||||
|
||||
// We register the function to call of the initialized object.
|
||||
static inline bool
|
||||
m_try_init_post(m_try_t state, void (M_TRY_FUNC_OPERATOR func)(void*), void *data)
|
||||
m_try_cb_post(m_try_t state, void (M_TRY_FUNC_OPERATOR func)(void*), void *data)
|
||||
{
|
||||
state->data.clear.func = func;
|
||||
state->data.clear.data = data;
|
||||
@@ -414,45 +414,17 @@ m_try_init_post(m_try_t state, void (M_TRY_FUNC_OPERATOR func)(void*), void *dat
|
||||
return true;
|
||||
}
|
||||
|
||||
// The object has been cleared.
|
||||
// The object will be cleared.
|
||||
// We can pop the stack frame of the errors.
|
||||
static inline void
|
||||
m_try_clear_pre(m_try_t state)
|
||||
m_try_cb_final(m_try_t state)
|
||||
{
|
||||
m_global_error_list = state->next;
|
||||
}
|
||||
|
||||
// M_LET Injection / pre initialization
|
||||
// Initialize the stack frame.
|
||||
#define M_LET_TRY_INJECT_PRE_B(cont, oplist, name) \
|
||||
for(m_try_t M_C(m_try_state_, name); cont && \
|
||||
m_try_init_pre(M_C(m_try_state_, name) ); )
|
||||
|
||||
// M_LET Injection / post initialization
|
||||
// Save the function to call using
|
||||
#if M_USE_TRY_MECHANISM == 2
|
||||
// blocks (CLANG)
|
||||
#define M_LET_TRY_INJECT_POST_B(cont, oplist, name) \
|
||||
for(m_try_init_post(M_C(m_try_state_, name), \
|
||||
^ void (void *_data) { M_GET_TYPE oplist *_t = _data; M_CALL_CLEAR(oplist, *_t); }, \
|
||||
(void*) &name); cont; m_try_clear_pre(M_C(m_try_state_, name)) )
|
||||
#else
|
||||
// nested functions (GCC)
|
||||
#define M_LET_TRY_INJECT_POST_B(cont, oplist, name) \
|
||||
for(m_try_init_post(M_C(m_try_state_, name), \
|
||||
__extension__ ({ __extension__ void _callback (void *_data) { M_GET_TYPE oplist *_t = _data; M_CALL_CLEAR(oplist, *_t); } _callback; }), \
|
||||
(void*) &name); cont; m_try_clear_pre(M_C(m_try_state_, name)) )
|
||||
#endif
|
||||
|
||||
#elif M_USE_TRY_MECHANISM == 4
|
||||
// STD C compliant (without compiler extension): use of setjmp
|
||||
// This is the basic implementation in case of compiler unknown.
|
||||
// It uses setjmp/longjmp, and as such, is much slower than
|
||||
// other implementations.
|
||||
|
||||
// Pre initialization function. Save the stack frame for a longjmp
|
||||
static inline bool
|
||||
m_try_init_pre(m_try_t state)
|
||||
m_try_jump_pre(m_try_t state)
|
||||
{
|
||||
state->kind = M_STATE_CLEAR_JMPBUF;
|
||||
state->next = m_global_error_list;
|
||||
@@ -461,41 +433,92 @@ m_try_init_pre(m_try_t state)
|
||||
|
||||
// Post initialization function. Register the stack frame for a longjmp
|
||||
static inline void
|
||||
m_try_init_post(m_try_t state)
|
||||
m_try_jump_post(m_try_t state)
|
||||
{
|
||||
m_global_error_list = state;
|
||||
}
|
||||
// And call setjmp to register the position in the code.
|
||||
#define m_try_init_post(s) \
|
||||
M_LIKELY ((m_try_init_post(s), setjmp(((s)->data.buf)) != 1))
|
||||
#define m_try_jump_post(s) \
|
||||
M_LIKELY ((m_try_jump_post(s), setjmp(((s)->data.buf)) != 1))
|
||||
|
||||
// The object has been cleared.
|
||||
// The object will be cleared.
|
||||
// We can pop the stack frame of the errors.
|
||||
static inline void
|
||||
m_try_clear_pre(m_try_t state)
|
||||
m_try_jump_final(m_try_t state)
|
||||
{
|
||||
m_global_error_list = state->next;
|
||||
}
|
||||
|
||||
|
||||
// Implement the M_LET injection macros, so that the CLEAR operator is called on exception
|
||||
//
|
||||
#if M_USE_TRY_MECHANISM == 1
|
||||
# error M*LIB: Internal error. C++ back-end requested within C implementation.
|
||||
|
||||
#elif M_USE_TRY_MECHANISM == 2
|
||||
// Use of CLANG blocks
|
||||
|
||||
#define M_LET_TRY_INJECT_PRE_B(cont, oplist, name) \
|
||||
for(m_try_t M_C(m_try_state_, name); cont && \
|
||||
m_try_cb_pre(M_C(m_try_state_, name) ); )
|
||||
|
||||
#define M_LET_TRY_INJECT_POST_B(cont, oplist, name) \
|
||||
for(m_try_cb_post(M_C(m_try_state_, name), \
|
||||
^ void (void *_data) { M_GET_TYPE oplist *_t = _data; M_CALL_CLEAR(oplist, *_t); }, \
|
||||
(void*) &name); cont; m_try_cb_final(M_C(m_try_state_, name)) )
|
||||
|
||||
#elif M_USE_TRY_MECHANISM == 3
|
||||
// Use of GCC nested functions.
|
||||
|
||||
#define M_LET_TRY_INJECT_PRE_B(cont, oplist, name) \
|
||||
for(m_try_t M_C(m_try_state_, name); cont && \
|
||||
m_try_cb_pre(M_C(m_try_state_, name) ); )
|
||||
|
||||
#define M_LET_TRY_INJECT_POST_B(cont, oplist, name) \
|
||||
for(m_try_cb_post(M_C(m_try_state_, name), \
|
||||
__extension__ ({ __extension__ void _callback (void *_data) { M_GET_TYPE oplist *_t = _data; M_CALL_CLEAR(oplist, *_t); } _callback; }), \
|
||||
(void*) &name); cont; m_try_cb_final(M_C(m_try_state_, name)) )
|
||||
|
||||
#elif M_USE_TRY_MECHANISM == 4
|
||||
// STD C compliant (without compiler extension): use of setjmp
|
||||
// This is the basic implementation in case of compiler unknown.
|
||||
// It uses setjmp/longjmp, and as such, is much slower than
|
||||
// other implementations.
|
||||
|
||||
// M_LET Injection / pre initialization
|
||||
// Initialize the stack frame.
|
||||
#define M_LET_TRY_INJECT_PRE_B(cont, oplist, name) \
|
||||
for(m_try_t M_C(m_try_state_, name); cont && \
|
||||
m_try_init_pre(M_C(m_try_state_, name)); )
|
||||
m_try_jump_pre(M_C(m_try_state_, name)); )
|
||||
|
||||
// M_LET Injection / post initialization
|
||||
// Register the stack frame and tests for the longjmp.
|
||||
// In which case call the CLEAR operator, unstack the error list and rethrow the error.
|
||||
#define M_LET_TRY_INJECT_POST_B(cont, oplist, name) \
|
||||
for( ; cont ; m_try_clear_pre(M_C(m_try_state_, name))) \
|
||||
if (m_try_init_post(M_C(m_try_state_, name)) \
|
||||
|| (M_CALL_CLEAR(oplist, name), m_try_clear_pre(M_C(m_try_state_, name)), m_rethrow(), false))
|
||||
for( ; cont ; m_try_jump_final(M_C(m_try_state_, name))) \
|
||||
if (m_try_jump_post(M_C(m_try_state_, name)) \
|
||||
|| (M_CALL_CLEAR(oplist, name), m_try_jump_final(M_C(m_try_state_, name)), m_rethrow(), false))
|
||||
|
||||
#else
|
||||
#error M*LIB: Invalid value for M_USE_TRY_MECHANISM [1..4]
|
||||
# error M*LIB: Invalid value for M_USE_TRY_MECHANISM [1..4]
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// M_DEFER Injection / pre initialization
|
||||
// Initialize the stack frame.
|
||||
#define M_DEFER_TRY_INJECT_PRE_B(cont, clear) \
|
||||
for(m_try_t M_C(m_try_state_, cont); cont && \
|
||||
m_try_jump_pre(M_C(m_try_state_, cont)); )
|
||||
|
||||
// M_DEFER Injection / post initialization
|
||||
// Register the stack frame and tests for the longjmp.
|
||||
// In which case call the CLEAR operator, unstack the error list and rethrow the error.
|
||||
#define M_DEFER_TRY_INJECT_POST_B(cont, clear) \
|
||||
for( ; cont ; m_try_jump_final(M_C(m_try_state_, cont))) \
|
||||
if (m_try_jump_post(M_C(m_try_state_, cont)) \
|
||||
|| (clear , m_try_jump_final(M_C(m_try_state_, cont)), m_rethrow(), false))
|
||||
|
||||
#endif /* cplusplus */
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
@@ -511,6 +534,14 @@ m_try_clear_pre(m_try_t state)
|
||||
M_IF(M_GET_PROPERTY(oplist, NOCLEAR))(M_EAT, M_LET_TRY_INJECT_POST_B) \
|
||||
(cont, oplist, name)
|
||||
|
||||
|
||||
// Macro injection for M_DEFER.
|
||||
#undef M_DEFER_TRY_INJECT_PRE
|
||||
#define M_DEFER_TRY_INJECT_PRE(cont, clear) M_DEFER_TRY_INJECT_PRE_B(cont, clear)
|
||||
#undef M_DEFER_TRY_INJECT_POST
|
||||
#define M_DEFER_TRY_INJECT_POST(cont, clear) M_DEFER_TRY_INJECT_POST_B(cont, clear)
|
||||
|
||||
|
||||
// In case of MEMORY FULL errors, throw an error instead of aborting.
|
||||
#undef M_MEMORY_FULL
|
||||
#define M_MEMORY_FULL(size) M_THROW(M_ERROR_MEMORY, (intptr_t)(size))
|
||||
|
||||
Reference in new issue
Block a user