宏没有用直接调用展开,而是用间接调用展开
Macro not expanded with direct call, but expanded with indirect
我有以下宏
#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宏?直接调用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)
我有以下宏
#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宏?直接调用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)