Learn » Examples » Non-Intrusive Program Example 2

The full code for this magistrate example can be found here: examples/checkpoint_example_2_nonintrusive.cc

Example source code:

#include <checkpoint/checkpoint.h>

#include <cstdio>

// \brief Namespace containing types which will be serialized
namespace checkpoint { namespace nonintrusive { namespace examples {

// \struct MyTest2
// \brief Simple structure with one variable of built-in type
struct MyTest2 {
  int c = 41;

  // \brief Default constructor
  //
  // The reconstruction strategy is required for deserialization. A default
  // constructor is one of the reconstruction strategies that checkpoint will
  // look for.
  MyTest2() = default;

  // \brief Printing function unto the standard display
  void print() {
    printf("\t MyTest2: c=%d\n", c);
  }
};

// \struct MyTest
//
// \brief Structure with two variables of built-in types and one variable of
// custom type (`MyTest2`)
struct MyTest {
  int a = 29, b = 31;
  MyTest2 my_test_2;

  // \brief Default constructor
  //
  // The reconstruction strategy is required for deserialization. A default
  // constructor is one of the reconstruction strategies that checkpoint will
  // look for.
  MyTest() = default;

  // \brief Printing function unto the standard display
  void print() {
    printf("MyTest: a=%d, b=%d\n", a, b);
    my_test_2.print();
  }
};

}}} // end namespace checkpoint::nonintrusive::examples

// \brief In Non-Intrusive way, serialize function needs to be placed in the namespace
// of the type which will be serialized.
namespace checkpoint { namespace nonintrusive { namespace examples {

// \brief Templated function for serializing/deserializing
// a variable of type `MyTest`
//
// \tparam <Serializer> The type of serializer depending on the pass
// \param[in,out] s the serializer for traversing this class
//
// \note The serialize method is typically called three times when
// (de-)serializing to a byte buffer:
//
// 1) Sizing: The first time its called, it sizes all the data it recursively
// traverses to generate a final size for the buffer.
//
// 2) Packing: As the traversal occurs, it copies the data traversed to the
// byte buffer in the appropriate location.
//
// 3) Unpacking: As the byte buffer is traversed, it extracts the bytes from
// the buffer to recursively reconstruct the types and setup the class members.
//
template <typename Serializer>
void serialize(Serializer& s, MyTest& my_test) {
  printf("MyTest serialize\n");
  s | my_test.a;
  s | my_test.b;

  // Recursive dispatch to the `MyTest2` object
  s | my_test.my_test_2;
}

// \brief Templated function for serializing/deserializing
// a variable of type `MyTest2`
template <typename Serializer>
void serialize(Serializer& s, MyTest2& my_test2) {
  printf("MyTest2 serialize\n");
  s | my_test2.c;
}

}}} // end namespace checkpoint::nonintrusive::examples

int main(int, char**) {
  using namespace magistrate::nonintrusive::examples;

  // Define a variable of custom type `MyTest`
  MyTest my_test_inst;
  my_test_inst.a = 10;
  my_test_inst.print();

  // Call the serialization routine for the variable `my_test_inst`
  // The output is a unique pointer: `std::unique_ptr<SerializedInfo>`
  // (defined in `src/checkpoint_api.h`)
  auto ret = checkpoint::serialize(my_test_inst);

  {
    // Display information about the serialization "message"
    auto const& buf = ret->getBuffer();
    auto const& buf_size = ret->getSize();
    printf("ptr=%p, size=%ld\n", static_cast<void*>(buf), buf_size);
  }

  // De-serialization call to create a new unique pointer to `MyTest`
  auto t = checkpoint::deserialize<MyTest>(ret->getBuffer());
  t->print();

  return 0;
}