diff --git a/README.md b/README.md index ca9ce4c2..716a58d9 100644 --- a/README.md +++ b/README.md @@ -622,7 +622,7 @@ Other documented operators are: * KEY\_OPLIST() --> oplist: Return the oplist of the key type for associative containers. * VALUE\_OPLIST() --> oplist: Return the oplist of the value type for associative containers. * NEW (type) -> type pointer: allocate a new object (with suitable alignment and size) and return a pointer to it. The returned object is **not initialized** (a constructor operator shall be called afterward). The default method is M\_MEMORY\_ALLOC (that allocates from the heap). It returns NULL in case of failure. -* DEL (&obj): free the allocated uninitialized object 'obj'. The object is not cleared before being free (A destructor operator shall be called before). The object shall have been allocated by the associated NEW method. The default method is M\_MEMORY\_DEL (that frees to the heap). +* DEL (&obj): free the allocated uninitialized object 'obj' given its type pointer. The object is not cleared before being free (A destructor operator shall be called before). The object shall have been allocated by the associated NEW method. The default method is M\_MEMORY\_DEL (that frees to the heap). * REALLOC(type, type pointer, number) --> type pointer: realloc the given array referenced by type pointer (either a NULL pointer or a pointer returned by the associated REALLOC method itself) to an array of the number of objects of this type and return a pointer to this new array. Previously objects pointed by the pointer are kept up to the minimum of the new size and old one. New objects are not initialized (a constructor operator shall be called afterward). Freed objects are not cleared (A destructor operator shall be called before). The default is M\_MEMORY\_REALLOC (that allocates from the heap). It returns NULL in case of failure in which case the original array is not modified. * FREE (&obj) : free the allocated uninitialized array object 'obj'. The objects are not cleared before being free (CLEAR operator has to be called before). The object shall have been allocated by the associated REALLOC method. The default is M\_MEMORY\_FREE (that frees to the heap). * INC\_ALLOC(size\_t s) -> size\_t: Define the growing policy of an array (or equivalent structure). It returns a new allocation size based on the old allocation size ('s'). Default policy is to get the maximum between '2*s' and 16. NOTE: It doesn't check for overflow: if the returned value is lower than the old one, the user shall raise an overflow error. diff --git a/m-array.h b/m-array.h index 4b2499e6..3e03115f 100644 --- a/m-array.h +++ b/m-array.h @@ -81,7 +81,7 @@ /* FIXME: Do we want to export some methods as they are slow and are not fit to be used for building other methods (like _it_remove)? */ #define M_ARRA4_OPLIST_P3(name, oplist) \ - (INIT(M_F(name, _init)) \ + ( M_USER_INIT_OPERATOR(name, oplist), \ ,M_IF_METHOD2(INIT_SET,SET, oplist)(INIT_SET(M_F(name, _init_set)),) \ ,M_IF_METHOD(INIT_SET, oplist)(INIT_WITH(API_1(M_INIT_WITH_VAI)),) \ ,M_IF_METHOD2(INIT_SET,SET, oplist)(SET(M_F(name, _set)), ) \ @@ -173,7 +173,8 @@ M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ M_ARRA4_DEF_CORE(name, type, oplist, array_t, it_t) \ M_ARRA4_DEF_IO(name, type, oplist, array_t, it_t) \ - M_EMPLACE_QUEUE_DEF(name, array_t, M_F(name, _emplace_back), oplist, M_ARRA4_EMPLACE_DEF) + M_EMPLACE_QUEUE_DEF(name, array_t, M_F(name, _emplace_back), oplist, M_ARRA4_EMPLACE_DEF) \ + M_USER_DEF_GEN(oplist, name, array_t) /* Define the types */ #define M_ARRA4_DEF_TYPE(name, type, oplist, array_t, it_t) \ @@ -183,6 +184,7 @@ size_t size; /* Number of elements in the array */ \ size_t alloc; /* Allocated size for the array base */ \ type *ptr; /* Pointer to the array base */ \ + M_USER_FIELD(oplist) \ } array_t[1]; \ \ /* Define an iterator over an array */ \ @@ -202,10 +204,23 @@ #define M_ARRA4_DEF_CORE(name, type, oplist, array_t, it_t) \ \ M_INLINE void \ - M_F(name, _init)(array_t v) \ + M_C3(m_arra4_,name,_realloc)(array_t m_v, size_t alloc) \ + { \ + type *ptr = M_CALL_REALLOC(oplist, type, m_v->ptr, alloc); \ + if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \ + M_MEMORY_FULL(sizeof (type) * alloc); \ + return; \ + } \ + m_v->ptr = ptr; \ + m_v->alloc = alloc; \ + } \ + \ + M_INLINE void \ + M_F(name, _init)(array_t v M_USER_PARAM(oplist)) \ { \ M_ASSERT (v != NULL); \ /* Initially, the array is empty with nothing allocated */ \ + M_USER_INIT(oplist, v, user_data); \ v->size = 0; \ v->alloc = 0; \ v->ptr = NULL; \ @@ -223,14 +238,14 @@ } \ \ M_INLINE void \ - M_F(name, _clear)(array_t v) \ + M_F(name, _clear)(array_t m_v) \ { \ - M_ARRA4_CONTRACT(v); \ - M_F(name, _reset)(v); \ - M_CALL_FREE(oplist, v->ptr); \ + M_ARRA4_CONTRACT(m_v); \ + M_F(name, _reset)(m_v); \ + M_CALL_FREE(oplist, m_v->ptr); \ /* This is so reusing the object implies an assertion failure */ \ - v->alloc = 1; \ - v->ptr = NULL; \ + m_v->alloc = 1; \ + m_v->ptr = NULL; \ } \ \ M_IF_METHOD2(INIT_SET, SET, oplist)( \ @@ -242,13 +257,7 @@ if (M_UNLIKELY (d == s)) return; \ if (s->size > d->alloc) { \ const size_t alloc = s->size; \ - type *ptr = M_CALL_REALLOC(oplist, type, d->ptr, alloc); \ - if (M_UNLIKELY_NOMEM (ptr == NULL)) { \ - M_MEMORY_FULL(sizeof (type) * alloc); \ - return ; \ - } \ - d->ptr = ptr; \ - d->alloc = alloc; \ + M_C3(m_arra4_,name,_realloc)(d, alloc); \ } \ size_t i; \ size_t step1 = M_MIN(s->size, d->size); \ @@ -266,7 +275,7 @@ M_F(name, _init_set)(array_t d, const array_t s) \ { \ M_ASSERT (d != s); \ - M_F(name, _init)(d); \ + M_F(name, _init)(d M_USER_CALL(oplist, M_USER_DATA(s))); \ M_F(name, _set)(d, s); \ } \ , /* No SET & INIT_SET */) \ @@ -276,10 +285,11 @@ { \ M_ASSERT (d != s); \ M_ARRA4_CONTRACT(s); \ + M_USER_INIT(opl, d, M_USER_DATA(s)); \ d->size = s->size; \ d->alloc = s->alloc; \ d->ptr = s->ptr; \ - /* Robustness */ \ + /* Robustness: invalid representation of an array */ \ s->alloc = 1; \ s->ptr = NULL; \ M_ARRA4_CONTRACT(d); \ @@ -325,13 +335,7 @@ return NULL; \ } \ M_ASSERT (alloc > v->size); \ - type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \ - if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \ - M_MEMORY_FULL(sizeof (type) * alloc); \ - return NULL; \ - } \ - v->ptr = ptr; \ - v->alloc = alloc; \ + M_C3(m_arra4_,name,_realloc)(v, alloc); \ } \ M_ASSERT(v->ptr != NULL); \ type *ret = &v->ptr[v->size]; \ @@ -390,13 +394,7 @@ return ; \ } \ M_ASSERT (alloc > v->size); \ - type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \ - if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \ - M_MEMORY_FULL(sizeof (type) * alloc); \ - return; \ - } \ - v->ptr = ptr; \ - v->alloc = alloc; \ + M_C3(m_arra4_,name,_realloc)(v, alloc); \ } \ M_ASSERT(v->ptr != NULL); \ memmove(&v->ptr[key+1], &v->ptr[key], (v->size-key)*sizeof(type)); \ @@ -420,13 +418,7 @@ /* Increase size of array */ \ if (size > v->alloc) { \ size_t alloc = size ; \ - type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \ - if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \ - M_MEMORY_FULL(sizeof (type) * alloc); \ - return; \ - } \ - v->ptr = ptr; \ - v->alloc = alloc; \ + M_C3(m_arra4_,name,_realloc)(v, alloc); \ } \ for(size_t i = v->size ; i < size; i++) \ M_CALL_INIT(oplist, v->ptr[i]); \ @@ -437,27 +429,21 @@ , /* No INIT */ ) \ \ M_INLINE void \ - M_F(name, _reserve)(array_t v, size_t alloc) \ + M_F(name, _reserve)(array_t m_v, size_t alloc) \ { \ - M_ARRA4_CONTRACT(v); \ + M_ARRA4_CONTRACT(m_v); \ /* NOTE: Reserve below needed size to perform a shrink to fit */ \ - if (v->size > alloc) { \ - alloc = v->size; \ + if (m_v->size > alloc) { \ + alloc = m_v->size; \ } \ if (M_UNLIKELY (alloc == 0)) { \ - M_CALL_FREE(oplist, v->ptr); \ - v->size = v->alloc = 0; \ - v->ptr = NULL; \ + M_CALL_FREE(oplist, m_v->ptr); \ + m_v->size = m_v->alloc = 0; \ + m_v->ptr = NULL; \ } else { \ - type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \ - if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \ - M_MEMORY_FULL(sizeof (type) * alloc); \ - return; \ - } \ - v->ptr = ptr; \ - v->alloc = alloc; \ + M_C3(m_arra4_,name,_realloc)(m_v, alloc); \ } \ - M_ARRA4_CONTRACT(v); \ + M_ARRA4_CONTRACT(m_v); \ } \ \ M_IF_METHOD(INIT, oplist)( \ @@ -476,13 +462,7 @@ M_MEMORY_FULL(sizeof (type) * alloc); \ return NULL; \ } \ - type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \ - if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \ - M_MEMORY_FULL(sizeof (type) * alloc); \ - return NULL; \ - } \ - v->ptr = ptr; \ - v->alloc = alloc; \ + M_C3(m_arra4_,name,_realloc)(v, alloc); \ } \ for(size_t i = v->size ; i < size; i++) \ M_CALL_INIT(oplist, v->ptr[i]); \ @@ -604,13 +584,7 @@ M_MEMORY_FULL(sizeof (type) * alloc); \ return ; \ } \ - type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \ - if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \ - M_MEMORY_FULL(sizeof (type) * alloc); \ - return; \ - } \ - v->ptr = ptr; \ - v->alloc = alloc; \ + M_C3(m_arra4_,name,_realloc)(v, alloc); \ } \ memmove(&v->ptr[i+num], &v->ptr[i], sizeof(type)*(v->size - i) ); \ for(size_t k = i ; k < i+num; k++) \ @@ -886,17 +860,17 @@ } \ \ M_INLINE void \ - M_F(name, _special_stable_sort)(array_t l) \ + M_F(name, _special_stable_sort)(array_t m_v) \ { \ - if (M_UNLIKELY (l->size < 2)) \ + if (M_UNLIKELY (m_v->size < 2)) \ return; \ /* NOTE: if size is <= 4, no need to perform an allocation */ \ - type *temp = M_CALL_REALLOC(oplist, type, NULL, l->size); \ + type *temp = M_CALL_REALLOC(oplist, type, NULL, m_v->size); \ if (M_UNLIKELY_NOMEM (temp == NULL)) { \ - M_MEMORY_FULL(sizeof (type) * l->size); \ + M_MEMORY_FULL(sizeof (type) * m_v->size); \ return ; \ } \ - M_C3(m_arra4_,name,_stable_sort_noalloc)(l->ptr, l->size, temp); \ + M_C3(m_arra4_,name,_stable_sort_noalloc)(m_v->ptr, m_v->size, temp); \ M_CALL_FREE(oplist, temp); \ } \ ,) /* IF SWAP & SET methods */ \ @@ -948,12 +922,7 @@ should have exhausted all memory before reaching such sizes. */ \ M_ASSERT_INDEX(a1->size, newSize); \ if (newSize > a1->alloc) { \ - type *ptr = M_CALL_REALLOC(oplist, type, a1->ptr, newSize); \ - if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \ - M_MEMORY_FULL(sizeof (type) * newSize); \ - } \ - a1->ptr = ptr; \ - a1->alloc = newSize; \ + M_C3(m_arra4_,name,_realloc)(a1, newSize); \ } \ M_ASSERT(a1->ptr != NULL); \ M_ASSERT(a2->ptr != NULL); \ diff --git a/m-core.h b/m-core.h index 2782664b..4c30b5ad 100644 --- a/m-core.h +++ b/m-core.h @@ -3404,6 +3404,7 @@ M_INLINE size_t m_core_cstr_hash(const char str[]) #define M_LIMITS_LIMITS(a) ,a, #define M_PROPERTIES_PROPERTIES(a) ,a, #define M_EMPLACE_TYPE_EMPLACE_TYPE(a) ,a, +#define M_USER_DATA_USER_DATA(a) ,a, // As attribute customization #define M_NEW_NEW(a) ,a, #define M_DEL_DEL(a) ,a, @@ -3495,6 +3496,7 @@ M_INLINE size_t m_core_cstr_hash(const char str[]) #define M_GET_LIMITS(...) M_GET_METHOD(LIMITS, M_LIMITS_DEFAULT, __VA_ARGS__) #define M_GET_PROPERTIES(...) M_GET_METHOD(PROPERTIES, (), __VA_ARGS__) #define M_GET_EMPLACE_TYPE(...) M_GET_METHOD(EMPLACE_TYPE, M_NO_DEFAULT, __VA_ARGS__) +#define M_GET_USER_DATA(...) M_GET_METHOD(USER_DATA, M_NO_DEFAULT, __VA_ARGS__) // As attribute customization #define M_GET_NEW(...) M_GET_METHOD(NEW, M_NEW_DEFAULT, __VA_ARGS__) #define M_GET_DEL(...) M_GET_METHOD(DEL, M_DEL_DEFAULT, __VA_ARGS__) @@ -3573,6 +3575,7 @@ M_INLINE size_t m_core_cstr_hash(const char str[]) //#define M_CALL_LIMITS(oplist, ...) M_APPLY_API(M_GET_LIMITS oplist, oplist, __VA_ARGS__) //#define M_CALL_PROPERTIES(oplist, ...) M_APPLY_API(M_GET_PROPERTIES oplist, oplist, __VA_ARGS__) //#define M_CALL_EMPLACE_TYPE(oplist, ...) M_APPLY_API(M_GET_EMPLACE_TYPE oplist, oplist, __VA_ARGS__) +//#define M_CALL_USER_DATA(oplist, ...) M_APPLY_API(M_GET_USER_DATA oplist, oplist, __VA_ARGS__) // As attribute customization #define M_CALL_DEL(oplist, ...) M_APPLY_API(M_GET_DEL oplist, oplist, __VA_ARGS__) #define M_CALL_REALLOC(oplist, ...) M_APPLY_API(M_GET_REALLOC oplist, oplist, __VA_ARGS__) @@ -3632,7 +3635,7 @@ M_INLINE size_t m_core_cstr_hash(const char str[]) #define M_OPLAPI_EXTRACT_API_7(...) __VA_ARGS__ -/* Generic API transformation. +/* Generic API transformation (GAIA). * The API itself is described in the operator mapping with the method name. * * Usage in oplist: @@ -3649,6 +3652,7 @@ M_INLINE size_t m_core_cstr_hash(const char str[]) * - ARG[1-9] : the associated argument number of the operator * - ARGPTR[1-9] : the pointer of the associated argument number of the operator * - OPLIST: the oplist + * - USER: The user data provided by the user to the container */ /* Needed duplicate of M_RET_ARG2 as we call it within a M_RET_ARG2 @@ -3690,6 +3694,18 @@ M_INLINE size_t m_core_cstr_hash(const char str[]) #define M_REORDER_ARGLIST_FUNC_ARGPTR8(arglist) , & M_RET_ARG9 arglist , #define M_REORDER_ARGLIST_FUNC_ARGPTR9(arglist) , & M_RET_ARG10 arglist , +#define M_REORDER_ARGLIST_FUNC_SIZEOFARG1(arglist) , sizeof ( M_RET_ARG2 arglist ), +#define M_REORDER_ARGLIST_FUNC_SIZEOFARG2(arglist) , sizeof ( M_RET_ARG3 arglist ), +#define M_REORDER_ARGLIST_FUNC_SIZEOFARG3(arglist) , sizeof ( M_RET_ARG4 arglist ), +#define M_REORDER_ARGLIST_FUNC_SIZEOFARG4(arglist) , sizeof ( M_RET_ARG5 arglist ), +#define M_REORDER_ARGLIST_FUNC_SIZEOFARG5(arglist) , sizeof ( M_RET_ARG6 arglist ), +#define M_REORDER_ARGLIST_FUNC_SIZEOFARG6(arglist) , sizeof ( M_RET_ARG7 arglist ), +#define M_REORDER_ARGLIST_FUNC_SIZEOFARG7(arglist) , sizeof ( M_RET_ARG8 arglist ), +#define M_REORDER_ARGLIST_FUNC_SIZEOFARG8(arglist) , sizeof ( M_RET_ARG9 arglist ), +#define M_REORDER_ARGLIST_FUNC_SIZEOFARG9(arglist) , sizeof ( M_RET_ARG10 arglist ), + +#define M_REORDER_ARGLIST_FUNC_USER(arglist) , m_v->user_data , + #define M_REORDER_ARGLIST_FUNC_ID(arg) , arg , M_EAT /* Perform the API translation of the return code depending @@ -4287,11 +4303,11 @@ m_core_parse2_enum (const char str[], const char **endptr) oplist has been recorded for this type. */ #define M_LET(a, ...) \ - M_ID(M_LETI0 ((M_REVERSE(a, __VA_ARGS__, \ - M_IF(M_PARENTHESIS_P(a))(M_LETI_VAR_NAME_A, \ - M_LETI_VAR_NAME_B)(a) )))) + M_ID(M_LETI0 ((M_REVERSE(a, __VA_ARGS__, M_LETI_VAR_NAME(a) )))) // 1b. Generate a unique name based on the first variable and the line number +#define M_LETI_VAR_NAME(a) \ + M_IF(M_PARENTHESIS_P(a))(M_LETI_VAR_NAME_A, M_LETI_VAR_NAME_B)(a) #define M_LETI_VAR_NAME_A(var) M_C3(_local_cont_, M_RET_ARG1 var, __LINE__) #define M_LETI_VAR_NAME_B(var) M_C3(_local_cont_, var, __LINE__) // 2. Evaluate with or without and inject oplist @@ -4964,6 +4980,60 @@ m_core_parse2_enum (const char str[], const char **endptr) } +/************************************************************/ +/******************* EMBEDDED USER DATA ********************/ +/************************************************************/ + +// LIMITATION: USER_DATA shall be compatible with M_BASIC_OPLIST or M_PTR_OPLIST +// Only integer or pointers are supported. +// Nothing is called on destruction + +// Within a generated service, 'v' refers to the main object + +// Expand to a user data field, to be embedded in the structure +#define M_USER_FIELD(opl) M_IF_METHOD(USER_DATA, opl)(M_USER_FIELD_EXPAND, M_EAT)(opl) +#define M_USER_FIELD_EXPAND(opl) M_GET_USER_DATA opl user_data; + +// Expand to a user data param, to be used in a function prototype +#define M_USER_PARAM(opl) M_IF_METHOD(USER_DATA, opl)(M_USER_PARAM_EXPAND, M_EAT)(opl) +#define M_USER_PARAM_EXPAND(opl) , M_GET_USER_DATA opl user_data + +// Expand to a user data param, to be used for calling a function +#define M_USER_CALL(opl, value) M_IF_METHOD(USER_DATA, opl)(M_USER_CALL_EXPAND, M_EAT)(opl, value) +#define M_USER_CALL_EXPAND(opl, value) , value + +// Initialize a user data from its param +#define M_USER_INIT(opl, v, value) M_IF_METHOD(USER_DATA, opl)(M_USER_INIT_EXPAND, M_USER_VOID)(opl, v, value) +#define M_USER_INIT_EXPAND(opl, v, value) v->user_data = value; + +// Expand to an instruction valid but without any effect. +#define M_USER_VOID(...) (void) 0 + +// Return the user data of an object +#define M_USER_DATA(obj) obj->user_data + +// generate set / get user_data method +#define M_USER_DEF_GEN(opl, name, type) M_IF_METHOD(USER_DATA, opl)(M_USER_DEF_GEN_EXPAND, M_EAT)(opl, name, type) +#define M_USER_DEF_GEN_EXPAND(opl, name, type) \ + struct M_F(name, _user_s) { M_GET_USER_DATA opl user_data; }; \ + M_INLINE M_GET_USER_DATA opl M_F(name, _user_data)(type const m_v) { return m_v->user_data; } \ + M_INLINE void M_F(name, _update_user_data)(type m_v, M_GET_USER_DATA opl user_data) { m_v->user_data = user_data; } + +// To be used in oplist instead of INIT method +#define M_USER_INIT_OPERATOR(name, opl) \ + M_IF_METHOD(USER_DATA, opl)( INIT(API(M_F(name,_init), NONE, ARG1, USER)), INIT(M_F(name, _init)) ) + +// Give 'value' as user data for the initialization of the following variables +#define M_USING_IN_LET(value, a, ...) \ + M_ID(M_USING_LETI0 ((value, M_REVERSE(a, __VA_ARGS__, M_LETI_VAR_NAME(a) )))) +#define M_USING_LETI0(list) M_USING_LETI1 list +#define M_USING_LETI1(value, cont, oplist, ...) \ + for(bool cont = true; cont; cont = false) \ + for(M_USING_SELF_TYPE(M_GLOBAL_OPLIST(oplist)) m_v[1] = { { value } }; cont ; cont = false) \ + M_LETI2(cont, M_GLOBAL_OPLIST(oplist), __VA_ARGS__) +#define M_USING_SELF_TYPE(op) struct M_F(M_GET_NAME op , _user_s) + + /************************************************************/ /******************* Exponential Backoff ********************/ /************************************************************/