Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use C/C++ Macros to Generate Function Signature

Tags:

c++

macros

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

like image 543
Patrick Wright Avatar asked Dec 21 '25 01:12

Patrick Wright


2 Answers

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,(,))
{
like image 63
galgoog Avatar answered Dec 23 '25 17:12

galgoog


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.

like image 37
HolyBlackCat Avatar answered Dec 23 '25 17:12

HolyBlackCat