关于boost:C预处理生成变量成员,setter和map

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))

    与您的问题更直接相关的是,BOOST_PP_VARIADIC_TO_SEQ宏在您的参数周围添加了一对括号:

    1
    (mInt, typeInt), (mStr, typeStr) ==> ((mInt, typeInt)) ((mStr, typeStr))

    因此,当BOOST_PP_SEQ_TRANSFORM生成其宏时,为每个转换宏生成的参数周围都带有括号,例如(mInt, typeInt)。为了消除这些括号,我添加了此宏:

    1
    MODEL_UNWRAP_MAP_ITEM(Unused1, Unused2, Arg) MODEL_GENERATE_MAP_ITEM Arg

    替换Arg时的

    1
    MODEL_GENERATE_MAP_ITEM (mInt, typeInt)

    最后一次转换为:

    1
    std::make_pair("mInt", mInt_ )

    演示:https://godbolt.org/z/5fyo3N

    更新

    要使更新的代码正常工作,我必须进行3处更改:

  • 代替BOOST_PP_SEQ_TRANSFORM,使用BOOST_PP_SEQ_FOR_EACH。前者对每个项目进行映射,然后将其变回SEQ

    1
    BOOST_PP_SEQ_TRANSFORM(ADD_1, , (a)(b)) ==> (a+1)(b+1)

    虽然BOOST_PP_SEQ_FOR_EACH进行映射操作,但不添加括号:

    1
    BOOST_PP_SEQ_TRANSFORM(ADD_1, , (a)(b)) ==> a+1 b+1
  • 由于具有set_mInt函数,因此必须将赋值运算符添加到typeBase_<T>

    1
    typeBase_& operator =(const typeBase_& that) { value_ = that.value_; return *this; }
  • MODEL_GENERATE_VARS必须在地图声明之前,以便可见mInt_和朋友

  • 一起:

    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