Reputation: 4434
I am looking for a variadic C preprocessor macro that passes its argument and a corresponding format string to a function, repeating a character depending on the number of arguments.
For example, I would like a macro FOO
which expands as follows (or to equivalent C code):
FOO(1)
→ bar("d",1)
FOO(1,2)
→ bar("dd",1,2)
,FOO(1,2,3)
→ bar("ddd",1,2,3)
FOO()
→ bar("")
While I can combine the solutions to C preprocessor macro for returning a string repeated a certain number of times and C++ preprocessor __VA_ARGS__ number of arguments (or similar questions) or use variadic macros, these have several drawbacks such as:
My hope is that some better solutions emerge when these problems are not regarded separately.
I want to callback Python functions in a C extension of Python in automatically generated code.
So, for example, I need foo(1,2,3)
to expand to:
PyObject_CallObject( callback_foo, Py_Build_Value("(Oddd)",Y,1,2,3) )
I know that all arguments of foo
are doubles, but I do not know their number.
(The above example is somewhat simplified. I am aware that it is missing a few Py_DECREF
s.)
Upvotes: 2
Views: 744
Reputation: 214300
With 100% standard C, you could do this:
#define COUNT_ARGS(...) (sizeof((int[]){__VA_ARGS__}) / sizeof(int))
#define STRTABLE (const char*[]){ "", "d", "dd", "ddd", "ddddd" } // and so on
#define FOO(...) bar(STRTABLE[COUNT_ARGS(__VA_ARGS__)], __VA_ARGS__)
In this example, STRTABLE
is a compound literal look-up table with a bunch of string literals as an initializer list. Only the initializer corresponding to the number of arguments passed to the macro is used, by counting the number of macro arguments and using that array index specifically.
Full example:
#include <stdio.h>
#define COUNT_ARGS(...) (sizeof((int[]){__VA_ARGS__}) / sizeof(int))
#define STRTABLE (const char*[]){ "", "d", "dd", "ddd", "ddddd" } // and so on
#define FOO(...) bar(STRTABLE[COUNT_ARGS(__VA_ARGS__)], __VA_ARGS__)
void bar(const char* fmt, ...)
{
puts(fmt);
}
int main (void)
{
FOO(1);
FOO(1,2);
FOO(1,2,3);
}
Upvotes: 3
Reputation: 141493
Just 2 macros:
#define GET_MACRO(_0,_1,_2,_3,_4,NAME,...) NAME
#define FOO(...) bar(GET_MACRO(0,##__VA_ARGS__,"dddd","ddd","dd","d",""), ##__VA_ARGS__)
Upvotes: -1
Reputation: 4434
The best I could come up with so far is to take this answer and simplify it:
# define EXPAND(x) x
# define FORMATSTRING(...) EXPAND(ELEVENTHARG1(__VA_ARGS__ __VA_OPT__(,) RSEQ()))
# define ELEVENTHARG1(...) EXPAND(ELEVENTHARG2(__VA_ARGS__))
# define ELEVENTHARG2(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,N,...) N
# define RSEQ() "dddddddddd","ddddddddd","dddddddd", \
"ddddddd","dddddd","ddddd","dddd","ddd","dd","d",""
# define FOO(...) bar( FORMATSTRING(__VA_ARGS__) __VA_OPT__(,) __VA_ARGS__ )
FOO() // expands to: bar( "" )
FOO(1) // expands to: bar( "d" , 1 )
FOO(1,2,3) // expands to: bar( "ddd" , 1,2,3 )
This works with GCC and Clang (with -std=c++2a
) and up to ten arguments (but can be expanded).
The biggest compatibility issue are the two instances of __VA_OPT__(,)
, which are only required for handling the zero-argument case.
Otherwise, they can be replaced by a simple ,
.
Upvotes: 0