I'm attempting to generate some code with preprocessor. In some class, I need to define multiple variable member, their corresponding setters and a map containing reference on each declared variable.
To illustrate my need, you can find the following code example of what I want to achieve. In this example, I only declare two variable but in the real case more variable should be declared in different 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"));
}
At the time I first tried with Boost preprocessing library and I am stuck for the moment at creating the map containing reference to each variable member:
#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))
With this code I produce this preprocessing line:
std::unordered_map<std::string, typeBase&> properties = { {("mInt", mInt)}, {("mStr", mStr)} };
As you can see, I have parenthesis which need to be removed, which I barrely failed to to.
Do you know a better solution to achieve what I need, or how can I FIX my code to successfully generate the needed code?
Regards
EDIT1:
I started to implement the @parktomatomi solution and I also tryed to add code to declare variable and setters:
#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_);
}
However this do not compile because the preprocessing generation add parenthesis around the declaration:
(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"); };)
How to remove this paranthesis?