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
783 stars 63 forks source link

Compile error when using MSVC and template struct #108

Closed AlexZhu2001 closed 1 month ago

AlexZhu2001 commented 1 month ago

Hi, I received some compilation errors when using MSVC with the following test code.

#include <string>
#include <rfl.hpp>
#include <rfl/json.hpp>

template <typename T>
struct A
{
    int code;
    std::string msg;
    T data;
};

struct B
{
    int some_val;
};

std::string test_case = R"(
{
    "code": 0,
    "msg": "hello",
    "data": {
        "some_val": 1
    }
}
)";

int main()
{
    auto resp = rfl::json::read<A<B>>(test_case).value();
    std::cout << "{" << std::endl
              << "  code: " << resp.code << std::endl
              << "  msg: " << resp.msg << std::endl
              << "  data: " << "{" << std::endl
              << "    some_val: " << resp.data.some_val << std::endl
              << "  }" << std::endl
              << "}" << std::endl;
}

This code works fine when using clang and mingw. The msvc version is 19.29.30153 for x64 The compiled output is as follows

  reflectcpp.vcxproj -> E:\testproj\build-msvc\reflect-cpp\Debug\reflectcpp.lib
  main.cpp
E:\testproj\reflect-cpp\include\rfl\Literal.hpp(341,41): error C2338: Duplicate strings are not allowed in a Literal. [E:\testproj\build-msvc\main.vcxproj]
E:\testproj\reflect-cpp\include\rfl\internal/get_field_names.hpp(101): message : see reference to class template instantiation 'rfl::Literal<rfl::internal::StringLiteral<16>{std::array<char,16>{_Ty101,116,95,102,105,101,108,100,95,110,97,109,101,95,115,0}},rfl::internal::StringLiteral<16>{std::array<_Ty,16>{_Ty101,116,95,102,105,101,108,100,95,110,97,109,101,95,115,0}}>' being compiled [E:\testproj\build-msvc\main.vcxproj]
          with
          [
              _Ty=char
          ]
E:\testproj\reflect-cpp\include\rfl\internal/get_field_names.hpp(106): message : see reference to function template instantiation 'auto rfl::internal::concat_two_literals<rfl::internal::StringLiteral<16>{std::array<char,16>{_Ty101,116,95,102,105,101,108,100,95,110,97,109,101,95,115,0}},rfl::internal::StringLiteral<16>{std::array<_Ty,16>{_Ty101,116,95,102,105,101,108,100,95,110,97,109,101,95,115,0}}>(const rfl::Literal<rfl::internal::StringLiteral<16>{std::array<_Ty,16>{_Ty101,116,95,102,105,101,108,100,95,110,97,109,101,95,115,0}}> &,const rfl::Literal<rfl::internal::StringLiteral<16>{std::array<_Ty,16>{_Ty101,116,95,102,105,101,108,100,95,110,97,109,101,95,115,0}}> &)' being compiled [E:\testproj\build-msvc\main.vcxproj]
          with
          [
              _Ty=char
          ]
E:\testproj\reflect-cpp\include\rfl\internal/get_field_names.hpp(106): message : see reference to function template instantiation 'auto rfl::internal::concat_literals<rfl::Literal<rfl::internal::StringLiteral<16>{std::array<char,16>{_Ty101,116,95,102,105,101,108,100,95,110,97,109,101,95,115,0}}>,rfl::Literal<rfl::internal::StringLiteral<16>{std::array<_Ty,16>{_Ty101,116,95,102,105,101,108,100,95,110,97,109,101,95,115,0}}>>(const Head &,const rfl::Literal<rfl::internal::StringLiteral<16>{std::array<_Ty,16>{_Ty101,116,95,102,105,101,108,100,95,110,97,109,101,95,115,0}}> &)' being compiled [E:\testproj\build-msvc\main.vcxproj]
          with
          [
              _Ty=char,
              Head=rfl::Literal<rfl::internal::StringLiteral<16>{std::array<char,16>{char101,116,95,102,105,101,108,100,95,110,97,109,101,95,115,0}}>
          ]
