【问题标题】:Expanding to different values based on length of __VA_ARGS__根据 __VA_ARGS__ 的长度扩展为不同的值
【发布时间】:2019-11-24 15:18:06
【问题描述】:

假设我们有以下两个函数:

void foo1(int p);
void foo2(int p, ...);

我想编写一个宏来根据参数的数量自动扩展为正确的宏。我使用了以下肮脏/骇人的方式,但我很好奇是否有针对此问题的干净解决方案。

#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

真正的问题:在Android NDK中使用以下函数我们可以写日志:

__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. 带/不带参数的字符串文字 2. 单参数变量char *

【问题讨论】:

  • 为什么不使用支持开箱即用的多态性的 c++ 呢? C 从未为这些情况而设计。
  • 另一个问题:您要解决什么问题 - 宏将如何使用?具有 12 个(或更多)参数的函数将难以使用。是否所有参数都属于同一类型 - 您正在尝试传递 'list of int' ?
  • @dash-o 是的,我知道。我没有使用超过 12 个参数的函数。我只是好奇有更好的方法来解决这个问题,或者不使用 C 而不是 C++。我的整个项目都是用 C 编写的,我不会用 C++ 来解决这个简单的问题。
  • 这两个函数分别在做什么?可能有一种方法可以将它们结合起来并避免任何宏诡计。
  • @dbush 我已经为我的实际问题添加了解释。

标签: c variadic-macros


【解决方案1】:

如果你的编译器支持它,来自 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,`
}

【讨论】:

    【解决方案2】:

    根据 cmets 中的讨论,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 $@
    

    【讨论】:

      猜你喜欢
      • 2012-08-01
      • 2015-11-30
      • 1970-01-01
      • 2011-07-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-19
      相关资源
      最近更新 更多