可变参数宏扩张(Variadic macro expanding)

2019-10-19 00:28发布

我想知道,有没有什么办法来选择性地调用C可变参数宏。

首先,让我告诉一些代码,我想实现:

#include <stdio.h>

#define _VA_NARGS_IMPL(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define _VA_NARGS(...) _VA_NARGS_IMPL(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1) 
#define binder(count, ...) arg##count(__VA_ARGS__)
#define foo(...) binder(_VA_NARGS(__VA_ARGS__), __VA_ARGS__)
#define arg1(_1) _1
#define arg2(_1, _2) _1, _2
#define arg3(_1, _2, _3) _1, _2, _3

int main()
{
    printf("%d %d %d", foo(11,22,33));
    return 0;
}

我测试了它在VC11,GCC4.8和3.4锵但没有人可以编译它,因为我想要的。

是的,我想打电话给由它的参数计数宏,但宏扩展为:

foo(...)
binder(count, ...)
arg_VA_NAGS(...)

是不是有什么诀窍?


编辑:

我更详细地写我真正想要的。

我发现从一些答案的线索和编辑我的代码。

typedef unsigned short ListHeader;

template<typename T>
inline const size_t GetSize(const T& _obj) {return sizeof(T);}

inline const size_t GetSize(const std::string& _str) {return sizeof(ListHeader) + _str.size() + 1;}

inline const size_t GetSize(const std::vector<std::string>& _vec)
{
    size_t total = 0;

    for (auto item : _vec)
    {
        total += GetSize(item);
    }

    return sizeof(ListHeader) + total;
}

template<typename T>
inline const size_t GetSize(const std::vector<T>& _vec)
{
    size_t total = 0;

    for (auto item : _vec)
    {
        total += GetSize<decltype(item)>(item);
    }

    return sizeof(ListHeader) + total;
}

#define VA_NARGS_IMPL(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)


#define VARARG_IMPL2(base, count, ...) base##count(__VA_ARGS__)
#define VARARG_IMPL(base, count, ...) VARARG_IMPL2(base, count, __VA_ARGS__) 
#define VARARG(base, ...) VARARG_IMPL(base, VA_NARGS(__VA_ARGS__), __VA_ARGS__)



#define SerialSize(...) VARARG(SerialSize, __VA_ARGS__)



#define SerialSize1(_1) \
    const size_t size() {return GetSize(_1);}
#define SerialSize2(_1,_2) \
    const size_t size() {return GetSize(_1) + GetSize(_2);}
#define SerialSize3(_1,_2,_3) \
    const size_t size() {return GetSize(_1) + GetSize(_2) +  GetSize(_3);}
#define SerialSize4(_1,_2,_3,_4) // same implementation except count of arguments: 1..4
#define SerialSize5(_1,_2,_3,_4,_5) // 1...5
#define SerialSize6(_1,_2,_3,_4,_5,_6) //1...6
#define SerialSize7(_1,_2,_3,_4,_5,_6,_7) //1...7
#define SerialSize8(_1,_2,_3,_4,_5,_6,_7,_8) //1..8


// Please don't care about detailed implementation of my Archive class.
// It's not important now I guess..
class Archive
{
public:
    template<typename T>
    Archive& operator, (T& _val) //comma operator for Variadic macro
    {
        if (reading)
            read(&_val);
        else
            write(&_val);

        return *this;
    }

    Archive& out();
    Archive& in();

private:

    template<typename T>
    Archive& read(T&);
    template<typename T>
    Archive& write(T&);
};



class Serializable
{
public:
    Serializable(void) {}
    virtual ~Serializable(void) {}

    virtual const size_t size() = 0;
    virtual void serialize(Archive&) = 0;
    virtual void deserialize(Archive&) = 0;
};


#define SerialFormat(...) \
    SerialSize(__VA_ARGS__) \
    void serialize(Archive& ar)\
    {\
        ar.out() , ##__VA_ARGS__ ;\
    }\
    void deserialize(Archive& ar)\
    {\
        ar.in() , ##__VA_ARGS__ ;\
    }


//usage:
struct Packet_ReqeustLogin
    : public Serializable
{
    std::string name;
    std::string password;

    SerialFormat(name, password);
};

它在Xcode5和VC11测试,但它不会在VC11工作。

VC11的输出是这样的:

警告C4002:宏“SerialSize1”太多的实际参数

我能做些什么来解决这个问题?

Answer 1:

C预处理器是不是你正在试图做的(即使你克服这个问题)什么是正确的工具。

首先,可以肯定的是你不能使用C ++模板解决问题。

如果做不到这一点,它要求一个代码生成过多:一些东西,需要一些符号你的类的规范和生成的代码与所有的序列化的东西。

这里是另一回事。 你拼命哄宏成产生多个方面的总和:

GetSize(arg1) + GetSize(arg2) + ... + GetSize(argN)

但你俯瞰,你可以有N元函数做同样的事情:

GetSizes(arg1, arg2, ... , argN);

现在,宏不必产生了多个函数调用的术语+之间操作,但只有args来逗号分隔的列表!

您在原始程序也过于复杂的事情。 该printf该程序可以简单地实现:

$ gcc -std=c99 -Wall -pedantic test.c
$ ./a.out
1 2 3
$ cat test.c
#include <stdio.h>

#define foo(arg, ...) arg, ##__VA_ARGS__

int main()
{
  printf("%d %d %d\n", foo(1, 2, 3));
  return 0;
}


Answer 2:

你不能把宏调用的参数binder ,因为它使用了##直接操作。

binder(_VA_NARGS(__VA_ARGS__), __VA_ARGS__)
#define binder(count, ...) arg##count(__VA_ARGS__)
=> arg##_VA_NARGS(__VA_ARGS__)(__VA_ARGS__)
=> arg_VA_NARGS(__VA_ARGS__)(__VA_ARGS__)

宏替换的参数,使用中间宏。

#define binder_impl(count, ...) arg##count(__VA_ARGS__)
#define binder(...) binder_impl( __VA_ARGS__ )

我不能告诉你的最终目标是什么,但这个bug只是跳出了我。



Answer 3:

最大的诀窍是了解##以及如何__VA_ARGS__扩大。 下面是我使用的Linux系统调用的例子(编号ARGS -1的,由于第一ARG指系统调用号码...注意,因此以0结束,而不是大多数其他实施例)

#define MKFNS(fn,...) MKFN_N(fn,##__VA_ARGS__,9,8,7,6,5,4,3,2,1,0)(__VA_ARGS__)
#define MKFN_N(fn, n0, n1, n2, n3, n4, n5, n6, n7, n8, n9, n, ...) fn##n

#define syscall(...) MKFNS(syscall,##__VA_ARGS__)
#define syscall1(n,a) _syscall1(n,(long)(a))
#define syscall2(n,a,b) _syscall2(n,(long)(a),(long)(b))
#define syscall3(n,a,b,c) _syscall3(n,(long)(a),(long)(b),(long)(c))
#define syscall4(n,a,b,c,d) _syscall4(n,(long)(a),(long)(b),(long)(c),(long)(d))
#define syscall5(n,a,b,c,d,e) _syscall5(n,(long)(a),(long)(b),(long)(c),(long)(d),(long)(e))

现在就可以定义由系统调用: #define open(...) syscall(__NR_open,__VA_ARGS__)它会扩大到syscall3(5,(long)a,(long)b,(long)c)打开时叫与3个参数(5个用于从__NR_open#包括unistd.h中来间接地)。



文章来源: Variadic macro expanding