1

I have a library (wrapper around nlohmann/json) that allows me to deserialize from JSON:

struct MyStruct {
    int propertyA;
    std::string propertyB;
    std::vector<int> propertyC;      
}

void from_json(const JSON::Node& json, MyStruct& s)
{
    json_get(s.propertyA, "propertyA", json);
    json_get(s.propertyB, "propertyB", json);
    json_get(s.propertyC, "propertyC", json);
}

As you can see, there is a lot of boiler plate in these definitions. I'm using an ECS framework that has hundreds of components I would like to deserialize. I'm hoping to simplify it with a macro such as:

struct MyStruct {
    int propertyA;
    std::string propertyB;
    std::vector<int> propertyC;    

    JSON(MyStruct, propertyA, propertyB, propertyC);
};

}

I'm aware of the oldschool __VA_ARGS__ approach with manually repeating the macro N times but I was hoping to avoid that with a more generic / modern method.

Is this possible with variadic templates? Is there a better way to get provide sort of syntactic sugar for this? I'm using a C++17 compiler.

kbirk
  • 3,906
  • 7
  • 48
  • 72
  • You might be out of luck. Templates can't really help you here. What you need is reflection, which C++ doesn't support (yet). So macros it is... – DeiDei Feb 09 '20 at 04:10
  • This is not possible in C++. This approach requires reflection, which C++ does not have. I typically implement things of this nature using an external XML or YAML specification of classes from which a script generates both a C++ declaration as well as the serialization/deserialization code. There is no law that says all C++ must be manually typed in. C++ code can also be script-generated. – Sam Varshavchik Feb 09 '20 at 04:23
  • I think this is possible. I did something similar with `REFL(MyStruct, REFL_DATA(propertyA) REFL_DATA(propertyB) REFL_DATA(propertyC));`. I think it might even be possible to unite the reflection registration with the class declaration (so you only need to name each member once). – Indiana Kernick Feb 09 '20 at 05:06

1 Answers1

2

Apparently there's a library that does exactly what you want! Here's a full example:

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

struct MyStruct {
  int propertyA;
  std::string propertyB;
  std::vector<int> propertyC;
};

VISITABLE_STRUCT(MyStruct, propertyA, propertyB, propertyC);

using nlohmann::json;

template <typename T>
std::enable_if_t<visit_struct::traits::is_visitable<std::decay_t<T>>::value>
from_json(const json &j, T &obj) {
  visit_struct::for_each(obj, [&](const char *name, auto &value) {
    // json_get(value, name, j);
    j.at(name).get_to(value);
  });
}

int main() {
  json j = json::parse(R"(
    {
      "propertyA": 42,
      "propertyB": "foo",
      "propertyC": [7]
    }
  )");
  MyStruct s = j.get<MyStruct>();
  std::cout << "PropertyA: " << s.propertyA << '\n';
  std::cout << "PropertyB: " << s.propertyB << '\n';
  std::cout << "PropertyC: " << s.propertyC[0] << '\n';
}
Indiana Kernick
  • 5,041
  • 2
  • 20
  • 50