问题
I've got the following macros
#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\
};\
};
Now the following code:
DB_TABLE(Test2,
DB_FIELD(int, foo),
DB_FIELD(int, bar)
)
Generates:
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 };
};
Which is as ugly as I wrote it, but my concern is why do I need this level of indirection (DECLARE) in the GEN_FIELD_DECL macro ? The direct call to DB_TOFIELD
#define GEN_FIELD_DECL(r, data, elem) DB_TOFIELD(BOOST_PP_SEQ_ELEM(0,elem),BOOST_PP_SEQ_ELEM(1,elem))
produces garbage:
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 };
};
The same behavior is reproduced with clang 3.7.1 and gcc 5.3
回答1:
What you are running into is an exception to how the preprocessor expands parameters if they are arguments to the #
or ##
operators. From 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.
The macro indirection avoids the exception clause, causing the argument to be expanded before being processed by the other macro.
For example:
#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)
来源:https://stackoverflow.com/questions/35710536/macro-not-expanded-with-direct-call-but-expanded-with-indirect