对的 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_VARDECL_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))

参见full example at coliru