根据 __VA_ARGS__ 的长度扩展为不同的值
Expanding to different values based on length of __VA_ARGS__
假设我们有以下两个函数:
void foo1(int p);
void foo2(int p, ...);
我想写一个宏来根据参数的数量自动扩展到合适的。我使用了以下 dirty/hacky 方法,但我很好奇是否有针对此问题的干净解决方案。
#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__)
这种方式只有在foo2
的参数数量不超过12时才有效。这是我的解决方案的一个缺点。我正在寻找一种没有这种限制的方法。
更新#1
真正的问题:在AndroidNDK中使用下面的函数我们可以写一个日志:
__android_log_print(int prio, const char *tag, const char *fmt, ...);
__android_log_write(int prio, const char *tag, const char *text);
为了简化函数名称,我定义了一个名为 LOG
:
的宏
#define LOG(...) __android_log_print(0, "test", __VA_ARGS__)
如果我向宏传递一个字符串文字,没问题,但是当我传递一个变量时,编译器会生成警告 -Wformat-security
。因此,我希望使用单个参数的宏调用扩展为 __android_log_write
,将其他扩展为 __android_log_print
。我的日志用例:1. 字符串文字 with/without 参数 2. 单个参数变量 char *
.
根据评论中的讨论,C 预处理器不是理想的选择。它将无法添加多态性。
作为替代方案,请考虑利用功能更强大的 m4 宏引擎。它可能会产生一些有趣的结构。我通常不会推荐它用于生产(通过 C++ 或 Java)。适用于概念验证项目或原型制作。
关于 GNU 的更多信息 m4
:https://www.gnu.org/software/m4/manual/
考虑 x.m4,它将对带有 N 个参数的 foo 的任意调用扩展到 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) ;
}
用'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) ;
}
在Makefile中使用时,一般会追加一个.m4后缀,您可以建立类似下面的规则来自动建立中间'.c'文件,使用默认的CC规则编译即可。
%.c: %.m4
m4 <$^ -o $@
如果您的编译器支持它,C++20 中的 __VA_OPT__
或多或少会使这变得简单:
#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,`
}
假设我们有以下两个函数:
void foo1(int p);
void foo2(int p, ...);
我想写一个宏来根据参数的数量自动扩展到合适的。我使用了以下 dirty/hacky 方法,但我很好奇是否有针对此问题的干净解决方案。
#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__)
这种方式只有在foo2
的参数数量不超过12时才有效。这是我的解决方案的一个缺点。我正在寻找一种没有这种限制的方法。
更新#1
真正的问题:在AndroidNDK中使用下面的函数我们可以写一个日志:
__android_log_print(int prio, const char *tag, const char *fmt, ...);
__android_log_write(int prio, const char *tag, const char *text);
为了简化函数名称,我定义了一个名为 LOG
:
#define LOG(...) __android_log_print(0, "test", __VA_ARGS__)
如果我向宏传递一个字符串文字,没问题,但是当我传递一个变量时,编译器会生成警告 -Wformat-security
。因此,我希望使用单个参数的宏调用扩展为 __android_log_write
,将其他扩展为 __android_log_print
。我的日志用例:1. 字符串文字 with/without 参数 2. 单个参数变量 char *
.
根据评论中的讨论,C 预处理器不是理想的选择。它将无法添加多态性。
作为替代方案,请考虑利用功能更强大的 m4 宏引擎。它可能会产生一些有趣的结构。我通常不会推荐它用于生产(通过 C++ 或 Java)。适用于概念验证项目或原型制作。
关于 GNU 的更多信息 m4
:https://www.gnu.org/software/m4/manual/
考虑 x.m4,它将对带有 N 个参数的 foo 的任意调用扩展到 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) ;
}
用'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) ;
}
在Makefile中使用时,一般会追加一个.m4后缀,您可以建立类似下面的规则来自动建立中间'.c'文件,使用默认的CC规则编译即可。
%.c: %.m4
m4 <$^ -o $@
如果您的编译器支持它,C++20 中的 __VA_OPT__
或多或少会使这变得简单:
#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,`
}