Dump structs to disk

Useful little helper using visit_struct and json to save and load data. Short example:

#include <string>
#include <vector>

#include "helpers.h"

struct OtherStruct
{
  int a=2;
  std::string b="world";
  std::vector<int> c = {1,2,3};
};
VISITABLE_STRUCT(OtherStruct, a, b, c);

struct ExampleStruct
{
  enum class EC {A, B, C};
  int a = 1;
  double b = 3.14;
  std::string c = "hello";
  OtherStruct d;
  std::vector<OtherStruct> e;
  EC f = EC::A;
  std::vector<std::string> g = {"one","two"};
};
VISITABLE_STRUCT(ExampleStruct, a, b, c, d, e, f, g);


int main(int argc, char* argv[])
{
  ExampleStruct A;
  A.a = 2;
  A.d.a = 3;
  std::string path = "test.json";
  serialize(A, path.c_str());
  ExampleStruct B;
  deserialize(B, path.c_str());
  if(struct_eq(A,B))
  {
    printf("serialize--derserialize success!\n");
  }
  return 0;
}

Generates the following output:

{
  "a": 2,
  "b": 3.14,
  "c": "hello",
  "d": {
    "a": 3,
    "b": "world",
    "c": [
      1,
      2,
      3
    ]
  },
  "f": 0,
  "g": [
    "one",
    "two"
  ]
}

and here is the template magic…

#ifndef SERIALIZE_DESERIALIZE_H
#define SERIALIZE_DESERIALIZE_H

#include <nlohmann/json.hpp>
#include <visit_struct/visit_struct.hpp>
#include <iostream>
#include <fstream>
#include <type_traits>

template<typename>
struct is_std_vector : std::false_type {};

template<typename T, typename A>
struct is_std_vector<std::vector<T,A>> : std::true_type {};


inline void write_json(nlohmann::json& data, const char* jsonPath)
{
  std::fstream of(jsonPath, std::fstream::out);
  if(!of.is_open())
  {
    return;
  }
  of << std::setw(2) << data << std::endl;
}

inline bool read_json(nlohmann::json& data, const char* jsonPath)
{
  std::ifstream jsonFile(jsonPath);
  if(jsonFile.is_open())
  {
    jsonFile >> data;
    return true;
  }
  return false;
}

// ===================================================================
// Deserialize

template <typename T>
void deserialize(T& out, const nlohmann::json& data);

template <typename T>
std::enable_if_t<!visit_struct::traits::is_visitable<std::decay_t<T>>::value && !is_std_vector<std::decay_t<T>>::value>
HandleStructElementDeserialize(T& value, const std::string& name, const nlohmann::json& data)
{
  data.at(name).get_to(value);
}

template <typename T>
std::enable_if_t<visit_struct::traits::is_visitable<std::decay_t<T>>::value>
HandleStructElementDeserialize(T& value, const std::string& name, const nlohmann::json& data)
{
  deserialize(value, data[name]);
}

template <typename T>
std::enable_if_t<!visit_struct::traits::is_visitable<std::decay_t<T>>::value>
HandleStructElementDeserializeIx(T& value, const std::string& name, size_t ix, const nlohmann::json& data)
{
  data.at(name).at(ix).get_to(value);
}

template <typename T>
std::enable_if_t<visit_struct::traits::is_visitable<std::decay_t<T>>::value>
HandleStructElementDeserializeIx(T& value, const std::string& name, size_t ix, const nlohmann::json& data)
{
  deserialize(value, data[name][ix]);
}

template <typename T>
void
HandleStructElementDeserialize(std::vector<T>& value, const std::string& name, const nlohmann::json& data)
{
  value.resize(data[name].size());
  for(size_t ii = 0; ii < value.size(); ++ii)
  {
    HandleStructElementDeserializeIx(value[ii], name, ii, data);
  }
}

