Patrick Wright
Patrick Wright

Reputation: 1663

Use C/C++ Macros to Generate Function Signature

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

Answers (5)

Patrick Wright
Patrick Wright

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

galgoog
galgoog

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

Bitwize
Bitwize

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:

  • You want the expansion to contain the size of __VA_ARGS__ / 2 (at expansion time)
  • You want the expansion of __VA_ARGS__ to produce Variant(<arg 1>), Variant(<arg 3>), Variant(<arg 5>), Variant()
  • You want the expansion of __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.

Getting the size of __VA_ARGS__ / 2

Getting the size of __VA_ARGS__ usually requires two macros:

  • One that passes __VA_ARGS__, N, N-1, N-2, ... into a helper macro, and
  • Another that extracts that N 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

Making __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)

Creating a list of index values

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]

Putting it all together

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

HolyBlackCat
HolyBlackCat

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

&#212;rel
&#212;rel

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

Related Questions