Variadic 宏包装器,扩展为使用与参数数量对应的字符格式化字符串
Variadic macro wrapper that expands to format string with characters corresponding to number of arguments
问题
我正在寻找一个可变参数 C 预处理器宏,它将其参数和相应的格式字符串传递给函数,并根据参数的数量重复一个字符。
例如,我想要一个扩展如下的宏 FOO
(或等效的 C 代码):
FOO(1)
→bar("d",1)
FOO(1,2)
→ bar("dd",1,2)
,
FOO(1,2,3)
→bar("ddd",1,2,3)
- 奖金:
FOO()
→bar("")
虽然我可以结合 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 的解决方案,但这些解决方案有几个缺点,例如:
- 需要特殊的库,例如 Boost(这对我来说是个问题),
- 依赖于编译器,
- 只在运行时工作,
- 极其复杂。
我希望在不分开看待这些问题的情况下出现一些更好的解决方案。
背景
我想在自动生成的代码中回调 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);
}
问题
我正在寻找一个可变参数 C 预处理器宏,它将其参数和相应的格式字符串传递给函数,并根据参数的数量重复一个字符。
例如,我想要一个扩展如下的宏 FOO
(或等效的 C 代码):
FOO(1)
→bar("d",1)
FOO(1,2)
→bar("dd",1,2)
,FOO(1,2,3)
→bar("ddd",1,2,3)
- 奖金:
FOO()
→bar("")
虽然我可以结合 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 的解决方案,但这些解决方案有几个缺点,例如:
- 需要特殊的库,例如 Boost(这对我来说是个问题),
- 依赖于编译器,
- 只在运行时工作,
- 极其复杂。
我希望在不分开看待这些问题的情况下出现一些更好的解决方案。
背景
我想在自动生成的代码中回调 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);
}