Using meta programming to select member variables

217 views Asked by At

I am trying to create a game save system using boost serialization, and want to create an easy way for clients to select member variables for serialization.

Basically I want the user to input something like this:

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


     SET_SAVED_MEMBERS(a, isTasty);
};

And I would like this to be expanded into something like

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

}

Of course, it is fine if it requires a few more macros than this.

I'm fine with template meta programming (preferably) or preprocessor meta programming.

C++11 is fine, too.

Thanks for the help!

1

There are 1 answers

2
Charles Ofria On BEST ANSWER

Previously I had a boost-focused answer that I wasn't able to test. Here's something more comprehensible using a combination of template meta-programming and macros:

#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__);    \
  }

Currently I have it just printing so I could test it (but indicate above the line you would need to change). Here is a test using your apple example:

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: ");
}

Note that I'm sending in a string instead of an archive object -- that works fine with the fact that it's currently using print.