yosh-matsuda / cpp-yyjson

Ultra-fast and intuitive C++ JSON reader/writer with yyjson backend
MIT License
63 stars 6 forks source link

yyjson::cast throws error when user-define structs aren't in the same file where yyjson::cast is #5

Closed Nukoooo closed 1 year ago

Nukoooo commented 1 year ago

Let's say I define my user-define structs in net_message.hpp and define yyjson::cast template in net_message.cpp, if I call yyjson::cast<struct>(*obj) in processor.cpp, it will throw no matching function for call to ‘yyjson::reader::object::cast<MyStruct>() const’

Some code to reproduce:

// in net_message.hpp
struct Base
{
  std::optional<std::string> Type{};
  std::optional<std::uint32_t> WorldId{};
   std::optional<std::uint32_t> TerritoryId{};
  std::optional<std::int32_t> InstanceId{};
};

// in net_message.cpp
template <>
struct yyjson::caster<Base>
{
    template <typename Json>
    static Base from_json(const Json& json)
    {
        if (const auto obj = json.as_object(); obj.has_value())
        {
            auto result = Base();
            for (const auto& kv : *obj)
            {
                if (kv.first == "Type")
                {
                    result.Type = yyjson::cast<decltype(result.Type)>(kv.second);
                    continue;
                }

                if (kv.first == "WorldId")
                {
                    result.WorldId = yyjson::cast<decltype(result.WorldId)>(kv.second);
                    continue;
                }

                if (kv.first == "TerritoryId")
                {
                    result.TerritoryId = yyjson::cast<decltype(result.TerritoryId)>(kv.second);
                    continue;
                }

                if (kv.first == "InstanceId")
                {
                    result.InstanceId = yyjson::cast<decltype(result.InstanceId)>(kv.second);
                }
            }
            return result;
        }
        throw bad_cast(fmt::format("{} is not constructible from JSON", NAMEOF_TYPE(Base)));
    }
};

// in processor.cpp
const auto obj = yyjson::read(message).as_object();
if (!obj.has_value())
{
  return;
}
const auto msg = yyjson::cast<Base>(obj.value()); // < throws "no matching function for call to ‘yyjson::reader::object::cast<Base>() const"

Edit: Code is compiled with C++20 Same error on C++23

yosh-matsuda commented 1 year ago

@Nukoooo

Can you move yyjson::caster<Base> from net_message.cpp to net_message.hpp or processor.cpp? The location of the definition of the yyjson::cast seems to be different from the location where it is used.

I checked that the above code can be compiled if the whole is put in the same file.

Nukoooo commented 1 year ago

It works after moving yyjson::caster<Base> to net_message.hpp or processor.cpp