E:\testproj\reflect-cpp\include\rfl\internal/get_field_names.hpp(144): message : see reference to function template instantiation 'auto rfl::internal::concat_literals<rfl::Literal<rfl::internal::StringLiteral<16>{std::array<char,16>{_Ty101,116,95,102,105,101,108,100,95,110,97,109,101,95,115,0}}>,rfl::Literal<rfl::internal::StringLiteral<16>{std::array<_Ty,16>{_Ty101,116,95,102,105,101,108,100,95,110,97,109,101,95,115,0}}>,rfl::Literal<rfl::internal::StringLiteral<16>{std::array<_Ty,16>{_Ty101,116,95,102,105,101,108,100,95,110,97,109,101,95,115,0}}>>(const Head &,const rfl::Literal<rfl::internal::StringLiteral<16>{std::array<_Ty,16>{_Ty101,116,95,102,105,101,108,100,95,110,97,109,101,95,115,0}}> &,const rfl::Literal<rfl::internal::StringLiteral<16>{std::array<_Ty,16>{_Ty101,116,95,102,105,101,108,100,95,110,97,109,101,95,115,0}}> &)' being compiled [E:\testproj\build-msvc\main.vcxproj]
          with
          [
              _Ty=char,
              Head=rfl::Literal<rfl::internal::StringLiteral<16>{std::array<char,16>{char101,116,95,102,105,101,108,100,95,110,97,109,101,95,115,0}}>
          ]
E:\testproj\reflect-cpp\include\rfl\internal/get_field_names.hpp(146): message : see reference to function template instantiation 'auto rfl::internal::get_field_names::<lambda_1>::operator ()<0,1,2>(std::integer_sequence<size_t,0,1,2>) const' being compiled [E:\testproj\build-msvc\main.vcxproj]
E:\testproj\reflect-cpp\include\rfl\internal\to_ptr_named_tuple.hpp(70): message : see reference to function template instantiation 'auto rfl::internal::get_field_names<A<B>>(void)' being compiled [E:\testproj\build-msvc\main.vcxproj]
E:\testproj\reflect-cpp\include\rfl\internal\to_ptr_named_tuple.hpp(70): message : see reference to alias template instantiation 'rfl::field_names_t<A<B>&>' being compiled [E:\testproj\build-msvc\main.vcxproj]
E:\testproj\reflect-cpp\include\rfl\parsing\../to_view.hpp(14): message : see reference to function template instantiation 'auto rfl::internal::to_ptr_named_tuple<T&>(A<B>&)' being compiled [E:\testproj\build-msvc\main.vcxproj]
          with
          [
              T=A<B>
          ]
E:\testproj\reflect-cpp\include\rfl\parsing\StructReader.hpp(28): message : see reference to function template instantiation 'auto rfl::to_view<StructType>(T &)' being compiled [E:\testproj\build-msvc\main.vcxproj]
          with
          [
              StructType=A<B>,
              T=A<B>
          ]
E:\testproj\reflect-cpp\include\rfl\parsing\StructReader.hpp(25): message : while compiling class template member function 'rfl::Result<A<B>> rfl::parsing::StructReader<R,W,T,ProcessorsType>::read(const R &,const rfl::json::Reader::YYJSONInputVar &)' [E:\testproj\build-msvc\main.vcxproj]
          with
          [
              R=rfl::json::Reader,
              W=rfl::json::Writer,
              T=A<B>,
              ProcessorsType=rfl::Processors<>
          ]
E:\testproj\reflect-cpp\include\rfl\parsing\Parser_default.hpp(58): message : see reference to function template instantiation 'rfl::Result<A<B>> rfl::parsing::StructReader<R,W,T,ProcessorsType>::read(const R &,const rfl::json::Reader::YYJSONInputVar &)' being compiled [E:\testproj\build-msvc\main.vcxproj]
          with
          [
              R=rfl::json::Reader,
              W=rfl::json::Writer,
              T=A<B>,
              ProcessorsType=rfl::Processors<>
          ]
E:\testproj\reflect-cpp\include\rfl\parsing\Parser_default.hpp(58): message : see reference to class template instantiation 'rfl::parsing::StructReader<R,W,T,ProcessorsType>' being compiled [E:\testproj\build-msvc\main.vcxproj]
          with
          [
              R=rfl::json::Reader,
              W=rfl::json::Writer,
              T=A<B>,
              ProcessorsType=rfl::Processors<>
          ]