// -------------------------------------------------------------------
template <typename T>
void deserialize(T& out, const nlohmann::json& data)
{
  visit_struct::for_each(out,
                         [data](const char * name, auto & value)
                           {
                             if(data.contains(name))
                             {
                               HandleStructElementDeserialize(value, name, data);
                             }
                           });
}
template <typename T>
void deserialize(T& out, const char* jsonPath)
{
  using json = nlohmann::json;
  json data;
  read_json(data, jsonPath);
  deserialize(out, data);
}

// ===================================================================
// Serialize

template <typename T>
void serialize(const T& out, nlohmann::json& data);

template <typename T>
std::enable_if_t<!visit_struct::traits::is_visitable<std::decay_t<T>>::value && !is_std_vector<std::decay_t<T>>::value>
 HandleStructElement(const T& e, const std::string& name, nlohmann::json& data)
{
  data[name] = e;
}

template <typename T>
std::enable_if_t<visit_struct::traits::is_visitable<std::decay_t<T>>::value>
HandleStructElement(const T& e, const std::string& name, nlohmann::json& data)
{
  serialize(e, data[name]);
}

template <typename T>
std::enable_if_t<!visit_struct::traits::is_visitable<std::decay_t<T>>::value>
HandleStructElementIx(const T& e, const std::string& name, size_t ix, nlohmann::json& data)
{
  data[name][ix] = e;
}
template <typename T>
std::enable_if_t<visit_struct::traits::is_visitable<std::decay_t<T>>::value>
HandleStructElementIx(const T& e, const std::string& name, size_t ix, nlohmann::json& data)
{
  serialize(e, data[name][ix]);
}

template <typename T>
void
HandleStructElement(const std::vector<T>& e, const std::string& name, nlohmann::json& data)
{
  for(size_t ii = 0; ii < e.size(); ++ii)
  {
    HandleStructElementIx(e[ii], name, ii, data);
  }
}

// -------------------------------------------------------------------
template <typename T>
void serialize(const T& out, nlohmann::json& data)
{
  visit_struct::for_each(out,
                         [&data](const char * name, const auto & value)
                           {
                             HandleStructElement(value, name, data);
                           });
}
template <typename T>
void serialize(const T& out, const char* jsonPath)
{
  using json = nlohmann::json;
  json data;
  serialize(out, data);
  write_json(data, jsonPath);
}

// ===================================================================
// Compare

template <typename T>
bool struct_eq(const T & t1, const T & t2);

struct eq_visitor {
  bool result = true;

  template <typename T>
  std::enable_if_t<!visit_struct::traits::is_visitable<std::decay_t<T>>::value && !is_std_vector<std::decay_t<T>>::value>
  operator()(const char *, const T & t1, const T & t2) {
    result = result && (t1 == t2);
  }
  template <typename T>
  std::enable_if_t<visit_struct::traits::is_visitable<std::decay_t<T>>::value>
  operator()(const char *, const T & t1, const T & t2) {
    result = result && struct_eq(t1, t2);
  }

  template <typename T>
  std::enable_if_t<!visit_struct::traits::is_visitable<std::decay_t<T>>::value>
  cmp(const T & t1, const T & t2) {
    result = result && (t1 == t2);
  }
  template <typename T>
  std::enable_if_t<visit_struct::traits::is_visitable<std::decay_t<T>>::value>
  cmp(const T & t1, const T & t2) {
    result = result && struct_eq(t1, t2);
  }

  template <typename T>
  void
  operator()(const char *, const std::vector<T> & t1, const std::vector<T> & t2) {
    if(t1.size() != t2.size())
    {
      result = result && false;
    }
    else
    {
      for(size_t ii = 0; ii < t1.size(); ++ii)
      {
        //result = result && cmp(t1, t2);
        cmp(t1[ii], t2[ii]);
      }
    }
  }
};

template <typename T>
bool struct_eq(const T & t1, const T & t2) {
  eq_visitor vis;
  visit_struct::for_each(t1, t2, vis);
  return vis.result;
}

template <typename T>
void copy_visitable_fields(const T& src, T& dst)
{
    visit_struct::for_each(src, dst, [](const char*, const auto& s, auto& d) {
        d = s;
    });
}
// ===================================================================

#endif