Variadic 宏包装器,扩展为使用与参数数量对应的字符格式化字符串

Variadic macro wrapper that expands to format string with characters corresponding to number of arguments

问题

我正在寻找一个可变参数 C 预处理器宏,它将其参数和相应的格式字符串传递给函数,并根据参数的数量重复一个字符。 例如,我想要一个扩展如下的宏 FOO(或等效的 C 代码):

虽然我可以结合 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 的解决方案,但这些解决方案有几个缺点,例如:

我希望在不分开看待这些问题的情况下出现一些更好的解决方案。

背景

我想在自动生成的代码中回调 Python 的 C 扩展中的 Python 函数。 因此,例如,我需要 foo(1,2,3) 扩展为:

PyObject_CallObject( callback_foo, Py_Build_Value("(Oddd)",Y,1,2,3) )

我知道 foo 的所有参数都是双精度数,但我不知道它们的个数。 (上面的示例有些简化。我知道它缺少一些 Py_DECREF。)

到目前为止我能想到的最好的方法是采用 this answer 并简化它:

# 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 )

这适用于 GCC 和 Clang(使用 -std=c++2a)和最多十个参数(但可以扩展)。

最大的兼容性问题是 __VA_OPT__(,) 的两个实例,它们仅在处理零参数情况时需要。 否则,可以将它们替换为简单的 ,.

只有 2 个宏:

#define GET_MACRO(_0,_1,_2,_3,_4,NAME,...) NAME
#define FOO(...) bar(GET_MACRO(0,##__VA_ARGS__,"dddd","ddd","dd","d",""), ##__VA_ARGS__)

使用 100% 标准 C,你可以这样做:

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

在此示例中,STRTABLE 是一个复合文字查找 table,其中包含一堆字符串文字作为初始值设定项列表。通过计算宏参数的数量并专门使用该数组索引,仅使用与传递给宏的参数数量相对应的初始化器。

完整示例:

#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);
}