it is used to control the inline semantics. By combining it with optimize option "-fno-inline" for the internal M*LIB function (using _Pragma optimize) we can control easily if we want to use an inline version or call of an external function. Define new user API: * M_USE_EXTERN_DECL : define it before including any MLIB header to requests to call the M*LIB functions and don't inline then. * M_USE_EXTERN_DEF : define it once in a translation unit to define the M*LIB functions. This translation unit shall contain all templated macro too. If you use one option, you need to use the other one too for another translation unit.
416 lines
24 KiB
C
416 lines
24 KiB
C
/*
|
|
* 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[1]; \
|
|
\
|
|
/* 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[1]; \
|
|
\
|
|
/* 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
|