c++ - 宏不通过直接调用扩展,而是通过间接调用扩展

标签 c++ macros variadic-macros boost-preprocessor

我有以下宏

#include <boost/preprocessor.hpp>

#define DB_FIELD(...) BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)

#define DB_TOFIELD(type,name) \
  private:\
  type name##_;\
  public:\
  const type& get_##name(){return name##_;}\
  void set_##name(const type& val) { name##_ = val; }

#define GEN_ENUM_FIELD(r,data,elem) BOOST_PP_CAT(FIELD_,BOOST_PP_SEQ_ELEM(1,elem)),

#define DECLARE(type, name) DB_TOFIELD(type, name)

#define GEN_FIELD_DECL(r, data, elem) DECLARE(BOOST_PP_SEQ_ELEM(0,elem),BOOST_PP_SEQ_ELEM(1,elem))

#define DB_TABLE(name, ...) class name : public DataBaseTable {\
  public:\
  constexpr static const char *get_table_name() { return #name; }\
  BOOST_PP_LIST_FOR_EACH(GEN_FIELD_DECL,, BOOST_PP_VARIADIC_TO_LIST(__VA_ARGS__)) \
  enum Fields{ \
  BOOST_PP_LIST_FOR_EACH(GEN_ENUM_FIELD,, BOOST_PP_VARIADIC_TO_LIST(__VA_ARGS__))\
  FIELD_COUNT\
  };\
  };

现在以下代码:

DB_TABLE(Test2,
  DB_FIELD(int, foo),
  DB_FIELD(int, bar)
)

生成:

class Test2 : public DataBaseTable {
public:
  constexpr static const char *get_table_name() { return "Test2"; }
private:
  int foo_;
public:
  const int &get_foo() { return foo_; }
  void set_foo(const int &val) { foo_ = val; }
private:
  int bar_;
public:
  const int &get_bar() { return bar_; }
  void set_bar(const int &val) { bar_ = val; }
  enum Fields { FIELD_foo, FIELD_bar, FIELD_COUNT };
};

这和我写的一样丑陋,但我关心的是为什么我需要在 GEN_FIELD_DECL 宏中使用这种级别的间接(DECLARE)?直接调用DB_TOFIELD

#define GEN_FIELD_DECL(r, data, elem) DB_TOFIELD(BOOST_PP_SEQ_ELEM(0,elem),BOOST_PP_SEQ_ELEM(1,elem))

产生垃圾:

class Test2 : public DataBaseTable {
public:
  constexpr static const char *get_table_name() { return "Test2"; }
private:
  int foo _;
public:
  const int &get_BOOST_PP_SEQ_ELEM(1, (int)(foo))() { return foo _; }
  void set_BOOST_PP_SEQ_ELEM(1, (int)(foo))(const int &val) { foo _ = val; }
private:
  int bar _;
public:
  const int &get_BOOST_PP_SEQ_ELEM(1, (int)(bar))() { return bar _; }
  void set_BOOST_PP_SEQ_ELEM(1, (int)(bar))(const int &val) { bar _ = val; }
  enum Fields { FIELD_foo, FIELD_bar, FIELD_COUNT };
};

clang 3.7.1 和 gcc 5.3 重现了相同的行为

最佳答案

您遇到的问题是预处理器如何扩展参数(如果参数是 ### 运算符的参数)。来自 C++.2011 §16.3.1¶1:

After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. A parameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file; no other preprocessing tokens are available.

宏间接寻址避免了异常子句,导致参数在被另一个宏处理之前被扩展。

例如:

#define FOO 10
#define BAR(x) x ## 7
#define BAR2(x) BAR(x)

int x = BAR(FOO);      // => int x = FOO7;

int y = BAR2(FOO);     // => int y = BAR(10); (intermediate result)
                       // => int y = 107;     (final expansion)

关于c++ - 宏不通过直接调用扩展,而是通过间接调用扩展,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37061288/

相关文章:

c++ - 可以将指向子类的指针分配给具有父类(super class)类型的变量吗?

c++ - `std::shuffle` 是否保证不同 vector 上相同种子的相同顺序?

c++ - 获取错误无法从C++ 17编译器的间接库继承构造函数

c++ - 遍历 C 中数组中不存在的变量

c++ - 使用可变参数宏打印多个调试行

c - 如何将参数传递给可变参数宏?

c++ - 使用 OpenCV 找到没有狭窄瓶颈的连续区域

macros - 带有图标的菜单栏中的 Notepad++ 宏

c++ - 是什么让这个头文件让 VS2005 慢得像爬行一样? (智能感知无罪?)

c - 可变参数宏 : expansion of pasted tokens