getml / reflect-cpp

A C++20 library for fast serialization, deserialization and validation using reflection. Supports JSON, BSON, CBOR, flexbuffers, msgpack, TOML, XML, YAML / msgpack.org[C++20]
https://getml.github.io/reflect-cpp/
MIT License
932 stars 78 forks source link

Library fails on default-constructible types #1

Closed riidefi closed 11 months ago

riidefi commented 11 months ago
:\Users\rii\Documents\dev\RiiStudio\source\vendor\rfl\internal\to_field_tuple.hpp(58,15): error : type 'const librii::g3d::JSONModel' decomposes into 3 elements, but only 1 name was provided
          auto& [f1] = _t;
                ^
  C:\Users\rii\Documents\dev\RiiStudio\source\vendor\rfl\to_named_tuple.hpp(45,38): note: in instantiation of function template specialization 'rfl::internal::to_field_tuple<librii::g3d::JSONModel>' requested here
          auto field_tuple = internal::to_field_tuple(_t);
                                       ^
  C:\Users\rii\Documents\dev\RiiStudio\source\vendor\rfl\parsing\Parser.hpp(95,38): note: in instantiation of function template specialization 'rfl::to_named_tuple<librii::g3d::JSONModel>' requested here
              const auto named_tuple = to_named_tuple(_var);
                                       ^
  C:\Users\rii\Documents\dev\RiiStudio\source\vendor\rfl\json\write.hpp(18,38): note: in instantiation of member function 'rfl::parsing::Parser<rfl::json::Reader, rfl::json::Writer, librii::g3d::JSONModel>::write' requested here
      const auto json_obj = Parser<T>::write(w, _obj);
                                       ^
  C:\Users\rii\Documents\dev\RiiStudio\source\librii\g3d\io\JSON.cpp(111,30): note: in instantiation of function template specialization 'rfl::json::write<librii::g3d::JSONModel>' requested here
    std::string s = rfl::json::write(homer);

In this case, this was the code

 struct JSONModel {
  rfl::Field<"name", std::string> name;
  rfl::Field<"info", JSONModelInfo> info = JSONModelInfo();
  // rfl::Field<"bones", std::vector<BoneData>> bones;
  // rfl::Field<"positions", std::vector<PositionBuffer>> positions;
  // rfl::Field<"normals", std::vector<NormalBuffer>> normals;
  // rfl::Field<"colors", std::vector<ColorBuffer>> colors;
  // rfl::Field<"texcoords", std::vector<TextureCoordinateBuffer>> texcoords;
  // rfl::Field<"materials", std::vector<G3dMaterialData>> materials;
  // rfl::Field<"meshes", std::vector<PolygonData>> meshes;
  rfl::Field<"matrices", std::vector<JSONDrawMatrix>> matrices =
      std::vector<JSONDrawMatrix>();

  static JSONModel make(const Model& original) {
    JSONModel model{.name = "bruh"};
    model.name = original.name;
    model.info = original.info;
    for (const auto& matrix : original.matrices) {
      model.matrices.value().emplace_back(matrix);
    }
    return model;
  }

  operator Model() const {
    Model result;
    result.name = name();
    result.info = info();
    for (const auto& jsonMatrix : matrices()) {
      result.matrices.push_back(static_cast<DrawMatrix>(jsonMatrix));
    }
    return result;
  }
};
void test() {
  JSONModel homer{.name = "bruh"};
  std::string s = rfl::json::write(homer);
}

Similarly having name be default set causes issues too:

struct JSONModel {
  rfl::Field<"name", std::string> name = std::string();
  rfl::Field<"info", JSONModelInfo> info = JSONModelInfo();
  // rfl::Field<"bones", std::vector<BoneData>> bones;
  // rfl::Field<"positions", std::vector<PositionBuffer>> positions;
  // rfl::Field<"normals", std::vector<NormalBuffer>> normals;
  // rfl::Field<"colors", std::vector<ColorBuffer>> colors;
  // rfl::Field<"texcoords", std::vector<TextureCoordinateBuffer>> texcoords;
  // rfl::Field<"materials", std::vector<G3dMaterialData>> materials;
  // rfl::Field<"meshes", std::vector<PolygonData>> meshes;
  rfl::Field<"matrices", std::vector<JSONDrawMatrix>> matrices =
      std::vector<JSONDrawMatrix>();

  static JSONModel make(const Model& original) {
    JSONModel model;
    model.name = original.name;
    model.info = original.info;
    for (const auto& matrix : original.matrices) {
      model.matrices.value().emplace_back(matrix);
    }
    return model;
  }

  operator Model() const {
    Model result;
    result.name = name();
    result.info = info();
    for (const auto& jsonMatrix : matrices()) {
      result.matrices.push_back(static_cast<DrawMatrix>(jsonMatrix));
    }
    return result;
  }
};

void test() {
  JSONModel homer;
  std::string s = rfl::json::write(homer);
}

s is written as "{}" since it is assumed to have zero fields given it is itself default-constructible.

Compiler: Clang 16.0.5 Target: x64 Windows Host: Windows 11 x64

liuzicheng1987 commented 11 months ago

Thank you for your input.

I was actually thinking about this problem just a couple of days ago. I think there is a simple fix to this. I'll implement it tonight and will let you know.

liuzicheng1987 commented 11 months ago

Here, this should fix your issue:

https://github.com/getml/reflect-cpp/commit/b67a2fababc97847fb3d49a3e2777b4b16b62b8a

I couldn't reproduce the example you have provided, because there were some classes you didn't define, but I have written my own test for this:

  struct Person {
      rfl::Field<"firstName", std::string> first_name;
      rfl::Field<"lastName", std::string> last_name = "Simpson";
      rfl::Field<"children", std::vector<Person>> children =
          rfl::default_value;
  };

  const auto bart = Person{.first_name = "Bart"};

  write_and_read(
      bart,
      "{\"firstName\":\"Bart\",\"lastName\":\"Simpson\",\"children\":[]}");

The test proves that setting default values in the way you proposed works and we get the expected outcome.

I will also add documentation for this.

Thanks again for your input.

riidefi commented 11 months ago

Thanks for the swift reply. That seems to have resolved one issue, although I am now running into another. I'll open another issue.