对的 C++ 可变参数宏
C++ variadic macro for pairs
我知道 MAP macro 可用于将宏函数应用于可变参数列表。但是如何将宏函数应用于可变参数对?
我想创建的是这样的:
#define DECL_VAR(type,var)\
type _##var;
#define DECL_GETSET(type,var)\
type get_##var() const {return _##var;}\
void set_##var(type val) {_##var = val;}
#define CREATE_CLASS(C, ...)\
class C {\
private:\
MAP_PAIR(DECL_VAR, ODD_VA_ARG, EVEN_VA_ARG)\
public:\
MAP_PAIR(DECL_GETSET, ODD_VA_ARG, EVEN_VA_ARG)\
};
CREATE_CLASS(Person, const char*, name, int, age, float, height)
// or maybe
CREATE_CLASS(Person, (const char*, name), (int, age), (float, height))
这适合你吗?
#include <iostream>
#include <string>
#define MACRO_DECL_TYPE(t, v) t
#define MACRO_DECL_NAME(t, v) v = {};
#define MACRO_GET_TYPE(t, v)
#define MACRO_GET_NAME(t, v) t get_##v(void) const { \
std::cout << "called get() for " << #t << " " << #v << " as " << v << std::endl; \
return v; \
}
#define MACRO_SET_TYPE(t, v)
#define MACRO_SET_NAME(t, v) void set_##v(t to) { \
v = to; \
std::cout << "called set() for " << #t << " " << #v << " as " << v << std::endl; \
}
#define CREATE_CLASS(CLASS_NAME, MACRO_VARS) \
class CLASS_NAME { \
private: \
MACRO_VARS(MACRO_DECL_TYPE, MACRO_DECL_NAME) \
public: \
MACRO_VARS(MACRO_GET_TYPE, MACRO_GET_NAME) \
MACRO_VARS(MACRO_SET_TYPE, MACRO_SET_NAME) \
};
#define LIST_MACRO_COMBO(list_macro1, list_macro2, type,name) \
list_macro1(type,name) list_macro2(type,name)
//
// Example
// g++ test.cpp --std=c++11 ; ./a.out
//
#define Foo_args(list_macro1, list_macro2) \
LIST_MACRO_COMBO(list_macro1, list_macro2, int, a) \
LIST_MACRO_COMBO(list_macro1, list_macro2, float, b) \
LIST_MACRO_COMBO(list_macro1, list_macro2, std::string, c) \
CREATE_CLASS(Foo, Foo_args)
int main (void)
{
Foo foo;
foo.get_a();
foo.get_b();
foo.get_c();
foo.set_a(1);
foo.set_b(2);
foo.set_c("hello");
foo.get_a();
foo.get_b();
foo.get_c();
}
输出
g++ test.cpp --std=c++11 ; ./a.out
called get() for int a as 0
called get() for float b as 0
called get() for std::string c as
called set() for int a as 1
called set() for float b as 2
called set() for std::string c as hello
called get() for int a as 1
called get() for float b as 2
called get() for std::string c as hello
CREATE_CLASS(Person, (const char*, name), (int, age), (float, height))
这将是更容易使用的选项,因为预处理器语法以某种方式处理平衡括号,例如(const char*, name)
是宏的单个参数,尽管包含逗号。
因此,一个直接的解决方案是提供包装宏,它接受 (type, varname)
形式的参数,并将其元素传递给实际的双参数宏:
#define DECL_VAR(type,var)\
type _##var;
#define DECL_VAR_PAIR(pair)\
DECL_VAR pair
#define DECL_GETSET(type,var)\
type get_##var() const {return _##var;}\
void set_##var(type val) {_##var = val;}
#define DECL_GETSET_PAIR(pair)\
DECL_GETSET pair
#define CREATE_CLASS(C, ...)\
class C {\
private:\
MAP(DECL_VAR_PAIR, __VA_ARGS__)\
public:\
MAP(DECL_GETSET_PAIR, __VA_ARGS__)\
};
CREATE_CLASS(Person, (const char*, name), (int, age), (float, height))
因此,例如,当最后 CREATE_CLASS
行中 MAP(DECL_VAR_PAIR, __VA_ARGS__)
的扩展将一个参数 (int, age)
传递给 DECL_VAR_PAIR
时,扩展的步骤包括:
DECL_VAR_PAIR((int, age))
DECL_VAR(int, age) // since DECL_VAR_PAIR(x) is just DECL_VAR then x
int _##age;
int _age;
虽然如果您有很多事情想用成对的参数来做,创建所有这些包装器宏可能会很麻烦。相反,我们可以添加一个类似于 MAP
的宏,该宏期望其参数是括在括号中的列表。首先,请注意在 <map.h>
中,实际将宏应用于其中一个参数的步骤与主要 MAP
宏密切相关:
#define MAP0(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP1)(f, peek, __VA_ARGS__)
#define MAP1(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP0)(f, peek, __VA_ARGS__)
#define MAP(f, ...) EVAL(MAP1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
如果参数 x
已经包含要传递给宏 f
的一组参数的括号,我们只希望并行版本跳过在 x
周围添加括号:
#define MAP_TUPLES0(f, x, peek, ...) f x MAP_NEXT(peek, MAP_TUPLES1)(f, peek, __VA_ARGS__)
#define MAP_TUPLES1(f, x, peek, ...) f x MAP_NEXT(peek, MAP_TUPLES0)(f, peek, __VA_ARGS__)
#define MAP_TUPLES(f, ...) EVAL(MAP_TUPLES1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
我称它为 MAP_TUPLES
而不是 MAP_PAIRS
因为它实际上并不限于成对。只要宏参数的数量匹配,它就可以将任何大小的参数列表传递给任何宏。您甚至可以使用具有不同大小参数列表的可变参数宏。
使用这个 MAP_TUPLES
得到你的 CREATE_CLASS
,假设你原来的 DECL_VAR
和 DECL_GETSET
,看起来像:
#define CREATE_CLASS(C, ...)\
class C {\
private:\
MAP_TUPLES(DECL_VAR, __VA_ARGS__)\
public:\
MAP_TUPLES(DECL_GETSET, __VA_ARGS__)\
};
CREATE_CLASS(Person, (const char*, name), (int, age), (float, height))
我知道 MAP macro 可用于将宏函数应用于可变参数列表。但是如何将宏函数应用于可变参数对?
我想创建的是这样的:
#define DECL_VAR(type,var)\
type _##var;
#define DECL_GETSET(type,var)\
type get_##var() const {return _##var;}\
void set_##var(type val) {_##var = val;}
#define CREATE_CLASS(C, ...)\
class C {\
private:\
MAP_PAIR(DECL_VAR, ODD_VA_ARG, EVEN_VA_ARG)\
public:\
MAP_PAIR(DECL_GETSET, ODD_VA_ARG, EVEN_VA_ARG)\
};
CREATE_CLASS(Person, const char*, name, int, age, float, height)
// or maybe
CREATE_CLASS(Person, (const char*, name), (int, age), (float, height))
这适合你吗?
#include <iostream>
#include <string>
#define MACRO_DECL_TYPE(t, v) t
#define MACRO_DECL_NAME(t, v) v = {};
#define MACRO_GET_TYPE(t, v)
#define MACRO_GET_NAME(t, v) t get_##v(void) const { \
std::cout << "called get() for " << #t << " " << #v << " as " << v << std::endl; \
return v; \
}
#define MACRO_SET_TYPE(t, v)
#define MACRO_SET_NAME(t, v) void set_##v(t to) { \
v = to; \
std::cout << "called set() for " << #t << " " << #v << " as " << v << std::endl; \
}
#define CREATE_CLASS(CLASS_NAME, MACRO_VARS) \
class CLASS_NAME { \
private: \
MACRO_VARS(MACRO_DECL_TYPE, MACRO_DECL_NAME) \
public: \
MACRO_VARS(MACRO_GET_TYPE, MACRO_GET_NAME) \
MACRO_VARS(MACRO_SET_TYPE, MACRO_SET_NAME) \
};
#define LIST_MACRO_COMBO(list_macro1, list_macro2, type,name) \
list_macro1(type,name) list_macro2(type,name)
//
// Example
// g++ test.cpp --std=c++11 ; ./a.out
//
#define Foo_args(list_macro1, list_macro2) \
LIST_MACRO_COMBO(list_macro1, list_macro2, int, a) \
LIST_MACRO_COMBO(list_macro1, list_macro2, float, b) \
LIST_MACRO_COMBO(list_macro1, list_macro2, std::string, c) \
CREATE_CLASS(Foo, Foo_args)
int main (void)
{
Foo foo;
foo.get_a();
foo.get_b();
foo.get_c();
foo.set_a(1);
foo.set_b(2);
foo.set_c("hello");
foo.get_a();
foo.get_b();
foo.get_c();
}
输出
g++ test.cpp --std=c++11 ; ./a.out
called get() for int a as 0
called get() for float b as 0
called get() for std::string c as
called set() for int a as 1
called set() for float b as 2
called set() for std::string c as hello
called get() for int a as 1
called get() for float b as 2
called get() for std::string c as hello
CREATE_CLASS(Person, (const char*, name), (int, age), (float, height))
这将是更容易使用的选项,因为预处理器语法以某种方式处理平衡括号,例如(const char*, name)
是宏的单个参数,尽管包含逗号。
因此,一个直接的解决方案是提供包装宏,它接受 (type, varname)
形式的参数,并将其元素传递给实际的双参数宏:
#define DECL_VAR(type,var)\
type _##var;
#define DECL_VAR_PAIR(pair)\
DECL_VAR pair
#define DECL_GETSET(type,var)\
type get_##var() const {return _##var;}\
void set_##var(type val) {_##var = val;}
#define DECL_GETSET_PAIR(pair)\
DECL_GETSET pair
#define CREATE_CLASS(C, ...)\
class C {\
private:\
MAP(DECL_VAR_PAIR, __VA_ARGS__)\
public:\
MAP(DECL_GETSET_PAIR, __VA_ARGS__)\
};
CREATE_CLASS(Person, (const char*, name), (int, age), (float, height))
因此,例如,当最后 CREATE_CLASS
行中 MAP(DECL_VAR_PAIR, __VA_ARGS__)
的扩展将一个参数 (int, age)
传递给 DECL_VAR_PAIR
时,扩展的步骤包括:
DECL_VAR_PAIR((int, age))
DECL_VAR(int, age) // since DECL_VAR_PAIR(x) is just DECL_VAR then x
int _##age;
int _age;
虽然如果您有很多事情想用成对的参数来做,创建所有这些包装器宏可能会很麻烦。相反,我们可以添加一个类似于 MAP
的宏,该宏期望其参数是括在括号中的列表。首先,请注意在 <map.h>
中,实际将宏应用于其中一个参数的步骤与主要 MAP
宏密切相关:
#define MAP0(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP1)(f, peek, __VA_ARGS__)
#define MAP1(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP0)(f, peek, __VA_ARGS__)
#define MAP(f, ...) EVAL(MAP1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
如果参数 x
已经包含要传递给宏 f
的一组参数的括号,我们只希望并行版本跳过在 x
周围添加括号:
#define MAP_TUPLES0(f, x, peek, ...) f x MAP_NEXT(peek, MAP_TUPLES1)(f, peek, __VA_ARGS__)
#define MAP_TUPLES1(f, x, peek, ...) f x MAP_NEXT(peek, MAP_TUPLES0)(f, peek, __VA_ARGS__)
#define MAP_TUPLES(f, ...) EVAL(MAP_TUPLES1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
我称它为 MAP_TUPLES
而不是 MAP_PAIRS
因为它实际上并不限于成对。只要宏参数的数量匹配,它就可以将任何大小的参数列表传递给任何宏。您甚至可以使用具有不同大小参数列表的可变参数宏。
使用这个 MAP_TUPLES
得到你的 CREATE_CLASS
,假设你原来的 DECL_VAR
和 DECL_GETSET
,看起来像:
#define CREATE_CLASS(C, ...)\
class C {\
private:\
MAP_TUPLES(DECL_VAR, __VA_ARGS__)\
public:\
MAP_TUPLES(DECL_GETSET, __VA_ARGS__)\
};
CREATE_CLASS(Person, (const char*, name), (int, age), (float, height))