【问题标题】:Using meta programming to select member variables使用元编程选择成员变量
【发布时间】:2015-06-19 02:40:49
【问题描述】:

我正在尝试使用 boost 序列化创建一个游戏保存系统,并希望为客户端创建一个简单的方法来选择序列化的成员变量。

基本上我希望用户输入如下内容:

class apple : public Actor
{
public:
     int a;
     bool isTasty;
     float unimportantData;


     SET_SAVED_MEMBERS(a, isTasty);
};

我希望将其扩展为类似

class apple : public Actor
{
public:
   // ...

   template<typename Archive>
   void serialize(Archive& arch, const unsigned int version)
   {
        arch & BOOST_SERIALIZATION_NVP(a);
        arch & BOOST_SERIALIZATION_NVP(isTasty);

}

当然,如果它需要比这更多的宏也没关系。

我对模板元编程(最好)或预处理器元编程很好。

C++11 也不错。

感谢您的帮助!

【问题讨论】:

  • 如果 BOOST_SERIALIZATION_NVP 是一个 c 预处理器宏,你就没有机会使用模板参数,因为宏的评估将在模板扩展之前完成。但我也不明白你的问题是什么。你只想手工写更少的代码吗?
  • 正确。不过我现在有一些想法。一旦我看到有人 typedef 一个 boost::mpl 容器,然后编写一个宏来定义一个函数,该函数使用 fusion 来遍历元素。这可能吗?
  • @Klaus 我不确定我是否理解为什么这不能用模板来完成......只要宏没有做任何特别奇怪的事情,它应该在模板定义中扩展,然后扩展会被应用多次,不是吗?
  • 是的,宏扩展是微不足道的。它将扩展到::boost::serialization::make_nvp("a", a);
  • 我想到了一个使用可变参数模板参数的解决方案,但这不起作用,因为宏在模板扩展之前扩展。而且在宏中不可能有来自 vars 的“名称”,因为使用 &lt;typename ... T&gt; void X(T...a ) 定义的所有参数都有名称 a,这在这个用例中没有帮助。

标签: c++ metaprogramming template-meta-programming


【解决方案1】:

以前我有一个无法测试的以提升为重点的答案。下面是使用模板元编程和宏的组合更容易理解的东西:

#include <iostream>

// Build a base template for the save Implementation
template <typename... ARG_TYPES>
struct save_impl;

// Create a DoSerialize function that will recursively serialize all objects.
template <typename Archive, typename FIRST_TYPE, typename... OTHER_TYPES>
struct save_impl<Archive, FIRST_TYPE, OTHER_TYPES...> {
  static void DoSerialize(Archive& arch, FIRST_TYPE & arg1, OTHER_TYPES&... others) {
    // Instead of printing in the next line, do:  arch & BOOST_SERIALIZATION_NVP(a);
    std::cout << arch << arg1 << std::endl;
    save_impl<Archive, OTHER_TYPES...>::DoSerialize(arch, others...);
  }
};

// Base case to end recursive call of struct-based DoSerialize
template <typename Archive>
struct save_impl<Archive> {
  static void DoSerialize(Archive& arch) { ; }
};

// Create a DoSerialize function that will call struct-based implementation.
template <typename Archive, typename... ARG_TYPES>
void DoSerialize(Archive & arch, ARG_TYPES&... args) {
  save_impl<Archive, ARG_TYPES...>::DoSerialize(arch, args...);
}

// Create the desired macro to add the serialize function to a class.
#define SET_SAVED_MEMBERS(...)         \
  template<typename Archive>           \
  void serialize(Archive& arch) {      \
    DoSerialize(arch, __VA_ARGS__);    \
  }

目前我只是打印它,所以我可以测试它(但在需要更改的行上方注明)。这是使用您的苹果示例进行的测试:

class apple {
public:
  int a;
  bool isTasty;
  float unimportantData;

  SET_SAVED_MEMBERS(a, isTasty);
};


int main() {
  apple a = {7, false, 2.34};
  a.isTasty=true;
  a.serialize("Archive: ");
}

请注意,我发送的是字符串而不是存档对象——这与它当前正在使用打印的事实很好地配合。

【讨论】:

  • 很好,将在选择正确答案之前进行测试。
  • 看起来不错。 +1,因为它是通过引用传递的。这对于加载方面很重要。非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-07-30
  • 2016-03-03
  • 2014-12-13
  • 1970-01-01
  • 1970-01-01
  • 2022-11-11
  • 1970-01-01
相关资源
最近更新 更多