E:\testproj\reflect-cpp\include\rfl\parsing\Parser_default.hpp(42): message : while compiling class template member function 'rfl::Result<A<B>> rfl::parsing::Parser<rfl::json::Reader,rfl::json::Writer,T,rfl::Processors<>>::read(const R &,const rfl::json::Reader::YYJSONInputVar &) noexcept' [E:\testproj\build-msvc\main.vcxproj]
          with
          [
              T=A<B>,
              R=rfl::json::Reader
          ]
E:\testproj\reflect-cpp\include\rfl\json\read.hpp(24): message : see reference to function template instantiation 'rfl::Result<A<B>> rfl::parsing::Parser<rfl::json::Reader,rfl::json::Writer,T,rfl::Processors<>>::read(const R &,const rfl::json::Reader::YYJSONInputVar &) noexcept' being compiled [E:\testproj\build-msvc\main.vcxproj]
          with
          [
              T=A<B>,
              R=rfl::json::Reader
          ]
E:\testproj\reflect-cpp\include\rfl\json\read.hpp(22): message : see reference to class template instantiation 'rfl::parsing::Parser<rfl::json::Reader,rfl::json::Writer,T,rfl::Processors<>>' being compiled [E:\testproj\build-msvc\main.vcxproj]
          with
          [
              T=A<B>
          ]
E:\testproj\reflect-cpp\include\rfl\json\read.hpp(29): message : see reference to function template instantiation 'rfl::Result<A<B>> rfl::json::read<T,>(const rfl::json::InputVarType &)' being compiled [E:\testproj\build-msvc\main.vcxproj]
          with
          [
              T=A<B>
          ]
E:\testproj\main.cpp(30): message : see reference to function template instantiation 'rfl::Result<A<B>> rfl::json::read<A<B>,>(const std::string &)' being compiled [E:\testproj\build-msvc\main.vcxproj]
AlexZhu2001 commented 1 month ago

By the way, it seems that only this version of MSVC will have this error, while version 19.38.33133 of MSVC does not seem to have any errors.

liuzicheng1987 commented 1 month ago

This is very interesting, thanks for the issue. What is clearly happening is that get_field_name_str_view is not doing what it is supposed to be doing:

https://github.com/getml/reflect-cpp/blob/main/include/rfl/internal/get_field_names.hpp

I will try to reproduce the problem.

liuzicheng1987 commented 1 month ago

Hi @AlexZhu2001 , so the reason it doesn't compile with 19.29.30153 is that the version for the compiler is simply too old. It doesn't have anything to do with the templating.

As we clearly state in our README, reflect-cpp requires at least MSVC 17.8, which corresponds to version 19.38. (MSVC versioning numbering is very confusing...) As you have correctly observed, it works for 19.38.

Take this simple example program:

#include <source_location>
#include <string_view>
#include <iostream>
#include <string>

template <class T, auto ptr>
consteval auto get_field_name_str_view() {
  const auto func_name = std::string_view{std::source_location::current().function_name()};
  const auto split = func_name.substr(0, func_name.size() - 7);
  return split.substr(split.rfind("->") + 2);
}

struct Test{int a;};

static constexpr auto test = Test{};

consteval auto get_f1() {
  auto& [f1] = test;
  return &f1;
}

int main(int argc, const char** argv) {  
  constexpr auto ptr = get_f1();
  constexpr auto str = get_field_name_str_view<Test, ptr>();
  static_assert(get_field_name_str_view<Test, &test.a>() == "a", "Failed");
  std::cout << str << std::endl;
  return 0;
}

You can insert it in godbolt.org...it compiles with 19.38, no problem. But it will fail with 19.29.

liuzicheng1987 commented 1 month ago

@AlexZhu2001, I have added your example as a test:

https://github.com/getml/reflect-cpp/blob/f/templates/tests/json/test_template.cpp

As you can see, the Github Action pipelines run through for all compilers, including MSVC:

https://github.com/getml/reflect-cpp/commits/f/templates/

So I would argue that this is simply an issue with an unsupported compiler.

But thank you for opening this issue. At the very least, we got another test out of it.

AlexZhu2001 commented 1 month ago

Okay, the version number of MSVC is really confusing. I will upgrade to the corresponding supported version.