我想知道有没有办法有选择地调用C VARIADIC MACRO。
首先,让我展示一些我想要实现的代码:
#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和Clang 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'的实际参数太多
我该怎么做才能解决它?
答案 0 :(得分:1)
C预处理器不适合您尝试做的事情(即使您克服了这个问题)。
首先,请确保您无法使用C ++模板解决问题。
如果做不到这一点,它也需要代码生成:以某种表示形式对类进行规范并生成包含所有序列化内容的代码。
这是另一回事。你正在努力哄骗宏来生成多个术语的求和:
GetSize(arg1) + GetSize(arg2) + ... + GetSize(argN)
但是你忽略了你可以有一个N-ary函数来做同样的事情:
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;
}
答案 1 :(得分:1)
您不能在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__ )
我不知道你的最终目标是什么,但这个错误只是跳出来了。
答案 2 :(得分:0)
最大的诀窍是了解##
以及__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__)
当使用3个参数调用open时它将扩展为syscall3(5,(long)a,(long)b,(long)c)
(__NR_open的5来自#include unistd.h)。< / p>