diff --git a/test/src/unit-udt.cpp b/test/src/unit-udt.cpp index bb150e76..60b83957 100644 --- a/test/src/unit-udt.cpp +++ b/test/src/unit-udt.cpp @@ -391,27 +391,27 @@ TEST_CASE("adl_serializer specialization", "[udt]") namespace nlohmann { - // TODO provide a real example, since this works now :) -// template -// struct adl_serializer> -// { -// static void to_json(json& j, std::vector const& opt) -// { -// -// } -// -// static void from_json(json const& j, std::vector& opt) -// { -// } -// }; +template <> +struct adl_serializer> +{ + static void to_json(json& j, std::vector const&) + { + j = "hijacked!"; + } + + static void from_json(json const&, std::vector& opt) + { + opt = {42.0, 42.0, 42.0}; + } +}; } -TEST_CASE("current supported types are preferred over specializations", "[udt]") +TEST_CASE("even supported types can be specialized", "[udt]") { - - json j = std::vector {1, 2, 3}; - auto f = j.get>(); - CHECK((f == std::vector {1, 2, 3})); + json j = std::vector {1.0, 2.0, 3.0}; + CHECK(j.dump() == R"("hijacked!")"); + auto f = j.get>(); + CHECK((f == std::vector{42.0, 42.0, 42.0})); } namespace nlohmann @@ -432,7 +432,6 @@ struct adl_serializer> } // this is the overload needed for non-copyable types, - // should we add a priority tag in the implementation to prefer this overload if it exists? static std::unique_ptr from_json(json const& j) { if (j.is_null()) @@ -456,7 +455,7 @@ TEST_CASE("Non-copyable types", "[udt]") json j = optPerson; CHECK(j.is_null()); - optPerson.reset(new udt::person{{42}, {"John Doe"}}); + optPerson.reset(new udt::person{{42}, {"John Doe"}, udt::country::russia}); j = optPerson; CHECK_FALSE(j.is_null()); @@ -465,7 +464,7 @@ TEST_CASE("Non-copyable types", "[udt]") SECTION("from_json") { - auto person = udt::person{{42}, {"John Doe"}}; + auto person = udt::person{{42}, {"John Doe"}, udt::country::russia}; json j = person; auto optPerson = j.get>(); @@ -478,39 +477,70 @@ TEST_CASE("Non-copyable types", "[udt]") } } -// custom serializer -// advanced usage (I did not have a real use case in mind) -template ::value>::type> +// custom serializer - advanced usage +// pack structs that are pod-types (but not scalar types) +// relies on adl for any other type +template struct pod_serializer { - template - static void from_json(Json const& j , T& t) - { - std::uint64_t value; - // Why cannot we simply use: j.get() ? - // Well, with the current experiment, the get method looks for a from_json function, which we are currently defining! - // This would end up in a stack overflow. Calling nlohmann::from_json is a workaround. - // I shall find a good way to avoid this once all constructors are converted to free methods - // - // In short, constructing a json by constructor calls to_json - // calling get calls from_json, for now, we cannot do this in custom serializers - nlohmann::from_json(j, value); - auto bytes = static_cast(static_cast(&value)); - std::memcpy(&t, bytes, sizeof(value)); - } + // use adl for non-pods, or scalar types + template < + typename Json, typename U = T, + typename std::enable_if< + not(std::is_pod::value and std::is_class::value), int>::type = 0> + static void from_json(Json const &j, U &t) + { + using nlohmann::from_json; + from_json(j, t); + } - template - static void to_json(Json& j, T const& t) - { - auto bytes = static_cast(static_cast(&t)); - std::uint64_t value = bytes[0]; - for (auto i = 1; i < 8; ++i) - { - value |= bytes[i] << 8 * i; - } - // same thing here - nlohmann::to_json(j, value); - } + // special behaviour for pods + template ::value and std::is_class::value, int>::type = 0> + static void from_json(Json const &j, U &t) + { + std::uint64_t value; + // TODO The following block is no longer relevant in this serializer, make another one that shows the issue + // the problem arises only when one from_json method is defined without any constraint + // + // Why cannot we simply use: j.get() ? + // Well, with the current experiment, the get method looks for a from_json + // function, which we are currently defining! + // This would end up in a stack overflow. Calling nlohmann::from_json is a + // workaround (is it?). + // I shall find a good way to avoid this once all constructors are converted + // to free methods + // + // In short, constructing a json by constructor calls to_json + // calling get calls from_json, for now, we cannot do this in custom + // serializers + nlohmann::from_json(j, value); + auto bytes = static_cast(static_cast(&value)); + std::memcpy(&t, bytes, sizeof(value)); + } + + template < + typename Json, typename U = T, + typename std::enable_if< + not(std::is_pod::value and std::is_class::value), int>::type = 0> + static void to_json(Json &j, T const &t) + { + using nlohmann::to_json; + to_json(j, t); + } + + template ::value and std::is_class::value, int>::type = 0> + static void to_json(Json &j, T const &t) noexcept + { + auto bytes = static_cast(static_cast(&t)); + std::uint64_t value = bytes[0]; + for (auto i = 1; i < 8; ++i) + value |= std::uint64_t{bytes[i]} << 8 * i; + nlohmann::to_json(j, value); + } }; namespace udt @@ -522,23 +552,58 @@ struct small_pod short end; }; -bool operator==(small_pod lhs, small_pod rhs) +struct non_pod +{ + std::string s; +}; + +template +void to_json(Json& j, non_pod const& np) +{ + j = np.s; +} + +template +void from_json(Json const& j, non_pod& np) +{ + np.s = j.template get(); +} + +bool operator==(small_pod lhs, small_pod rhs) noexcept { return std::tie(lhs.begin, lhs.middle, lhs.end) == - std::tie(lhs.begin, lhs.middle, lhs.end); + std::tie(rhs.begin, rhs.middle, rhs.end); +} + +bool operator==(non_pod const &lhs, non_pod const &rhs) noexcept +{ + return lhs.s == rhs.s; +} + +std::ostream& operator<<(std::ostream& os, small_pod l) +{ + return os << "begin: " << l.begin << ", middle: " << l.middle << ", end: " << l.end; } } TEST_CASE("custom serializer for pods", "[udt]") { - using custom_json = nlohmann::basic_json; + using custom_json = + nlohmann::basic_json; - auto p = udt::small_pod{42, '/', 42}; - custom_json j = p; + auto p = udt::small_pod{42, '/', 42}; + custom_json j = p; - auto p2 = j.get(); + auto p2 = j.get(); - CHECK(p == p2); + CHECK(p == p2); + + auto np = udt::non_pod{{"non-pod"}}; + custom_json j2 = np; + auto np2 = j2.get(); + CHECK(np == np2); } template