/* * M*LIB - Function Object module * * Copyright (c) 2017-2023, Patrick Pelissier * All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * + Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * + Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef MSTARLIB_FUNCOBJ_H #define MSTARLIB_FUNCOBJ_H #include "m-core.h" /* Define a function object interface of name 'name' * with a function like retcode, type of param1, type of param 2, ... * USAGE: * FUNC_OBJ_ITF_DEF(name, retcode type, type of param1, type of param 2, ...) */ #define M_FUNC_OBJ_ITF_DEF(name, ...) \ M_FUNC_OBJ_ITF_DEF_AS(name, M_F(name,_t), __VA_ARGS__) /* Define a function object interface of name 'name' * as the given name name_t * USAGE: * FUNC_OBJ_ITF_DEF_AS(name, name_t, retcode type, type of param1, type of param 2, ...) */ #define M_FUNC_OBJ_ITF_DEF_AS(name, name_t, ...) \ M_BEGIN_PROTECTED_CODE \ M_IF_NARGS_EQ1(__VA_ARGS__)(M_FUNC0BJ_ITF_NO_PARAM_DEF, M_FUNC0BJ_ITF_PARAM_DEF)(name, name_t, __VA_ARGS__) \ M_END_PROTECTED_CODE /* Define a function object instance of name 'name' based on the interface 'base_name' * The function is defined using: * - the prototype of the inherited interface * - the parameters of the function are named as per the list param_list * - the core of the function given in 'callback_core' * - optionals member attributes of the function object can be defined after the core * (just like for tuple & variant: (name, type [, oplist]) * * In the core of the function, parameters are accessible just like a normal function. * A special variable named 'self' that refers to the function object itself * can be used to access member attributes using the syntax self->param1, ... * * There shall be **exactly** the same number of parameters in 'param_list' than * the number of parameters of the interface 'base_name' * * USAGE/EXAMPLE: * FUNC_OBJ_INS_DEF(name, base_name, (param1, ...), { return param1 * self->member1 }, (member1, int), ...) */ #define M_FUNC_OBJ_INS_DEF(name, base_name, param_list, ...) \ M_FUNC_OBJ_INS_DEF_AS(name, M_F(name,_t), base_name, param_list, __VA_ARGS__) /* Define a function object instance of name 'name' based on the interface 'base_name' * as the given name name_t. * See FUNC_OBJ_INS_DEF for additional details. * * USAGE/EXAMPLE: * FUNC_OBJ_INS_DEF_AS(name, name_t, base_name, (param1, ...), { return param1 * self->member1 }, (member1, int), ...) */ #define M_FUNC_OBJ_INS_DEF_AS(name, name_t, base_name, param_list, ...) \ M_BEGIN_PROTECTED_CODE \ M_IF_NARGS_EQ1(__VA_ARGS__)(M_FUNC0BJ_INS_NO_ATTR_DEF, M_FUNC0BJ_INS_ATTR_DEF)(name, name_t, base_name, param_list, __VA_ARGS__) \ M_END_PROTECTED_CODE /* OPLIST of the instanced function object * USAGE: * FUNC_OBJ_INS_OPLIST(name, oplist of the attr1, ...) */ #define M_FUNC_OBJ_INS_OPLIST(...) \ M_IF_NARGS_EQ1(__VA_ARGS__)(M_FUNC0BJ_INS_NO_ATTR_OPLIST, M_FUNC0BJ_INS_ATTR_OPLIST_P1)( __VA_ARGS__) /*****************************************************************************/ /******************************** INTERNAL ***********************************/ /*****************************************************************************/ /* To be used by M_IF_FUNCOBJ macro defined in m-core. NOTE: It is reversed (0 instead of 1) so that it can be used in M_IF reliabely. */ #define M_FUNC0BJ_IS_NOT_DEFINED 0 /* Design Constraints: * callback SHALL be the first member of the structures in all the definitions. * * Structure definitions are specialized in function of the presence or not * of parameters and/or attributes * FIXME: How to factorize reasonnably well between the definitions? */ /* Specialization of the OPLIST in function if there is at least one member or not */ #define M_FUNC0BJ_INS_NO_ATTR_OPLIST(name) ( \ NAME(name), \ TYPE(M_F(name, _ct)), \ CLEAR(M_F(name, _clear)), \ INIT(M_F(name,_init)) \ ) /* Validate the oplist before going further */ #define M_FUNC0BJ_INS_ATTR_OPLIST_P1(name, ...) \ M_IF(M_REDUCE(M_OPLIST_P, M_AND, __VA_ARGS__))(M_FUNC0BJ_INS_ATTR_OPLIST_P3, M_FUNC0BJ_INS_ATTR_OPLIST_FAILURE)(name, __VA_ARGS__) /* Prepare a clean compilation failure */ #define M_FUNC0BJ_INS_ATTR_OPLIST_FAILURE(name, ...) \ ((M_LIB_ERROR(ONE_ARGUMENT_OF_FUNC_OBJ_INS_OPLIST_IS_NOT_AN_OPLIST, name, __VA_ARGS__))) /* Define the oplist of the instance */ #define M_FUNC0BJ_INS_ATTR_OPLIST_P3(name, ...) ( \ NAME(name), \ TYPE(M_F(name, _ct)), \ INIT_WITH(M_F(name, _init_with)), \ CLEAR(M_F(name, _clear)), \ M_IF_METHOD_ALL(INIT, __VA_ARGS__)(INIT(M_F(name,_init)),), \ PROPERTIES(( LET_AS_INIT_WITH(1) )) \ ) /******************************** INTERNAL ***********************************/ /* Specialization of the definition a function object interface of name 'name' * with a function like "retcode (void)" that doesn't have any input parameters. * Define the following types to be used by instance: * - M_F(name, _retcode_ct): internal type of the return code * - M_F(name, _callback_ct): internal type of the callback. * - M_F(name, _ct): synonym of main type used by oplist. */ #define M_FUNC0BJ_ITF_NO_PARAM_DEF(name, interface_t, retcode) \ \ /* Forward declaration */ \ struct M_F(name, _s); \ \ /* Internal type for instance */ \ typedef retcode M_F(name, _retcode_ct); \ /* No parameters to the callback */ \ typedef retcode(*M_F(name, _callback_ct))(struct M_F(name, _s) *); \ \ typedef struct M_F(name, _s) { \ M_F(name, _callback_ct) callback; \ } *interface_t; \ \ /* Internal type for oplist & instance */ \ typedef interface_t M_F(name, _ct); \ \ M_INLINE retcode \ M_F(name, _call)(interface_t funcobj) \ { \ M_IF(M_KEYWORD_P(void, retcode)) ( /* nothing */,return) \ funcobj->callback(funcobj); \ } /* Specialization of the definition a function object interface of name 'name' * with a function like retcode, type of param1, type of param 2, ... * with mandatory input parameters. * Define the following types to be used by instance: * - M_F(name, _retcode_ct): internal type of the return code * - M_F(name, _callback_ct): internal type of the callback. * - M_C4(name, _param_, num, _ct) for each parameter defined * - M_F(name, _ct): synonym of main type used by oplist. */ #define M_FUNC0BJ_ITF_PARAM_DEF(name, interface_t, retcode, ...) \ \ /* Forward declaration */ \ struct M_F(name, _s); \ \ /* Internal types for instance */ \ typedef retcode M_F(name, _retcode_ct); \ /* Define types for all parameters */ \ M_MAP3(M_FUNC0BJ_BASE_TYPE, name, __VA_ARGS__) \ /* Define callback type with all parameters */ \ typedef retcode(*M_F(name, _callback_ct))(struct M_F(name, _s) *, __VA_ARGS__); \ \ typedef struct M_F(name, _s) { \ M_F(name, _callback_ct) callback; \ } *interface_t; \ \ /* Internal type for oplist & instance */ \ typedef interface_t M_F(name, _ct); \ \ M_INLINE retcode \ M_F(name, _call)(interface_t funcobj \ M_MAP3(M_FUNC0BJ_BASE_ARGLIST, name, __VA_ARGS__) ) \ { \ /* If the retcode is 'void', don't return the value of the callback */ \ M_IF(M_KEYWORD_P(void, retcode)) ( /* nothing */,return) \ funcobj->callback(funcobj M_MAP3(M_FUNC0BJ_BASE_ARGCALL, name, __VA_ARGS__) ); \ } /******************************** INTERNAL ***********************************/ /* Specialization of the definition a function object instance of name 'name' * with no member attribute. */ #define M_FUNC0BJ_INS_NO_ATTR_DEF(name, instance_t, base_name, param_list, callback_core) \ typedef struct M_F(name, _s) { \ M_C(base_name, _callback_ct) callback; \ } instance_t[1]; \ \ /* Internal type for oplist */ \ typedef instance_t M_F(name, _ct); \ \ M_INLINE M_C(base_name, _retcode_ct) \ M_F(name, _callback)(M_C(base_name, _ct) _self \ M_IF_EMPTY(M_OPFLAT param_list)( \ /* No param */, \ M_MAP3(M_FUNC0BJ_INS_ARGLIST, base_name, M_OPFLAT param_list) \ ) \ ) \ { \ struct M_F(name, _s) *self = (struct M_F(name, _s) *)_self; \ (void) self; /* maybe unused */ \ callback_core; \ } \ \ M_INLINE void \ M_F(name, _init_with)(instance_t obj) \ { \ obj->callback = M_F(name, _callback); \ } \ \ M_INLINE void \ M_F(name, _clear)(instance_t obj) \ { \ (void) obj; /* nothing to do */ \ } \ \ M_INLINE struct M_C(base_name, _s) * \ M_F(name, _as_interface)(instance_t obj) \ { \ return (struct M_C(base_name, _s) *) obj; \ } \ \ M_INLINE void \ M_F(name, _init)(instance_t obj) \ { \ obj->callback = M_F(name, _callback); \ } \ /* Specialization of the definition a function object instance of name 'name' * with mandatory member attribute. * First inject oplist in member attributes. */ #define M_FUNC0BJ_INS_ATTR_DEF(name, instance_t, base_name, param_list, callback_core, ...) \ M_FUNC0BJ_INS_ATTR_DEF_P2(name, instance_t, base_name, param_list, callback_core, M_FUNC0BJ_INJECT_GLOBAL(__VA_ARGS__) ) /* Inject the oplist within the list of arguments */ #define M_FUNC0BJ_INJECT_GLOBAL(...) \ M_MAP_C(M_FUNC0BJ_INJECT_OPLIST_A, __VA_ARGS__) /* Transform (x, type) into (x, type, oplist) if there is global registered oplist or (x, type, M_BASIC_OPLIST) if there is no global one, or keep (x, type, oplist) if oplist was already present */ #define M_FUNC0BJ_INJECT_OPLIST_A( duo_or_trio ) \ M_FUNC0BJ_INJECT_OPLIST_B duo_or_trio #define M_FUNC0BJ_INJECT_OPLIST_B( f, ... ) \ M_IF_NARGS_EQ1(__VA_ARGS__)( (f, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)()), (f, __VA_ARGS__) ) // Test if all third argument of all arguments is an oplist #define M_FUNC0BJ_IF_ALL_OPLIST(...) \ M_IF(M_REDUCE(M_FUNC0BJ_IS_OPLIST_P, M_AND, __VA_ARGS__)) // Test if the third argument is an oplist. a is a trio (name, type, oplist) #define M_FUNC0BJ_IS_OPLIST_P(a) \ M_OPLIST_P(M_RET_ARG3 a) /* Validate the oplist before going further */ #define M_FUNC0BJ_INS_ATTR_DEF_P2(name, instance_t, base_name, param_list, callback_core, ...) \ M_FUNC0BJ_IF_ALL_OPLIST(__VA_ARGS__)(M_FUNC0BJ_INS_ATTR_DEF_P3, M_FUNC0BJ_INS_ATTR_DEF_FAILURE)(name, instance_t, base_name, param_list, callback_core, __VA_ARGS__) /* Stop processing with a compilation failure */ #define M_FUNC0BJ_INS_ATTR_DEF_FAILURE(name, instance_t, base_name, param_list, callback_core, ...) \ M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(FUNC_OBJ_INS_DEF): at least one of the given argument is not a valid oplist: " #__VA_ARGS__) /* Expand the Function Object with members */ #define M_FUNC0BJ_INS_ATTR_DEF_P3(name, instance_t, base_name, param_list, callback_core, ...) \ typedef struct M_F(name, _s) { \ /* Callback is the mandatory first argument */ \ M_C(base_name, _callback_ct) callback; \ /* All the member attribute of the Function Object */ \ M_MAP(M_FUNC0BJ_INS_ATTR_STRUCT, __VA_ARGS__) \ } instance_t[1]; \ \ /* Internal type for oplist */ \ typedef instance_t M_F(name, _ct); \ \ M_FUNC0BJ_CONTROL_ALL_OPLIST(name, __VA_ARGS__) \ \ M_INLINE M_C(base_name, _retcode_ct) \ M_F(name, _callback)(M_C(base_name, _ct) _self \ M_IF_EMPTY(M_OPFLAT param_list)( \ /* No param */, \ M_MAP3(M_FUNC0BJ_INS_ARGLIST, base_name, M_OPFLAT param_list) \ ) \ ) \ { \ /* Let's go through an uintptr_t to avoid [broken] aliasing detection by compiler */ \ uintptr_t __self = (uintptr_t) _self; \ struct M_F(name, _s) *self = (struct M_F(name, _s) *)(void*)__self; \ (void) self; /* maybe unused */ \ callback_core; \ } \ \ M_INLINE void \ M_F(name, _init_with)(instance_t obj M_MAP(M_FUNC0BJ_INS_ATTR_LIST, __VA_ARGS__)) \ { \ obj->callback = M_F(name, _callback); \ M_MAP(M_FUNC0BJ_INS_ATTR_INIT_SET, __VA_ARGS__); \ } \ \ M_INLINE void \ M_F(name, _clear)(instance_t obj) \ { \ M_MAP(M_FUNC0BJ_INS_ATTR_CLEAR, __VA_ARGS__); \ } \ \ M_INLINE struct M_C(base_name, _s) * \ M_F(name, _as_interface)(instance_t obj) \ { \ return (struct M_C(base_name, _s) *) obj; \ } \ \ M_IF(M_FUNC0BJ_TEST_METHOD_P(INIT, __VA_ARGS)) \ ( \ M_INLINE void \ M_F(name, _init)(instance_t obj) \ { \ obj->callback = M_F(name, _callback); \ M_MAP(M_FUNC0BJ_INS_ATTR_INIT, __VA_ARGS__); \ } \ , /* END OF INIT METHOD */ ) \ /* Define a numbered type of a parameter of the callback*/ #define M_FUNC0BJ_BASE_TYPE(name, num, type) \ typedef type M_C4(name, _param_, num, _ct); /* Define a list of the type of arguments for a function definition */ #define M_FUNC0BJ_BASE_ARGLIST(name, num, type) \ M_DEFERRED_COMMA type M_C(param_, num) /* Define a list of arguments for a function call */ #define M_FUNC0BJ_BASE_ARGCALL(name, num, type) \ M_DEFERRED_COMMA M_C(param_, num) /* Helper macros */ /* arg = (name, type [, oplist]) */ #define M_FUNC0BJ_INS_ATTR_STRUCT(arg) \ M_RET_ARG2 arg M_RET_ARG1 arg; #define M_FUNC0BJ_INS_ATTR_LIST(arg) \ M_DEFERRED_COMMA M_RET_ARG2 arg const M_RET_ARG1 arg #define M_FUNC0BJ_INS_ATTR_INIT(arg) \ M_CALL_INIT(M_RET_ARG3 arg, obj -> M_RET_ARG1 arg); #define M_FUNC0BJ_INS_ATTR_INIT_SET(arg) \ M_CALL_INIT_SET(M_RET_ARG3 arg, obj -> M_RET_ARG1 arg, M_RET_ARG1 arg); #define M_FUNC0BJ_INS_ATTR_CLEAR(arg) \ M_CALL_CLEAR(M_RET_ARG3 arg, obj -> M_RET_ARG1 arg); /* Define the list of arguments of the instance of the callback */ #define M_FUNC0BJ_INS_ARGLIST(name, num, param) \ M_DEFERRED_COMMA M_C4(name, _param_, num, _ct) param /* Macros for testing for a method presence in all the attributes */ #define M_FUNC0BJ_TEST_METHOD2_P(method, op) \ M_TEST_METHOD_P(method, op) #define M_FUNC0BJ_TEST_METHOD1_P(method, arg) \ M_APPLY(M_FUNC0BJ_TEST_METHOD2_P, method, M_RET_ARG3 arg) #define M_FUNC0BJ_TEST_METHOD_P(method, ...) \ M_IF(M_REDUCE2(M_FUNC0BJ_TEST_METHOD1_P, M_AND, method, __VA_ARGS__)) /* Macro for checking compatible type and oplist for all the attributes */ #define M_FUNC0BJ_CONTROL_ALL_OPLIST(name, ...) \ M_MAP2(M_FUNC0BJ_CONTROL_OPLIST, name, __VA_ARGS__) #define M_FUNC0BJ_CONTROL_OPLIST(name, a) \ M_CHECK_COMPATIBLE_OPLIST(name, M_RET_ARG1 a, M_RET_ARG2 a, M_RET_ARG3 a) /******************************** INTERNAL ***********************************/ #if M_USE_SMALL_NAME #define FUNC_OBJ_ITF_DEF M_FUNC_OBJ_ITF_DEF #define FUNC_OBJ_ITF_DEF_AS M_FUNC_OBJ_ITF_DEF_AS #define FUNC_OBJ_INS_DEF M_FUNC_OBJ_INS_DEF #define FUNC_OBJ_INS_DEF_AS M_FUNC_OBJ_INS_DEF_AS #define FUNC_OBJ_INS_OPLIST M_FUNC_OBJ_INS_OPLIST #endif #endif