C++ preprocessing generate variable member, setters and map
我正在尝试使用预处理器生成一些代码。
在某些类中,我需要定义多个变量成员,其对应的setter和一个包含每个已声明变量的引用的映射。
为说明我的需求,您可以找到以下我想要实现的代码示例。
在此示例中,我仅声明了两个变量,但实际上,应在不同的类中声明更多变量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | #include <boost/preprocessor.hpp> #include <iostream> #include <string> #include <unordered_map> #include <unordered_set> class typeBase { }; template <typename T> class typeBase_: public typeBase { private: T value_; public: typeBase_() { } typeBase_(const T& v): value_(v) { } }; using typeInt = typeBase_<int>; using typeStr = typeBase_<std::string>; std::unordered_set<std::string> changed_list_; ////////////////////////////////////////////////////////// // Here use generation to generate the bellow code ////////////////////////////////////////////////////////// typeInt mInt_; typeStr mStr_; std::unordered_map<std::string, typeBase&> properties_ = { {"mInt", mInt_}, {"mStr", mStr_} }; void set_mInt(const typeInt& mInt) { mInt_ = mInt; changed_list_.insert("mInt"); } void set_mStr(const typeStr& mStr) { mStr_ = mStr; changed_list_.insert("mStr"); } ///////////////////////////////////////////// ///////////////////////////////////////////// int main() { set_mInt(2); set_mStr(std::string("test")); } |
当我第一次尝试使用Boost预处理库时,我暂时无法创建包含对每个变量成员的引用的映射:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #define MODEL_DECLARE(...) \\ std::unordered_map<std::string, typeBase&> properties = { \\ MODEL_GENERATE_MAP_ITEMS(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \\ }; #define MODEL_GENERATE_MAP_ITEMS(Args) \\ BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(MODEL_GENERATE_MAP_ITEM, %%, Args)) #define MODEL_GENERATE_MAP_ITEM(s, Unused, Arg) \\ {(MODEL_STRINGIFY(BOOST_PP_TUPLE_ELEM(2, 0, Arg)), BOOST_PP_TUPLE_ELEM(2, 0, Arg))} #define MODEL_STRINGIFY_(V) #V #define MODEL_STRINGIFY(V) MODEL_STRINGIFY_(V) #define MODEL_MAKE_ITEM(s, Unused, Arg) \\ {BOOST_PP_TUPLE_ELEM(2, 0, Arg) BOOST_PP_TUPLE_ELEM(2, 1, Arg)} // Generate model with this line MODEL_DECLARE((mInt, typeInt), (mStr, typeStr)) |
使用此代码,我会生成以下预处理行:
1 | std::unordered_map<std::string, typeBase&> properties = { {("mInt", mInt)}, {("mStr", mStr)} }; |
如您所见,我有一个需要删除的括号,而我大桶没能删除。
您是否知道实现我所需的更好的解决方案,或者如何修复我的代码以成功生成所需的代码?
致谢
EDIT1:
我开始实现@parktomatomi解决方案,并且我还尝试添加代码来声明变量和设置器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | #include <boost/preprocessor.hpp> #include <iostream> #include <string> #include <unordered_map> #include <unordered_set> #include <cassert> class typeBase { }; template <typename T> class typeBase_: public typeBase { private: T value_; public: typeBase_() { } typeBase_(const T& v): value_(v) { } }; using typeInt = typeBase_<int>; using typeStr = typeBase_<std::string>; std::unordered_set<std::string> changed_list_; ////////////////////////////////////////////////////////// // Here use generation to generate the bellow code ////////////////////////////////////////////////////////// template <typename... Ts> std::unordered_map<std::string, typeBase*> build_properties(Ts&&... args) { return std::unordered_map<std::string, typeBase*> { { args.first, args.second }... }; } // Macro used to generate properties map #define MODEL_GENERATE_MAP_ITEM(Name, Type) std::make_pair( #Name, &Name##_ ) #define MODEL_UNWRAP_MAP_ITEM(Unused1, Unused2, Arg) MODEL_GENERATE_MAP_ITEM Arg #define MODEL_GENERATE_MAP_ITEMS(Args) BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(MODEL_UNWRAP_MAP_ITEM,,Args)) // Macro used to declare vars and setters #define MODEL_GENERATE_VAR(Name, Type) Type Name##_; \\ void set_##Name(const Type& Name) { \\ Name##_ = Name; \\ changed_list_.insert(#Name); \\ }; #define MODEL_UNWRAP_VAR(Unused1, Unused2, Arg) MODEL_GENERATE_VAR Arg #define MODEL_GENERATE_VARS(Args) BOOST_PP_SEQ_TRANSFORM(MODEL_UNWRAP_VAR,,Args) // Macro to generate model #define MODEL_DECLARE(...) \\ std::unordered_map<std::string, typeBase*> properties_ = build_properties( \\ MODEL_GENERATE_MAP_ITEMS(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \\ ); \\ MODEL_GENERATE_VARS(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) // Generate model MODEL_DECLARE((mInt, typeInt), (mStr, typeStr)) int main() { assert(properties_.size() == 2); assert(properties_["mInt"] == &mInt_); assert(properties_["mStr"] == &mStr_); } |
但是这不能编译,因为预处理生成在声明周围加上了括号:
1 | (typeInt mInt_; void set_mInt(const typeInt& mInt) { mInt_ = mInt; changed_list_.insert("mInt"); };) (typeStr mStr_; void set_mStr(const typeStr& mStr) { mStr_ = mStr; changed_list_.insert("mStr"); };) |
如何删除该旁瓣?
我试图将其编译为两个问题:
要修复#1,请改用指针:
1 | std::unordered_map<std::string, typeBase*> |
要修复#2,请使用辅助函数初始化地图:
1 2 3 4 | template <typename... Ts> std::unordered_map<std::string, typeBase*> build_properties(Ts&&... args) { return std::unordered_map<std::string, typeBase*> { { args.first, args.second }... }; } |
然后目标是使用宏生成此代码:
1 2 3 4 | build_properties( std::make_pair("mInt", &mInt_), std::make_pair("mStr", &mStr_) ) |
哪个更容易并且可以成功编译:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #define MODEL_GENERATE_MAP_ITEM(Name, Type) std::make_pair( #Name, &Name##_ ) #define MODEL_UNWRAP_MAP_ITEM(Unused1, Unused2, Arg) MODEL_GENERATE_MAP_ITEM Arg #define MODEL_GENERATE_MAP_ITEMS(Args) \\ BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(MODEL_UNWRAP_MAP_ITEM,,Args)) #define MODEL_DECLARE(...) \\ std::unordered_map<std::string, typeBase*> properties = build_properties( \\ MODEL_GENERATE_MAP_ITEMS(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \\ ); // Generate model with this line MODEL_DECLARE((mInt, typeInt), (mStr, typeStr)) |
与您的问题更直接相关的是,
1 | (mInt, typeInt), (mStr, typeStr) ==> ((mInt, typeInt)) ((mStr, typeStr)) |
因此,当
1 | MODEL_UNWRAP_MAP_ITEM(Unused1, Unused2, Arg) MODEL_GENERATE_MAP_ITEM Arg |
替换
:
1 | MODEL_GENERATE_MAP_ITEM (mInt, typeInt) |
最后一次转换为:
1 | std::make_pair("mInt", mInt_ ) |
演示:https://godbolt.org/z/5fyo3N
更新
要使更新的代码正常工作,我必须进行3处更改:
1 | BOOST_PP_SEQ_TRANSFORM(ADD_1, , (a)(b)) ==> (a+1)(b+1) |
虽然
1 | BOOST_PP_SEQ_TRANSFORM(ADD_1, , (a)(b)) ==> a+1 b+1 |
由于具有
1 | typeBase_& operator =(const typeBase_& that) { value_ = that.value_; return *this; } |
一起:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #define MODEL_GENERATE_MAP_ITEM(Name, Type) std::make_pair( #Name, &Name##_ ) #define MODEL_UNWRAP_MAP_ITEM(Unused1, Unused2, Arg) MODEL_GENERATE_MAP_ITEM Arg #define MODEL_GENERATE_MAP_ITEMS(Args) BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(MODEL_UNWRAP_MAP_ITEM,,Args)) // Macro used to declare vars and setters #define MODEL_GENERATE_VAR(Name, Type) Type Name##_; \\ void set_##Name(const Type& Name) { \\ Name##_ = Name; \\ changed_list_.insert(#Name); \\ }; #define MODEL_UNWRAP_VAR(Unused1, Unused2, Arg) MODEL_GENERATE_VAR Arg #define MODEL_GENERATE_VARS(Args) BOOST_PP_SEQ_FOR_EACH(MODEL_UNWRAP_VAR,,Args) // Macro to generate model #define MODEL_DECLARE(...) \\ MODEL_GENERATE_VARS(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \\ std::unordered_map<std::string, typeBase*> properties_ = build_properties( \\ MODEL_GENERATE_MAP_ITEMS(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \\ ); // Generate model MODEL_DECLARE((mInt, typeInt), (mStr, typeStr)) |
演示:https://godbolt.org/z/bDDe94