Reputation: 1663
I am attempting to use macros in C/C++ to generate some boiler-plate function declarations and definitions.
I would like a macro similar to:
DECLARE_FUNCTION(myFunction, int, A, int, B, char, C)
to generate the following code (please ignore the fact that this code seems pointless, it is just a simplified example)
void myFunction(int A, int B, char C) {
myFunction_PROXY((Variant[4]){Variant(A), Variant(B), Variant(C), Variant()});
}
void myFunction_PROXY(const Variant (&args)[4]) {
myFunction_HANDLER(args[0], args[1], args[2]);
}
void myFunction_HANDLER(int A, int B, char C) {
}
Notice how I need both the function signature (with types and names) as well as just the names to be used in the Variant() constructors. Thus, I would I assume I need to selectively "loop" through the VA_ARGS value of the macro to get different combinations of arguments and apply them in different ways.
Where I already have a class named Variant defined.
From my research, I believe this involves some combination of "recursive macros" however, I can't seem to understand how to get my desired output.
Would anyone be able to help or at least point me towards a good explanation on how recursive macros in C work?
Thanks
Upvotes: 3
Views: 3213
Reputation: 1663
Based upon many of the other answers and comments, here is what I cam up with that solves my particular problem:
#define END(...) END_(__VA_ARGS__)
#define END_(...) __VA_ARGS__##_END
#define PARAMS_LOOP_0(type_, name_) PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_A
#define PARAMS_LOOP_A(type_, name_) , PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_B
#define PARAMS_LOOP_B(type_, name_) , PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_A
#define PARAMS_LOOP_0_END
#define PARAMS_LOOP_A_END
#define PARAMS_LOOP_B_END
#define PARAMS_LOOP_BODY(type_, name_) type_ name_
#define VAR_LOOP_0(type_, name_) , VAR_LOOP_BODY(type_, name_) VAR_LOOP_A
#define VAR_LOOP_A(type_, name_) , VAR_LOOP_BODY(type_, name_) VAR_LOOP_B
#define VAR_LOOP_B(type_, name_) , VAR_LOOP_BODY(type_, name_) VAR_LOOP_A
#define VAR_LOOP_0_END
#define VAR_LOOP_A_END
#define VAR_LOOP_B_END
#define VAR_LOOP_BODY(type_, name_) name_
//------------------------------------------------------------------------------
#define DEFINE_SLOT(func_, ...) \
void func_(END(PARAMS_LOOP_0 __VA_ARGS__)) { invoke(this, &func_##_SLOT END(VAR_LOOP_0 __VA_ARGS__)); }\
void func_##_SLOT(END(PARAMS_LOOP_0 __VA_ARGS__))
#define DECLARE_SLOT(func_, ...) \
void func_(END(PARAMS_LOOP_0 __VA_ARGS__));\
void func_##_SLOT(END(PARAMS_LOOP_0 __VA_ARGS__))
#define DECLARE_VIRTUAL_SLOT(func_, ...) \
virtual void func_(END(PARAMS_LOOP_0 __VA_ARGS__));\
virtual void func_##_SLOT(END(PARAMS_LOOP_0 __VA_ARGS__))
#define DECLARE_SLOT_INTERFACE(func_, ...) \
virtual void func_(END(PARAMS_LOOP_0 __VA_ARGS__)) = 0;\
virtual void func_##_SLOT(END(PARAMS_LOOP_0 __VA_ARGS__)) = 0
#define DECLARE_SLOT_OVERRIDE(func_, ...) \
void func_(END(PARAMS_LOOP_0 __VA_ARGS__)) override;\
void func_##_SLOT(END(PARAMS_LOOP_0 __VA_ARGS__)) override
I have adopted some of Qt's naming conventions by calling these "slots."
These macros would be used as follows:
//In the .h file
class MyBase {
public:
DECLARE_SLOT_INTERFACE(foo, (int, a) (int, b));
};
class MyClass : public MyBase {
public:
DECLARE_SLOT_OVERRIDE(foo, (int, a) (int, b));
DECLARE_SLOT(bar, (bool, a) (const MyClass& obj));
};
//In the .cpp file
DEFINE_SLOT(MyClass::foo, (int, a) (int, b)) {
}
DEFINE_SLOT(MyClass::bar, (bool, a) (const MyClass& obj)) {
}
Upvotes: 0
Reputation: 21
I've found extremely useful all your reply; in my case I had the need to protect a set of routines implemented as part of an old application.
Constraints: mutex, minimize changes in the code.
The "MTX_DB_PROTECTED_FUNCTION" macro works great.
#define END(...) END_(__VA_ARGS__)
#define END_(...) __VA_ARGS__##_END
#define PARAMS_LOOP_0(type_, name_) PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_A
#define PARAMS_LOOP_A(type_, name_) , PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_B
#define PARAMS_LOOP_B(type_, name_) , PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_A
#define PARAMS_LOOP_0_END
#define PARAMS_LOOP_A_END
#define PARAMS_LOOP_B_END
#define PARAMS_LOOP_BODY(type_, name_) type_ name_
#define VAR_LOOP_0(type_, name_) VAR_LOOP_BODY(type_, name_) VAR_LOOP_A
#define VAR_LOOP_A(type_, name_) , VAR_LOOP_BODY(type_, name_) VAR_LOOP_B
#define VAR_LOOP_B(type_, name_) , VAR_LOOP_BODY(type_, name_) VAR_LOOP_A
#define VAR_LOOP_0_END
#define VAR_LOOP_A_END
#define VAR_LOOP_B_END
#define VAR_LOOP_BODY(type_, name_) name_
//https://stackoverflow.com/questions/62903631/use-c-c-macros-to-generate-function-signature
#define MTX_DB_PROTECTED_FUNCTION(type_, func_, seq_) \
\
static type_ _s_mtx_##func_##_protected(END(PARAMS_LOOP_0 seq_));\
\
type_ func_(END(PARAMS_LOOP_0 seq_))\
{\
UTL_AcquireMutex(__FUNCTION__, &g_h_dataFileMutex, OSL_TIMEOUT_INFINITE);\
type_ ret = _s_mtx_##func_##_protected(END(VAR_LOOP_0 seq_));\
UTL_ReleaseMutex(__FUNCTION__, &g_h_dataFileMutex);\
return ret;\
}\
\
\
static type_ _s_mtx_##func_##_protected(END(PARAMS_LOOP_0 seq_))
Sample
Original func
int dummyfunc(char TabId, char checksum)
{
return 0;
}
Macro insertion
MTX_DB_PROTECTED_FUNCTION(int, dummyfunc, (char,TabId)(char,checksum))
{
return 0;
}
Macro expansion
static int _s_mtx_dummyfunc_protected(char TabId , char checksum );
int dummyfunc(char TabId , char checksum )
{
UTL_AcquireMutex(__FUNCTION__, &g_h_dataFileMutex, (unsigned long)(-1));
int ret = _s_mtx_dummyfunc_protected(TabId , checksum );
UTL_ReleaseMutex(__FUNCTION__, &g_h_dataFileMutex);
return ret;
}
static int _s_mtx_dummyfunc_protected(char TabId , char checksum )
{
return 0;
}
For no-params function
MTX_DB_PROTECTED_FUNCTION(int, dummyWoParams,(,))
{
Upvotes: 2
Reputation: 11220
Disclaimer 1:
Honestly, I don't advise you do such machinery behind macros. They will become a massive maintenance burden, and the return is minimal. If you can do this with other more maintainable abstractions, it would be better for you in the long run.
Disclaimer 2:
I am writing this without a compiler at the moment. I may have a syntax error, but these are the generic building blocks to make this solution.
That disclaimer said, this can be done -- but not nicely.
You have several problems here that need to be tackled with a lot of macro trickery:
__VA_ARGS__ / 2
(at expansion time)__VA_ARGS__
to produce Variant(<arg 1>), Variant(<arg 3>), Variant(<arg 5>), Variant()
__VA_ARGS__
to produce Variant[size]{args[0], args[1], args[2], ...}
To start with, I'm going to create a helper called JOIN
:
#define JOIN(a, b) JOIN_H(a, b)
#define JOIN_H(a, b) a ## b
This may seem silly, but what it actually does is ensures that macros being joined together will be evaluated before they get joined -- so that macro functions being called will properly instantiate a join with their result rather than the full name.
__VA_ARGS__ / 2
Getting the size of __VA_ARGS__
usually requires two macros:
__VA_ARGS__, N, N-1, N-2, ...
into a helper macro, andN
at the end.Something like:
#define COUNT_VA_ARGS(...) \
COUNT_VA_ARGS_H(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define COUNT_VA_ARGS_H(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N,...) N
This works because the first one passes all the arguments of __VA_ARGS__
and counts backwards from the Nth number, and we then extract N
.
In your case, you want the __VA_ARGS__ / 2
, so you will need to double those arguments
#define COUNT_VA_ARGS(...) \
COUNT_VA_ARGS_H(__VA_ARGS__, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0)
#define COUNT_VA_ARGS_H(_1, _1, _2, _2, _3, _3, _4, _4, _5, _5, _6, _6, _7, _7, _8, _8, _9, _9, _10, _10, N,...) N
__VA_ARGS__
produce Wrap(<arg 1>), Wrap(<arg 3>), ...
Unlike C++ variadic templates, macros are not able to have expansion expressions where you can wrap each argument. To simulate this in Macros, you pretty much have to have N expansions explicitly defined, and then to call this, you will need to combine the result of one macro to call it.
#define WRAP_VA_ARGS_0(wrap)
#define WRAP_VA_ARGS_1(wrap,x0) wrap(x0)
...
#define WRAP_VA_ARGS_10(wrap,x0,x1, ..., x10) wrap(x0), wrap(x1), ..., wrap(x10)
// Call into one of the concrete ones above
#define WRAP_VA_ARGS(wrap, __VA_ARGS__) JOIN(WRAP_VA_ARGS_, COUNT_VA_ARGS(__VA_ARGS__))(__VA_ARGS__)
Since the expression actually wants every other argument in it, you will again need to double the arguments:
#define WRAP_VA_ARGS_0(wrap)
#define WRAP_VA_ARGS_1(wrap,x0type,x0) wrap(x0)
#define WRAP_VA_ARGS_2(wrap,x0type,x0,x1type,x1) wrap(x0), wrap(x1)
...
Calling WRAP_VA_ARGS(Variant, int, A, float, B)
will now create Variant(A), Variant(B)
Similar to the above wrapping, you will need to find a way to generate a list of numbers, and wrap it. Again this must delegate to the counting wrapper
#define WRAP_COUNT_VA_ARGS_0(wrap)
#define WRAP_COUNT_VA_ARGS_1(wrap) wrap[0]
#define WRAP_COUNT_VA_ARGS_2(wrap) wrap[0], wrap[1]
...
#define WRAP_COUNT_VA_COUNT_ARGS(wrap, ...) JOIN(WRAP_COUNT_VA_ARGS_, COUNT_VA_ARGS(__VA_ARGS))(wrap)
Calling WRAP_COUNT_VA_COUNT_ARGS(args, int, A, float, B)
should generate args[0], args[1]
Trigger warning: this is going to be ugly
#define DECLARE_FUNCTION(name, ...) \
void name(__VA_ARGS__) { \
JOIN(name, _PROXY)((Variant[COUNT_VA_ARGS(__VA_ARGS__)+1]) {WRAP_VA_ARGS(Variant,__VA_ARGS__), Variant()}); \
} \
void JOIN(name, _PROXY)(const Variant (&args)[COUNT_VA_ARGS(__VA_ARGS__) + 1]) { \
JOIN(name, _HANDLER)(WRAP_COUNT_VA_COUNT_ARGS(args, __VA_ARGS__)); \
} \
void JOIN(name, _HANDLER)(__VA_ARGS__) { \
\
}
With any luck, an example of DECLARE_FUNCTION(myFunction, int, A, int, B, char, C)
should produce:
void myFunction(int A, int B, char C) {
myFunction_PROXY((Variant[3+1]{Variant(A), Variant(B), Variant(C), Variant()});
}
void myFunction_PROXY(const Variant (&args)[3+1]) {
myFunction_HANDLER(args[0], args[1], args[2]);
}
void myFunction_HANDLER(int A, int B, char C) {
}
Note: the array is created by the constant expression 3 + 1
, since we need do this arithmetic to account for the Variant()
at the end of myFunction_PROXY
's call
Don't do macros. Macros are bad, mmmm'kay?
Upvotes: 1
Reputation: 96081
Iterating over comma-separated lists with preprocessor requires writing boilerplate macros.
Normally you must write or generate at least O(n)
macros to process list up to n
elements long. @Human-Compiler's answer does it with O(n2)
.
You can get similar macros from Boost.Preprocessor, or use it as an inspiration.
Or you can use a different syntax for your list:
DECLARE_FUNCTION(myFunction, (int,A)(int,B)(char,C))
Then you can process lists of any size, with a fixed amount of macros:
#define DECLARE_FUNCTION(func_, seq_) \
void myFunction(END(PARAMS_LOOP_0 seq_)) { \
myFunction_PROXY(
(Variant[1 END(COUNT_LOOP_A seq_)]){END(VAR_LOOP_A seq_) Variant()}); \
} \
void myFunction_PROXY(const Variant (&args)[1 END(COUNT_LOOP_A seq_)]) { \
const int x = __COUNTER__+1; \
myFunction_HANDLER(END(ARR_LOOP_0 seq_)); \
} \
void myFunction_HANDLER(END(PARAMS_LOOP_0 seq_)) {}
#define END(...) END_(__VA_ARGS__)
#define END_(...) __VA_ARGS__##_END
#define PARAMS_LOOP_0(type_, name_) PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_A
#define PARAMS_LOOP_A(type_, name_) , PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_B
#define PARAMS_LOOP_B(type_, name_) , PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_A
#define PARAMS_LOOP_0_END
#define PARAMS_LOOP_A_END
#define PARAMS_LOOP_B_END
#define PARAMS_LOOP_BODY(type_, name_) type_ name_
#define COUNT_LOOP_A(...) COUNT_LOOP_BODY COUNT_LOOP_B
#define COUNT_LOOP_B(...) COUNT_LOOP_BODY COUNT_LOOP_A
#define COUNT_LOOP_A_END
#define COUNT_LOOP_B_END
#define COUNT_LOOP_BODY +1
#define VAR_LOOP_A(type_, name_) VAR_LOOP_BODY(name_) VAR_LOOP_B
#define VAR_LOOP_B(type_, name_) VAR_LOOP_BODY(name_) VAR_LOOP_A
#define VAR_LOOP_A_END
#define VAR_LOOP_B_END
#define VAR_LOOP_BODY(name_) Variant(name_),
#define ARR_LOOP_0(...) ARR_LOOP_BODY ARR_LOOP_A
#define ARR_LOOP_A(...) , ARR_LOOP_BODY ARR_LOOP_B
#define ARR_LOOP_B(...) , ARR_LOOP_BODY ARR_LOOP_A
#define ARR_LOOP_A_END
#define ARR_LOOP_B_END
#define ARR_LOOP_BODY args[__COUNTER__-x]
With those macros, DECLARE_FUNCTION(myFunction, (int,A)(int,B)(char,C))
expands to:
void myFunction(int A, int B, char C)
{
myFunction_PROXY((Variant[1+1+1+1]){Variant(A), Variant(B), Variant(C), Variant()});
}
void myFunction_PROXY(const Variant (&args)[1+1+1+1])
{
const int x = 0+1;
myFunction_HANDLER(args[1-x], args[2-x], args[3-x]);
}
void myFunction_HANDLER(int A, int B, char C) {}
Note the use __COUNTER__
. It's not a part of the standard C++, but the major compilers support it as an extension. You don't have any other options for getting consecutive array indices, other than writing boilerplate macros.
Upvotes: 1
Reputation: 7622
Not very clear what it variable into your case, you can do what you are looking for with __VA_ARGS__
#define DECLARE_FUNCTION(myFunction, ...) \
static void myFunction##_HANDLER(__VA_ARGS__) { \
\
} \
\
static void myFunction##_PROXY(const Variant (&args)[4]) { \
myFunction##_HANDLER(args[0], args[1], args[2]); \
} \
\
static void myFunction(__VA_ARGS__) { \
myFunction##_PROXY((Variant[4]){Variant(A), Variant(B), Variant(C), Variant()}); \
}
DECLARE_FUNCTION(myFunction, int A, int B, char C)
Will generate
static void myFunction_HANDLER(int A, int B, char C) { }
static void myFunction_PROXY(const Variant (&args)[4]) {
myFunction_HANDLER(args[0], args[1], args[2]);
}
static void myFunction(int A, int B, char C) {
myFunction_PROXY((Variant[4]){Variant(A), Variant(B), Variant(C), Variant()});
}
Upvotes: -1