便携式可变参数宏

Posted

技术标签:

【中文标题】便携式可变参数宏【英文标题】:Portable variadic macro 【发布时间】:2016-07-04 12:34:42 【问题描述】:

我正在寻找一个可变参数宏,它只为每个参数(例如,最多 6 个)调用某个函数。到目前为止,我一直在 MSVC 中使用以下代码:

#define do_write2(x,y) dodo_write(x);do_write(y);while(0)
#define do_write3(x,y,z) dodo_write(x);do_write(y);do_write(z);while(0)
#define do_write4(x,y,z,w) dodo_write(x);do_write(y);do_write(z);do_write(w);while(0)
#define do_write5(x,y,z,w,t) dodo_write(x);do_write(y);do_write(z);do_write(w);do_write(t);while(0)
#define do_write6(x,y,z,w,t,u) dodo_write(x);do_write(y);do_write(z);do_write(w);do_write(t);do_write(u);while(0)
#define expand(x) x
#define _get_write(_1,_2,_3,_4,_5,_6,name,...) name
#define dumpval(...) expand(_get_write(__VA_ARGS__,do_write6,do_write5,do_write4,do_write3,do_write2,do_write))expand((__VA_ARGS__))

由于在 MSVC 中对 __VA_ARGS__ 的特殊处理,需要 expand,否则我会得到

error C2660: 'do_write' : function does not take 6 arguments

但是,现在我需要在 GCC 中构建相同的代码,并且遇到了问题:

error: ‘do_write3’ was not declared in this scope

只需删除 expand 包装器就可以了。但是,是否有任何“正确”的方法可以在不使用#ifdef 的情况下在这两种情况下编译代码?

【问题讨论】:

必须使用宏来完成吗?可以用C++11吗? 我想我可以重写它以使用可变参数模板,代码很旧所以我从来没有考虑过这种可能性。 【参考方案1】:

变量参数宏有多种技术,在此讨论:Variadic recursive preprocessor macros - is it possible?

这里有一些您可能感兴趣的实现。就个人而言,我认为call_vla2 是最好的,前提是您支持 C++11。如果这不可行,请告诉我们更多有关您的问题的信息。

#include <iostream>

// target function
void z(int i) 
    std::cout << "[" << i << "]\n";


// 1. manually specifying the argument count
#define call1(a)        doz(a);while(0)
#define call2(a,b)      doz(a);z(b);while(0)
#define call3(a,b,c)    doz(a);z(b);z(c);while(0)
#define call_n(n, ...)  call ## n (__VA_ARGS__)

// 2. using a variable-length array (GCC compatible)
// thanks to https://***.com/a/824769/6096046
#define call_vla(...) do  \
        int args[] =  __VA_ARGS__ ; \
        for(size_t i = 0; i < sizeof(args)/sizeof(*args); i ++) \
            z(args[i]); \
     while(0)

// 3. using a variable-length array and C++11
#define call_vla2(...) for(auto x :  __VA_ARGS__ ) z(x);

// 4. using C++11 variadic templates
template <typename... T>
void call_n_times() 

template <typename... T>
void call_n_times(int a, T... other) 
    z(a), call_n_times(other...);


#define call_template(...) call_n_times(__VA_ARGS__)

// tests
int main() 
    call_n(1, 88);      call_n(3, 1,2,3);
    call_vla(88);       call_vla(1,2,3);
    call_vla2(88);      call_vla2(1,2,3);
    call_template(88);  call_template(1,2,3);
    return 0;

【讨论】:

以上是关于便携式可变参数宏的主要内容,如果未能解决你的问题,请参考以下文章

可变参数宏(DEBUG)

gnu printf可变参数宏

是否可以迭代可变参数宏中的参数?

在指向另一个宏的可变参数宏中为每个参数添加前缀

为啥我的可变参数宏不能正确接受任何参数?

C语言宏定义实现可变参数