I am trying to write a variadic macro in C(Not C++ so I cannot use Boost) that allows to assign function pointers like following:
#define INIT_METHODS(name,...)
typedef struct{
void (*method1)();
}data1_t;
typedef struct{
void (*method1)();
void (*method2)();
}data2_t;
void function1(){}
void function2(){}
data1_t ptr1 = calloc(sizeof(data1,1));
data2_t ptr2 = calloc(sizeof(data2,1));
INIT_METHODS(ptr1, method1, function1);
INIT_MEGHODS(ptr2, method1,function1, method2, function2);
I am hoping that the macro will generate following code(the size of the variable arguments list should always be even)
ptr1->method1 = function1;
ptr2->method1 = function1;ptr2->method2 = function2;
Unfortunately, I was not able to do it. Following is my attempt.
#define VA_NARGS_IMPL(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define setfunc1 (name,a8) a8
#define setfunc2 (name, a7, a8) name->a7=setfunc1(name,a8)
#define setfunc3 (name, a6, a7, a8) a6;setfunc2(name,a7,a8)
#define setfunc4 (name, a5, a6, a7, a8) name->a5=setfunc3(name,a6,a7,a8)
#define setfunc5 (name, a4, a5, a6, a7, a8) a4;setfun4(name,a5,a6,a7,a8)
#define setfunc6 (name, a3, a4, a5, a6, a7, a8) name->a3=setfunc5(name,a4,a5,a6,a7,a8)
#define setfunc7 (name, a2, a3, a4, a5, a6, a7, a8) a2;setfunc6(name,a3,a4,a5,a6,a7,a8)
#define setfunc8 (name, a1, a2, a3, a4, a5, a6, a7, a8) \
name->a1=setfunc7(name,a2,a3,a4,a5,a6,a7,a8)
#define INIT_METHODSP(name, count, ...) setfunc##count(name, __VA_ARGS__)
#define INIT_METHODS (name, ...) INIT_METHODSP(name, VA_NARGS(__VA_ARGS__), __VA_ARGS__))
Preliminary observations
You should note that the space between setfunc1 and the open parenthesis in:
#define setfunc1 (name,a8) a8
means that the name setfunc1 is an object-like macro, not a function-like macro. If you want (name, a8) to be arguments to a function-like macro, the open parenthesis must not have any space (or comment) after the macro name when you define the macro. When you use the macro, you can have any amount of white space (including comments) between the macro name and its argument list, but not when defining the macro.
Defining INIT_METHODS
You can do what you want — though I still have major reservations about whether it is appropriate to do it.
#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
#define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define INIT_METHODSP(name, count, ...) setfunc##count(name, __VA_ARGS__)
#define INIT_METHODEV(name, count, ...) INIT_METHODSP(name, count, __VA_ARGS__)
#define INIT_METHODS(name, ...) INIT_METHODEV(name, VA_NARGS(__VA_ARGS__), __VA_ARGS__)
#define setfunc2(p, m1, f1) p->m1 = f1
#define setfunc4(p, m1, f1, ...) setfunc2(p, m1, f1); setfunc2(p, __VA_ARGS__)
#define setfunc6(p, m1, f1, ...) setfunc2(p, m1, f1); setfunc4(p, __VA_ARGS__)
#define setfunc8(p, m1, f1, ...) setfunc2(p, m1, f1); setfunc6(p, __VA_ARGS__)
typedef struct{
void (*method1)(void);
}data1_t;
typedef struct{
void (*method1)(void);
void (*method2)(void);
}data2_t;
typedef struct{
void (*method1)(void);
void (*method2)(void);
void (*method3)(void);
void (*method4)(void);
}data4_t;
void function1(void){}
void function2(void){}
data1_t *ptr1 = calloc(sizeof(data1_t), 1));
data2_t *ptr2 = calloc(sizeof(data2_t), 1));
data2_t *ptr4 = calloc(sizeof(data4_t), 1));
INIT_METHODS(ptr1, method1, function1);
INIT_METHODS(ptr2, method1, function1, method2, function2);
INIT_METHODS(ptr4, method1, function1, method2, function2, method3, function3, method4, function4);
Explanation
The VA_NARGS_IMPL and VA_NARGS macros are unchanged apart from spacing or lack thereof.
The INIT_METHODEV macro triggers evaluation (hence the EV) of the count argument. Without this macro, you get to see expansions such as:
setfuncVA_NARGS(method1, function1)(ptr1, method1, function1);
which really isn't very helpful.
The setfuncN macros have one pointer argument (p) and N/2 pairs of arguments listing member and function to initialize it to. Note that there isn't a semicolon after the expansion of setfunc2; that is provided by the semicolon after the invocation of INIT_METHODS.
The generalization of the setfuncN macros to more elements is straight-forward (though you'll need to modify VA_NARGS and VA_NARGS_IMPL to handle more arguments too).
The lines defining ptr1 etc were fixed to:
Define pointers instead of structures.
Use sizeof() correctly.
Also, all function pointers and definitions have strict prototypes. When you declare something like void (*method1)(); in C, you are defining a pointer to a function that returns void but takes an indeterminate but not variadic argument list. (In C++, it would be a pointer to a function that takes no arguments, but this is C, not C++.) The 'not variadic' bit means that the function prototype would not contain ellipsis .... All functions that take a variadic argument list must have a full prototype in scope when used.
Output
$gcc -std=c99 -E vma2.c
# 1 "vma2.c"
# 1 "<command-line>"
# 1 "vma2.c"
# 13 "vma2.c"
typedef struct{
void (*method1)(void);
}data1_t;
typedef struct{
void (*method1)(void);
void (*method2)(void);
}data2_t;
typedef struct{
void (*method1)(void);
void (*method2)(void);
void (*method3)(void);
void (*method4)(void);
}data4_t;
void function1(void){}
void function2(void){}
data1_t *ptr1 = calloc(sizeof(data1_t), 1));
data2_t *ptr2 = calloc(sizeof(data2_t), 1));
data2_t *ptr4 = calloc(sizeof(data4_t), 1));
ptr1->method1 = function1;
ptr2->method1 = function1; ptr2->method2 = function2;
ptr4->method1 = function1; ptr4->method2 = function2; ptr4->method3 = function3; ptr4->method4 = function4;
$
This looks like what I think you wanted.
Note that the code passes the preprocessor; it won't pass the compiler proper as written because:
function3 and function4 are undeclared.
Assignments like the calloc calls must be in the body of a function.
The assignments that initialize the structures need to be in the body of a function too.
Related
The Issue
Well, I have this really interesting macro set
#define __macro_3_opt_args(_0, _1, _2, _3, name, ...) name
#define _macro_3_opt_args_(arg0, arg1, arg2, arg3, ...) __macro_3_opt_args(, ##__VA_ARGS__, arg3, arg2, arg1, arg0)(__VA_ARGS__)
#define __padding_id(size, id) uint8_t _padding_##id##_ [size]
#define ____padding_line(size, line) __padding_id(size, ln##line) //adding `ln` prefix
#define ___padding_line(size, line) ____padding_line(size, line) //expanding `__LINE__` macro first
#define __padding_line(size) ___padding_line(size, __LINE__)
#define _padding_(...) _macro_2_opt_args_(, __padding_line, __padding_id, __VA_ARGS__)
#define offset_struct union
#define _offset_zero_member(type, name) type name
#define _offset_member(type, name, offset) struct { __padding_id(offset, to_##name); type name; }
#define offset_member(type, name, ...) _macro_3_opt_args_(, , _offset_zero_member, _offset_member, type, name, ##__VA_ARGS__)
But if I try to use it (like this:)
offset_struct Test {
offset_member (int, header);
offset_member (void*, data, 64);
} test;
//Let's say this two code snippets are in a same file
VS highlights the offset_member macro "calls" as errors (I use Microsoft's C/C++ Extension).
When I lookup VS Code's macro expansion
it shows struct { uint8_t _padding_to__ []; void*, data,64 ; }
instead of struct { uint8_t _padding_to_data_ [64]; void* data; } (what GCC sees)
The Question
How do I make VS ignore the macro expansion?
Can I use some magic #pragma for that?
Or is there a config that does it?
Suppose we've got the following two functions:
void foo1(int p);
void foo2(int p, ...);
I'd like to write a macro to automatically expand to the proper one based on the number of arguments. I've used the following dirty/hacky way, but I'm curious whether there's a clean solution for this problem or not.
#define SELECT(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, NAME, ...) NAME
#define foo(...) SELECT(__VA_ARGS__, \
foo2, foo2, foo2, foo2, \
foo2, foo2, foo2, foo2, \
foo2, foo2, foo2, foo1)(__VA_ARGS__)
This way only works if foo2's number of arguments doesn't exceed 12. This is a drawback of my solution. I'm looking for a way without this limitation.
Update #1
The real problem: In Android NDK using the following functions we can write a log:
__android_log_print(int prio, const char *tag, const char *fmt, ...);
__android_log_write(int prio, const char *tag, const char *text);
To simplify the functions names, I define a macro called LOG:
#define LOG(...) __android_log_print(0, "test", __VA_ARGS__)
If I pass the macro a string literal, it's okay, but when I pass a variable, compiler generates the warning -Wformat-security. So, I'd like the macro calls with single argument to expand to __android_log_write and others to __android_log_print. My use cases for log: 1. string literal with/without arguments 2. single argument variable char *.
If your compiler supports it, __VA_OPT__ from C++20 makes this more or less simple:
#define LOG(...) LOG1(__VA_ARGS__,)(__VA_ARGS__)
#define LOG1(x, ...) LOG2##__VA_OPT__(a)
#define LOG2(x) std::cout << "n = 1: " STR(x) "\n";
#define LOG2a(x, ...) std::cout << "n > 1: " STR(x, __VA_ARGS__) "\n";
#define STR(...) STR_(__VA_ARGS__)
#define STR_(...) #__VA_ARGS__
int main()
{
LOG(1) // Prints: `n = 1: 1`
LOG(1, 2) // Prints: `n > 1: 1, 2,`
LOG(1, 2, 3) // Prints: `n > 1: 1, 2, 3,`
}
As per discussion in comments, C pre-processor is the not the ideal choice. It will not be able to add polymorphism.
As an alternative, consider leveraging the m4 macro engine, which has more power. It might produce some fun constructs. I would not usually recommend it for production (over C++ or Java). Good for Proof of concept projects, or prototyping.
More about GNU m4: https://www.gnu.org/software/m4/manual/
Consider x.m4,w hich will expand arbitrary call to foo with N arguments to foo(arguments).
define(`foo', `foo$#($*)')
void foo1(int v1) { }
void foo2(int v1, int v2) { }
void foo3(int v1, int v2, int v3) {}
void main(void)
{
foo(a) ;
foo(a, b) ;
foo(a, b, c) ;
}
Expand with 'm4 x.m4`
void foo1(int v1) { }
void foo2(int v1, int v2) { }
void foo3(int v1, int v2, int v3) {}
void main(void)
{
foo1(a) ;
foo2(a,b) ;
foo3(a,b,c) ;
}
When used in Makefile, you will usually append a .m4 suffix, you can build a rule similar to below to automatic build the intermediate '.c' file, which can be compiled using the default CC rule.
%.c: %.m4
m4 <$^ -o $#
I have many macros that end up generating code. For example:
#define CODE_GEN_IDENT1_HDR(PARAM1, PARAM2, PARAM3) \
// code generated
#define CODE_GEN_IDENT2_HDR(PARAM1, PARAM2, PARAM3) \
// code generated
#define CODE_GEN_IDENT1_SRC(PARAM1, PARAM2, PARAM3) \
// code generated
#define CODE_GEN_IDENT2_SRC(PARAM1, PARAM2, PARAM3) \
// code generated
The idea is that HDR generates functions definitions and SRC generates their implementation. All macros have the same amount of arguments (in this example, 3). IDENT can be any name like MATH, TRIG, ALGS, CONTAINERS, etc. This is what I want to focus on.
I'm trying to build up a macro that can generate all of these macros macros with different IDENT using variadic macros. For example:
// Concatenate macros to a standard form
#define CONCATH_(C) CODE_GEN_##C##_HDR
#define CONCATC_(C) CODE_GEN_##C##_SRC
// CONCATH concatenates to HDR
// CONCATC concatenates to SRC
#define CONCATH(C) CONCATH_(C)
#define CONCATC(C) CONCATC_(C)
#define MASTER_MACRO(PARAM1, PARAM2, PARAM3, ...) \
// Code that generates all other macros
// using CONCATH and CONCATC
// how could this be done?
When I write:
MASTER_MACRO(int, "Hello", char *, MATH, TRIG, CONT)
I would like to have something like:
CODE_GEN_MATH_HDR(int, "Hello", char *)
CODE_GEN_TRIG_HDR(int, "Hello", char *)
CODE_GEN_CONT_HDR(int, "Hello", char *)
CODE_GEN_MATH_SRC(int, "Hello", char *)
CODE_GEN_TRIG_SRC(int, "Hello", char *)
CODE_GEN_CONT_SRC(int, "Hello", char *)
To somehow access the given arguments and concatenate each, making both header and source.
What I currently have is a fixed length macro like:
MASTER_MACRO(PARAM1, PARAM2, PARAM3, MATH, TRIG, CONT, DUPL, SORT) \
CONCATH(MATH)(PARAM1, PARAM2, PARAM3)
CONCATH(TRIG)(PARAM1, PARAM2, PARAM3)
...
CONCATC(MATH)(PARAM1, PARAM2, PARAM3)
...
And when the user doesn't want to generate CONT, DUPL or any other macro he's got to pass in a pre-defined argumet like _ meaning he is not wanting to generate that and having an empty macro:
#define CODE_GEN___SRC(PARAM1, PARAM2, PARAM3) // Empty
But this is not good enough. On different projects these different IDENT have different names, so having to make new master macros are a bit annoying.
But the big questions are:
Can this be done?
Is there a simple way to do this?
Can it be done using only standard C (no compiler-dependent macro)?
Yes, you can do this. For a hand rolled implementation, you probably want to start with a basic argument counter like this:
#define COUNT(...) \
COUNT_I(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1,)
#define COUNT_I(_ ,_9,_8,_7,_6,_5,_4,_3,_2, X,...) X
The argument counter works kind of like a "shift register", injecting the argument list before the count. If called with one argument, everything aligns X with 1. Each additional argument shifts this list over... 2 arguments shifts 2 into X, 3 shifts 3 in, and so on. This is just the basic form, supporting up to 9 arguments, to convey the idea.
...now you can generate variadic macro utilities like this one:
#define GLUE(A,B) GLUE_I(A,B)
#define GLUE_I(A,B) A##B
#define TRANSFORM_CD(MACRO, ...) GLUE(TRANSFORM_CD_,COUNT(__VA_ARGS__))(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_1(MACRO,X) MACRO(X)
#define TRANSFORM_CD_2(MACRO,X,...) MACRO(X),TRANSFORM_CD_1(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_3(MACRO,X,...) MACRO(X),TRANSFORM_CD_2(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_4(MACRO,X,...) MACRO(X),TRANSFORM_CD_3(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_5(MACRO,X,...) MACRO(X),TRANSFORM_CD_4(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_6(MACRO,X,...) MACRO(X),TRANSFORM_CD_5(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_7(MACRO,X,...) MACRO(X),TRANSFORM_CD_6(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_8(MACRO,X,...) MACRO(X),TRANSFORM_CD_7(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_9(MACRO,X,...) MACRO(X),TRANSFORM_CD_8(MACRO,__VA_ARGS__)
Conceptually TRANSFORM_CD is meant to "transform" a Comma Delimited list (args 2 and up) to another comma delimited list by applying a macro to it. In this case, we start with a comma delimited list of base names (what you call IDENT here) and apply one of your transform macros to it; for example, TRANSFORM(CONCATH, TRIG, CONT, DUPL) expands to CODE_GEN_TRIG_HDR, CODE_GEN_CONT_HDR, CODE_GEN_DUPL_HDR.
Next we need something to generate calls with multiple macros and the same parameter set; like this utility:
#define INVOKE_ALL(TUPLE_, ...) GLUE(INVOKE_ALL_,COUNT(__VA_ARGS__))(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_1(TUPLE_, X) X TUPLE_
#define INVOKE_ALL_2(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_1(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_3(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_2(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_4(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_3(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_5(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_4(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_6(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_5(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_7(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_6(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_8(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_7(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_9(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_8(TUPLE_,__VA_ARGS__)
Here, TUPLE_ is meant to be a parenthesized argument list (this makes the utility a bit more generic than requiring it support exactly three parameters); and each other parameter represents a macro to be called with those arguments.
Combining the two, this:
INVOKE_ALL((p1 a1, p2 a2, p3 a3),TRANSFORM_CD(CONCATH,MATH,TRIG,CONT,DUPL))
...should expand to (reformatted for clarity):
CODE_GEN_TRIG_HDR(p1 a1, p2 a2, p3 a3)
CODE_GEN_CONT_HDR(p1 a1, p2 a2, p3 a3)
CODE_GEN_DUPL_HDR(p1 a1, p2 a2, p3 a3)
...and if you really want, you could preserve MASTER_MACRO's form and function, simply making it variadic, like this:
#define MASTER_MACRO(PARAM1, PARAM2, PARAM3, ...) \
INVOKE_ALL((PARAM1, PARAM2, PARAM3),TRANSFORM_CD(CONCATH,__VA_ARGS__)) \
INVOKE_ALL((PARAM1, PARAM2, PARAM3),TRANSFORM_CD(CONCATC,__VA_ARGS__))
demo at stacked-crooked
background
I'm trying to make automatic generator of Lua-C interface using C macros. The biggest problem was to make it general for varying number of arguments, which I resolved by using __VA_ARGS__ with help of this answer: Is it possible to iterate over arguments in variadic macros?
Simpler almost working solution
this solution almost works, but it produce some redudant commas (nottice ,,,, in output)
// helper macros for iteration over __VA_ARGS__
#define ARG1(WHAT,X,...) WHAT(X)ARG2(WHAT,__VA_ARGS__)
#define ARG2(WHAT,X,...) WHAT(X)ARG3(WHAT,__VA_ARGS__)
#define ARG3(WHAT,X,...) WHAT(X)ARG4(WHAT,__VA_ARGS__)
#define ARG4(WHAT,X,...) WHAT(X)ARG5(WHAT,__VA_ARGS__)
#define ARG5(WHAT,X,...) WHAT(X)ARG6(WHAT,__VA_ARGS__)
#define ARG6(WHAT,X,...) WHAT(X)//ARG2(__VA_ARGS__)
// macros dispatch propper type of Lua::get
#define LUA_GET_int(i) Lua::getInt(L,i)
#define LUA_GET_long(i) Lua::getInt(L,i)
#define LUA_GET_float(i) (float)Lua::getDouble(L,i)
#define LUA_GET_double(i) Lua::getDouble(L,i)
#define LUA_GET_string(i) Lua::getString(L,i)
#define LUA_PUSH_int(a) lua_pushnumber(L,a)
#define LUA_PUSH_float(a) lua_pushnumber(L,a)
#define LUA_PUSH_double(a) lua_pushnumber(L,a)
#define LUA_PUSH_float(a) lua_pushstring(L,a)
#define LUA_GET_(T)
#define LUA_GET(T) ,LUA_GET_##T(i++) // commas come from here
#define MAKE_LUA_FUNC(TR,fname,T1,...) int l_##fname(lua_State * L){ int i=0; LUA_PUSH_##TR( fname( LUA_GET_##T1(i++) ARG1(LUA_GET,__VA_ARGS__) ) ); return 1; }
// interface for function:
// double add3(float, int, double );
MAKE_LUA_FUNC( double, add3, float, int, double )
// output:
// 'int l_add3(lua_State * L){ int i=0; lua_pushnumber(L,add3((float)Lua::getDouble(L,i++) ,Lua::getInt(L,i++),Lua::getDouble(L,i++),,,, )); return 1; }'
Working but less-nice solution
I had to duplicate LUA_GET_ macros for case when it is first in argument list (without comma) and otherwise (with comma in front)
// begin of argument list => no commas
#define LUA_GET_int(i) Lua::getInt(L,i)
#define LUA_GET_long(i) Lua::getInt(L,i)
#define LUA_GET_float(i) (float)Lua::getDouble(L,i)
#define LUA_GET_double(i) Lua::getDouble(L,i)
#define LUA_GET_string(i) Lua::getString(L,i)
// rest of argument list => with commas
#define LUA_GET__int(i) ,Lua::getInt(L,i)
#define LUA_GET__long(i) ,Lua::getInt(L,i)
#define LUA_GET__float(i) ,(float)Lua::getDouble(L,i)
#define LUA_GET__double(i) ,Lua::getDouble(L,i)
#define LUA_GET__string(i) ,Lua::getString(L,i)
#define LUA_PUSH_int(a) lua_pushnumber(L,a)
#define LUA_PUSH_float(a) lua_pushnumber(L,a)
#define LUA_PUSH_double(a) lua_pushnumber(L,a)
#define LUA_PUSH_float(a) lua_pushstring(L,a)
#define LUA_GET_(T)
#define LUA_GET__(T)
#define LUA_GET(T) LUA_GET__##T(i++)
#define MAKE_LUA_FUNC(TR,fname,T1,...) int l_##fname(lua_State * L){ int i=0; LUA_PUSH_##TR( fname( LUA_GET_##T1(i++) ARG1(LUA_GET,__VA_ARGS__) ) ); return 1; }
// MAKE_LUA_FUNC( double, add3, float, int, double )
// output:
// int l_add3(lua_State * L){ int i=0; lua_pushnumber(L,add3( (float)Lua::getDouble(L,i++) ,Lua::getInt(L,i++),Lua::getDouble(L,i++) )); return 1; }
Is it possible to make it simpler / nicer ?
NOTE - for debugging I found very useful this Seeing expanded C macros in particular https://stackoverflow.com/a/31460434/1291544
You need to count the number of arguments you have, and then call the corresponding ARG# macro.
#define ARGS_N(M,...) \
ARGS_N__(__VA_ARGS__, 6, 5, 4, 3, 2, 1)(M, __VA_ARGS__)
#define ARGS_N__(_1, _2, _3, _4, _5, _6, X, ...) ARGS_##X
#define ARGS_1(M, X) M(X)
#define ARGS_2(M, X, ...) M(X)ARGS_1(M, __VA_ARGS__)
#define ARGS_3(M, X, ...) M(X)ARGS_2(M, __VA_ARGS__)
#define ARGS_4(M, X, ...) M(X)ARGS_3(M, __VA_ARGS__)
#define ARGS_5(M, X, ...) M(X)ARGS_4(M, __VA_ARGS__)
#define ARGS_6(M, X, ...) M(X)ARGS_5(M, __VA_ARGS__)
Now, change MAKE_LUA_FUNC to call ARGS_N instead of your ARG1.
The way the counting technique works is that ARGS_N invokes the helper ARGS_N__ with the variable arguments, and then pads out the invocation with additional arguments. ARGS_N__ does the counting by always utilizing the 7th argument. So, if ARGS_N is provided 4 variable arguments after the first one, ARGS_N__ will produce ARGS_4, because in that case, in the padding provided by ARGS_N, 4 would be the 7th argument.
ARGS_N__(__VA_ARGS__, 6, 5, 4, 3, 2, 1)(M, __VA_ARGS__)
. .
/|\ /|\
| |
If this has 4 arguments
|
This would be the 7th argument
This is the same technique that was shown in the answer you pointed to. However, that version was a bit more complicated than the version I am illustrating for you, so hopefully you will find this explanation helpful.
Imagine, I have a debug source file,
which is like this:
#if _OWN_DEBUG_LEVEL != 0
void DebugLogMsg (DebugStruct_t *DebugStruct, size_t sizeID, char const *szFormat, ...);
#else
#define DebugLogMsg(_Expression1, _Expression2, _Expression3) ((void)0)
#endif
In this case I do not really care about the additional arguments to the function, but what about this case?
#if _OWN_DEBUG_LEVEL > 0
#undef DebugLogMsg1
#define DebugLogMsg1(_Expression1, _Expression2, _Expression3) \
DebugLogMsg(_Expression1, _Expression2, _Expression3)
#endif
In this case I'm not quite sure... when I call the macro like this:
DebugLogMsg1(pointer, var, pointer, 1, 2, 3);
will _Expression3 be treat as would it be pointer, 1, 2, 3 or what would be the exact behaviour?
It just wouldn't work. You should use variadic macros:
#define DebugLogMsg1(a, b, c, ...) DebugLogMsg(a, b, c, __VA_ARGS__)
Or perhaps better (since it doesn't cause issues with trailing commas):
#define DebugLogMsg1(...) DebugLogMsg(__VA_ARGS__)