我可以使用C++宏将代码插入到不同的地方吗?

ose*_*aya 2 c++ template-meta-programming preprocessor-meta-program

我想编写一个宏来声明一个结构体字段并将该字段的初始化过程插入到一个Init()函数中。但我发现宏不可能将代码插入到不同的位置(在本例中为字段声明和 Init() 函数定义)。

// I have 
template <typename T>
T* initValue() {
  // return T* or nullptr
}

// I need some sort of 
DECL_CLASS(MyClass)
DECL_FIELD(int, foo)
DECL_FIELD(float, bar)
DECL_CLASS_END()

// To generate
struct MyClass {
  int *foo;
  float *bar;

public:
  bool Init() {
    if (!(foo = initValue<decay_t<decltype(*foo)>>())) {
      return false;
    }
    if (!(bar = initValue<decay_t<decltype(*bar)>>())) {
      return false;
    }
    return true;
  }
};
Run Code Online (Sandbox Code Playgroud)

我可以使用某种代码生成工具(例如 cog)来实现此目的,但我不想在代码库中引入额外的语法。我可以用纯 C++ 实现这个吗?

如果预处理器在这种情况下无法提供帮助,您将如何使该结构定义更简单?

Hol*_*Cat 6

详细说明我的评论,这就是您使用我的macro_sequence_for.

run on gcc.godbolt.org

这需要将调用语法更改为:

DECL_CLASS( MyClass,
    (foo, int)
    (bar, float)
)
Run Code Online (Sandbox Code Playgroud)

这是实现:

#include <macro_sequence_for.h>
#include <type_traits>

#define DECL_CLASS(name_, members_) \
    struct name_ \
    { \
        SF_FOR_EACH(DETAIL_MEMBER_BODY, SF_NULL, SF_NULL,, members_) \
      public: \
        bool Init() \
        { \
            SF_FOR_EACH(DETAIL_MEMBER_INIT, SF_NULL, SF_NULL,, members_) \
            return true; \
        } \
    };

#define DETAIL_MEMBER_BODY(n, d, name_, .../*type*/) \
    ::std::type_identity_t<__VA_ARGS__> *name_ = nullptr;

#define DETAIL_MEMBER_INIT(n, d, name_, .../*type*/) \
    if (!(name_ = initValue<__VA_ARGS__>())) return false;
Run Code Online (Sandbox Code Playgroud)

这扩展到:

struct MyClass
{
    ::std::type_identity_t<int> *foo = nullptr;
    ::std::type_identity_t<float> *bar = nullptr;

  public:
    bool Init()
    {
        if (!(foo = initValue<int>()))
            return false;
        if (!(bar = initValue<float>()))
            return false;
        return true;
    }
};
Run Code Online (Sandbox Code Playgroud)

注意使用std::type_identity_t. 这让它可以与eg数组一起使用,因为egint[4] *foo;是无效的,但是type_identity_t<int[4]> *foo;很好。

注意(name, type)而不是(type, name)(并用于...类型)。这使您可以使用名称中带有逗号的类型,例如std::map<X, Y>.

没有我的图书馆,这也是同样的事情:

#define DECL_CLASS(name_, members_) \
    struct name_ \
    { \
        END(MEMBER_A members_) \
      public: \
        bool Init() \
        { \
            END(INIT_A members_) \
            return true; \
        } \
    };

#define END(...) END_(__VA_ARGS__)
#define END_(...) __VA_ARGS__##_END

#define MEMBER_A(...) MEMBER_BODY(__VA_ARGS__) MEMBER_B
#define MEMBER_B(...) MEMBER_BODY(__VA_ARGS__) MEMBER_A
#define MEMBER_A_END
#define MEMBER_B_END

#define MEMBER_BODY(name_, .../*type*/) \
    ::std::type_identity_t<__VA_ARGS__> *name_ = nullptr;

#define INIT_A(...) INIT_BODY(__VA_ARGS__) INIT_B
#define INIT_B(...) INIT_BODY(__VA_ARGS__) INIT_A
#define INIT_A_END
#define INIT_B_END

#define INIT_BODY(name_, .../*type*/) \
    if (!(name_ = initValue<__VA_ARGS__>())) return false;
Run Code Online (Sandbox Code Playgroud)