根据 __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 的更多信息 m4https://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,`
}