C++预处理生成变量成员、setter和map
C++ preprocessing generate variable member, setters and map
我正在尝试使用预处理器生成一些代码。
在某些 class 中,我需要定义多个变量成员、它们对应的 setter 和一个包含对每个声明变量的引用的映射。
为了说明我的需要,您可以找到我想要实现的以下代码示例。
在这个例子中,我只声明了两个变量,但在实际情况下,应该在不同的 class:
中声明更多的变量
#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 预处理库时,我暂时无法创建包含对每个变量成员的引用的映射:
#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))
使用这段代码,我生成了这条预处理线:
std::unordered_map<std::string, typeBase&> properties = { {("mInt", mInt)}, {("mStr", mStr)} };
如您所见,我有需要删除的括号,但我没有这样做。
您是否知道实现我需要的更好的解决方案,或者我怎样才能修复我的代码以成功生成所需的代码?
此致
编辑 1:
我开始实施@parktomatomi 解决方案,我还尝试添加代码来声明变量和设置器:
#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_);
}
但是这不会编译,因为预处理生成在声明周围添加括号:
(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,请改用指针:
std::unordered_map<std::string, typeBase*>
要修复 #2,请使用辅助函数初始化地图:
template <typename... Ts>
std::unordered_map<std::string, typeBase*> build_properties(Ts&&... args) {
return std::unordered_map<std::string, typeBase*> { { args.first, args.second }... };
}
然后目标变成用宏生成this:
build_properties(
std::make_pair("mInt", &mInt_),
std::make_pair("mStr", &mStr_)
)
哪个比较简单,编译成功:
#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))
与您的问题更直接相关的是,BOOST_PP_VARIADIC_TO_SEQ
宏在您的参数周围添加了一对括号:
(mInt, typeInt), (mStr, typeStr) ==> ((mInt, typeInt)) ((mStr, typeStr))
所以当 BOOST_PP_SEQ_TRANSFORM
生成它的宏时,为每个 t运行sformation 宏生成的参数都有括号,例如(mInt, typeInt)
。为了摆脱那些括号,我添加了这个宏:
MODEL_UNWRAP_MAP_ITEM(Unused1, Unused2, Arg) MODEL_GENERATE_MAP_ITEM Arg
当Arg
被替换为:
MODEL_GENERATE_MAP_ITEM (mInt, typeInt)
最后一次运行形成为:
std::make_pair( "mInt", mInt_ )
演示:https://godbolt.org/z/5fyo3N
更新
为了使更新后的代码正常工作,我必须进行 3 处更改:
- 而不是
BOOST_PP_SEQ_TRANSFORM
,使用 BOOST_PP_SEQ_FOR_EACH
。前者在每个项目上做一个映射并将其转回 SEQ
:
BOOST_PP_SEQ_TRANSFORM(ADD_1, , (a)(b)) ==> (a+1)(b+1)
虽然 BOOST_PP_SEQ_FOR_EACH
做了一个映射操作,但是没有把括号加回来:
BOOST_PP_SEQ_TRANSFORM(ADD_1, , (a)(b)) ==> a+1 b+1
因为你有那个 set_mInt
函数,你必须添加一个赋值运算符到 typeBase_<T>
:
typeBase_& operator =(const typeBase_& that) { value_ = that.value_; return *this; }
MODEL_GENERATE_VARS
需要在地图声明之前所以 mInt_
和朋友可见
总计:
#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))
我正在尝试使用预处理器生成一些代码。 在某些 class 中,我需要定义多个变量成员、它们对应的 setter 和一个包含对每个声明变量的引用的映射。
为了说明我的需要,您可以找到我想要实现的以下代码示例。 在这个例子中,我只声明了两个变量,但在实际情况下,应该在不同的 class:
中声明更多的变量#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 预处理库时,我暂时无法创建包含对每个变量成员的引用的映射:
#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))
使用这段代码,我生成了这条预处理线:
std::unordered_map<std::string, typeBase&> properties = { {("mInt", mInt)}, {("mStr", mStr)} };
如您所见,我有需要删除的括号,但我没有这样做。
您是否知道实现我需要的更好的解决方案,或者我怎样才能修复我的代码以成功生成所需的代码?
此致
编辑 1:
我开始实施@parktomatomi 解决方案,我还尝试添加代码来声明变量和设置器:
#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_);
}
但是这不会编译,因为预处理生成在声明周围添加括号:
(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,请改用指针:
std::unordered_map<std::string, typeBase*>
要修复 #2,请使用辅助函数初始化地图:
template <typename... Ts>
std::unordered_map<std::string, typeBase*> build_properties(Ts&&... args) {
return std::unordered_map<std::string, typeBase*> { { args.first, args.second }... };
}
然后目标变成用宏生成this:
build_properties(
std::make_pair("mInt", &mInt_),
std::make_pair("mStr", &mStr_)
)
哪个比较简单,编译成功:
#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))
与您的问题更直接相关的是,BOOST_PP_VARIADIC_TO_SEQ
宏在您的参数周围添加了一对括号:
(mInt, typeInt), (mStr, typeStr) ==> ((mInt, typeInt)) ((mStr, typeStr))
所以当 BOOST_PP_SEQ_TRANSFORM
生成它的宏时,为每个 t运行sformation 宏生成的参数都有括号,例如(mInt, typeInt)
。为了摆脱那些括号,我添加了这个宏:
MODEL_UNWRAP_MAP_ITEM(Unused1, Unused2, Arg) MODEL_GENERATE_MAP_ITEM Arg
当Arg
被替换为:
MODEL_GENERATE_MAP_ITEM (mInt, typeInt)
最后一次运行形成为:
std::make_pair( "mInt", mInt_ )
演示:https://godbolt.org/z/5fyo3N
更新
为了使更新后的代码正常工作,我必须进行 3 处更改:
- 而不是
BOOST_PP_SEQ_TRANSFORM
,使用BOOST_PP_SEQ_FOR_EACH
。前者在每个项目上做一个映射并将其转回SEQ
:
虽然BOOST_PP_SEQ_TRANSFORM(ADD_1, , (a)(b)) ==> (a+1)(b+1)
BOOST_PP_SEQ_FOR_EACH
做了一个映射操作,但是没有把括号加回来:BOOST_PP_SEQ_TRANSFORM(ADD_1, , (a)(b)) ==> a+1 b+1
因为你有那个
set_mInt
函数,你必须添加一个赋值运算符到typeBase_<T>
:typeBase_& operator =(const typeBase_& that) { value_ = that.value_; return *this; }
MODEL_GENERATE_VARS
需要在地图声明之前所以mInt_
和朋友可见
总计:
#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))