Расширение макроса Variadic

Я хочу знать, есть ли способ вызывать 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’

Что я могу сделать, чтобы это исправить?

1

Решение

Препроцессор C не подходит для того, что вы пытаетесь сделать (даже если вы преодолеваете эту проблему).

Во-первых, убедитесь, что вы не можете решить проблему с шаблонами C ++.

В противном случае он также требует генерации кода: то, что принимает спецификацию ваших классов в некоторой нотации и генерирует код со всеми элементами сериализации.

Здесь другая вещь. Вы очень стараетесь уговорить макросы на создание суммирования с несколькими терминами:

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

но вы упускаете из виду, что у вас может быть N-арная функция, которая делает то же самое:

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

Теперь макрос не должен генерировать несколько терминов вызова функции с + оператор между ними, но только список аргументов через запятую!

Вы слишком сложные вещи в вашей оригинальной программе также. 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

Другие решения

Вы не можете поместить вызов макроса в аргументы 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__ )

Я не могу сказать, какова ваша конечная цель, но эта ошибка просто выскочила на меня.

1

Самый большой трюк это понимание ## и как __VA_ARGS__ расширяется. Вот пример, который я использую для системных вызовов Linux (количество аргументов -1, поскольку первый аргумент относится к номеру системного вызова … обратите внимание, что поэтому он заканчивается на 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) когда open вызывается с 3 параметрами (5 для __NR_open поступает косвенно из #inclusive unistd.h).

0
По вопросам рекламы [email protected]