| // __ _____ _____ _____ |
| // __| | __| | | | JSON for Modern C++ (supporting code) |
| // | | |__ | | | | | | version 3.11.3 |
| // |_____|_____|_____|_|___| https://github.com/nlohmann/json |
| // |
| // SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me> |
| // SPDX-License-Identifier: MIT |
| |
| #include "doctest_compatibility.h" |
| |
| // disable -Wnoexcept due to class Evil |
| DOCTEST_GCC_SUPPRESS_WARNING_PUSH |
| DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") |
| |
| #include <nlohmann/json.hpp> |
| using nlohmann::json; |
| #ifdef JSON_TEST_NO_GLOBAL_UDLS |
| using namespace nlohmann::literals; // NOLINT(google-build-using-namespace) |
| #endif |
| |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| namespace udt |
| { |
| enum class country |
| { |
| china, |
| france, |
| russia |
| }; |
| |
| struct age |
| { |
| int m_val; |
| age(int rhs = 0) |
| : m_val(rhs) |
| {} |
| }; |
| |
| struct name |
| { |
| std::string m_val; |
| name(std::string rhs = "") |
| : m_val(std::move(rhs)) |
| {} |
| }; |
| |
| struct address |
| { |
| std::string m_val; |
| address(std::string rhs = "") |
| : m_val(std::move(rhs)) |
| {} |
| }; |
| |
| struct person |
| { |
| age m_age{}; |
| name m_name{}; |
| country m_country{}; |
| person() = default; |
| person(const age& a, name n, const country& c) |
| : m_age(a) |
| , m_name(std::move(n)) |
| , m_country(c) |
| {} |
| }; |
| |
| struct contact |
| { |
| person m_person{}; |
| address m_address{}; |
| contact() = default; |
| contact(person p, address a) |
| : m_person(std::move(p)) |
| , m_address(std::move(a)) |
| {} |
| }; |
| |
| struct contact_book |
| { |
| name m_book_name{}; |
| std::vector<contact> m_contacts{}; |
| contact_book() = default; |
| contact_book(name n, std::vector<contact> c) |
| : m_book_name(std::move(n)) |
| , m_contacts(std::move(c)) |
| {} |
| }; |
| } // namespace udt |
| |
| // to_json methods |
| namespace udt |
| { |
| // templates because of the custom_json tests (see below) |
| template<typename BasicJsonType> |
| static void to_json(BasicJsonType& j, age a) |
| { |
| j = a.m_val; |
| } |
| |
| template<typename BasicJsonType> |
| static void to_json(BasicJsonType& j, const name& n) |
| { |
| j = n.m_val; |
| } |
| |
| template<typename BasicJsonType> |
| static void to_json(BasicJsonType& j, country c) |
| { |
| switch (c) |
| { |
| case country::china: |
| j = "中华人民共和国"; |
| return; |
| case country::france: |
| j = "France"; |
| return; |
| case country::russia: |
| j = "Российская Федерация"; |
| return; |
| default: |
| break; |
| } |
| } |
| |
| template<typename BasicJsonType> |
| static void to_json(BasicJsonType& j, const person& p) |
| { |
| j = BasicJsonType{{"age", p.m_age}, {"name", p.m_name}, {"country", p.m_country}}; |
| } |
| |
| static void to_json(nlohmann::json& j, const address& a) |
| { |
| j = a.m_val; |
| } |
| |
| static void to_json(nlohmann::json& j, const contact& c) |
| { |
| j = json{{"person", c.m_person}, {"address", c.m_address}}; |
| } |
| |
| static void to_json(nlohmann::json& j, const contact_book& cb) |
| { |
| j = json{{"name", cb.m_book_name}, {"contacts", cb.m_contacts}}; |
| } |
| |
| // operators |
| static bool operator==(age lhs, age rhs) |
| { |
| return lhs.m_val == rhs.m_val; |
| } |
| |
| static bool operator==(const address& lhs, const address& rhs) |
| { |
| return lhs.m_val == rhs.m_val; |
| } |
| |
| static bool operator==(const name& lhs, const name& rhs) |
| { |
| return lhs.m_val == rhs.m_val; |
| } |
| |
| static bool operator==(const person& lhs, const person& rhs) |
| { |
| return std::tie(lhs.m_name, lhs.m_age) == std::tie(rhs.m_name, rhs.m_age); |
| } |
| |
| static bool operator==(const contact& lhs, const contact& rhs) |
| { |
| return std::tie(lhs.m_person, lhs.m_address) == std::tie(rhs.m_person, rhs.m_address); |
| } |
| |
| static bool operator==(const contact_book& lhs, const contact_book& rhs) |
| { |
| return std::tie(lhs.m_book_name, lhs.m_contacts) == std::tie(rhs.m_book_name, rhs.m_contacts); |
| } |
| } // namespace udt |
| |
| // from_json methods |
| namespace udt |
| { |
| template<typename BasicJsonType> |
| static void from_json(const BasicJsonType& j, age& a) |
| { |
| a.m_val = j.template get<int>(); |
| } |
| |
| template<typename BasicJsonType> |
| static void from_json(const BasicJsonType& j, name& n) |
| { |
| n.m_val = j.template get<std::string>(); |
| } |
| |
| template<typename BasicJsonType> |
| static void from_json(const BasicJsonType& j, country& c) |
| { |
| const auto str = j.template get<std::string>(); |
| const std::map<std::string, country> m = {{"中华人民共和国", country::china}, {"France", country::france}, {"Российская Федерация", country::russia}}; |
| |
| const auto it = m.find(str); |
| // TODO(nlohmann) test exceptions |
| c = it->second; |
| } |
| |
| template<typename BasicJsonType> |
| static void from_json(const BasicJsonType& j, person& p) |
| { |
| p.m_age = j["age"].template get<age>(); |
| p.m_name = j["name"].template get<name>(); |
| p.m_country = j["country"].template get<country>(); |
| } |
| |
| static void from_json(const nlohmann::json& j, address& a) |
| { |
| a.m_val = j.get<std::string>(); |
| } |
| |
| static void from_json(const nlohmann::json& j, contact& c) |
| { |
| c.m_person = j["person"].get<person>(); |
| c.m_address = j["address"].get<address>(); |
| } |
| |
| static void from_json(const nlohmann::json& j, contact_book& cb) |
| { |
| cb.m_book_name = j["name"].get<name>(); |
| cb.m_contacts = j["contacts"].get<std::vector<contact>>(); |
| } |
| } // namespace udt |
| |
| TEST_CASE("basic usage" * doctest::test_suite("udt")) |
| { |
| // a bit narcissistic maybe :) ? |
| const udt::age a{23}; |
| const udt::name n{"theo"}; |
| const udt::country c{udt::country::france}; |
| const udt::person sfinae_addict{a, n, c}; |
| const udt::person senior_programmer{{42}, {"王芳"}, udt::country::china}; |
| const udt::address addr{"Paris"}; |
| const udt::contact cpp_programmer{sfinae_addict, addr}; |
| const udt::contact_book book{{"C++"}, {cpp_programmer, {senior_programmer, addr}}}; |
| |
| SECTION("conversion to json via free-functions") |
| { |
| CHECK(json(a) == json(23)); |
| CHECK(json(n) == json("theo")); |
| CHECK(json(c) == json("France")); |
| CHECK(json(sfinae_addict) == R"({"name":"theo", "age":23, "country":"France"})"_json); |
| CHECK(json("Paris") == json(addr)); |
| CHECK(json(cpp_programmer) == R"({"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"})"_json); |
| |
| CHECK( |
| json(book) == |
| R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"中华人民共和国", "name":"王芳"}, "address":"Paris"}]})"_json); |
| } |
| |
| SECTION("conversion from json via free-functions") |
| { |
| const auto big_json = |
| R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"中华人民共和国", "name":"王芳"}, "address":"Paris"}]})"_json; |
| SECTION("via explicit calls to get") |
| { |
| const auto parsed_book = big_json.get<udt::contact_book>(); |
| const auto book_name = big_json["name"].get<udt::name>(); |
| const auto contacts = big_json["contacts"].get<std::vector<udt::contact>>(); |
| const auto contact_json = big_json["contacts"].at(0); |
| const auto contact = contact_json.get<udt::contact>(); |
| const auto person = contact_json["person"].get<udt::person>(); |
| const auto address = contact_json["address"].get<udt::address>(); |
| const auto age = contact_json["person"]["age"].get<udt::age>(); |
| const auto country = contact_json["person"]["country"].get<udt::country>(); |
| const auto name = contact_json["person"]["name"].get<udt::name>(); |
| |
| CHECK(age == a); |
| CHECK(name == n); |
| CHECK(country == c); |
| CHECK(address == addr); |
| CHECK(person == sfinae_addict); |
| CHECK(contact == cpp_programmer); |
| CHECK(contacts == book.m_contacts); |
| CHECK(book_name == udt::name{"C++"}); |
| CHECK(book == parsed_book); |
| } |
| |
| SECTION("via explicit calls to get_to") |
| { |
| udt::person person; |
| udt::name name; |
| |
| json person_json = big_json["contacts"][0]["person"]; |
| CHECK(person_json.get_to(person) == sfinae_addict); |
| |
| // correct reference gets returned |
| person_json["name"].get_to(name).m_val = "new name"; |
| CHECK(name.m_val == "new name"); |
| } |
| |
| #if JSON_USE_IMPLICIT_CONVERSIONS |
| SECTION("implicit conversions") |
| { |
| const udt::contact_book parsed_book = big_json; |
| const udt::name book_name = big_json["name"]; |
| const std::vector<udt::contact> contacts = big_json["contacts"]; |
| const auto contact_json = big_json["contacts"].at(0); |
| const udt::contact contact = contact_json; |
| const udt::person person = contact_json["person"]; |
| const udt::address address = contact_json["address"]; |
| const udt::age age = contact_json["person"]["age"]; |
| const udt::country country = contact_json["person"]["country"]; |
| const udt::name name = contact_json["person"]["name"]; |
| |
| CHECK(age == a); |
| CHECK(name == n); |
| CHECK(country == c); |
| CHECK(address == addr); |
| CHECK(person == sfinae_addict); |
| CHECK(contact == cpp_programmer); |
| CHECK(contacts == book.m_contacts); |
| CHECK(book_name == udt::name{"C++"}); |
| CHECK(book == parsed_book); |
| } |
| #endif |
| } |
| } |
| |
| namespace udt |
| { |
| struct legacy_type |
| { |
| std::string number{}; |
| legacy_type() = default; |
| legacy_type(std::string n) |
| : number(std::move(n)) |
| {} |
| }; |
| } // namespace udt |
| |
| namespace nlohmann |
| { |
| template<typename T> |
| struct adl_serializer<std::shared_ptr<T>> |
| { |
| static void to_json(json& j, const std::shared_ptr<T>& opt) |
| { |
| if (opt) |
| { |
| j = *opt; |
| } |
| else |
| { |
| j = nullptr; |
| } |
| } |
| |
| static void from_json(const json& j, std::shared_ptr<T>& opt) |
| { |
| if (j.is_null()) |
| { |
| opt = nullptr; |
| } |
| else |
| { |
| opt.reset(new T(j.get<T>())); // NOLINT(cppcoreguidelines-owning-memory) |
| } |
| } |
| }; |
| |
| template<> |
| struct adl_serializer<udt::legacy_type> |
| { |
| static void to_json(json& j, const udt::legacy_type& l) |
| { |
| j = std::stoi(l.number); |
| } |
| |
| static void from_json(const json& j, udt::legacy_type& l) |
| { |
| l.number = std::to_string(j.get<int>()); |
| } |
| }; |
| } // namespace nlohmann |
| |
| TEST_CASE("adl_serializer specialization" * doctest::test_suite("udt")) |
| { |
| SECTION("partial specialization") |
| { |
| SECTION("to_json") |
| { |
| std::shared_ptr<udt::person> optPerson; |
| |
| json j = optPerson; |
| CHECK(j.is_null()); |
| |
| optPerson.reset(new udt::person{{42}, {"John Doe"}, udt::country::russia}); // NOLINT(cppcoreguidelines-owning-memory,modernize-make-shared) |
| j = optPerson; |
| CHECK_FALSE(j.is_null()); |
| |
| CHECK(j.get<udt::person>() == *optPerson); |
| } |
| |
| SECTION("from_json") |
| { |
| auto person = udt::person{{42}, {"John Doe"}, udt::country::russia}; |
| json j = person; |
| |
| auto optPerson = j.get<std::shared_ptr<udt::person>>(); |
| REQUIRE(optPerson); |
| CHECK(*optPerson == person); |
| |
| j = nullptr; |
| optPerson = j.get<std::shared_ptr<udt::person>>(); |
| CHECK(!optPerson); |
| } |
| } |
| |
| SECTION("total specialization") |
| { |
| SECTION("to_json") |
| { |
| udt::legacy_type const lt{"4242"}; |
| |
| json const j = lt; |
| CHECK(j.get<int>() == 4242); |
| } |
| |
| SECTION("from_json") |
| { |
| json const j = 4242; |
| auto lt = j.get<udt::legacy_type>(); |
| CHECK(lt.number == "4242"); |
| } |
| } |
| } |
| |
| namespace nlohmann |
| { |
| template<> |
| struct adl_serializer<std::vector<float>> |
| { |
| using type = std::vector<float>; |
| static void to_json(json& j, const type& /*type*/) |
| { |
| j = "hijacked!"; |
| } |
| |
| static void from_json(const json& /*unnamed*/, type& opt) |
| { |
| opt = {42.0, 42.0, 42.0}; |
| } |
| |
| // preferred version |
| static type from_json(const json& /*unnamed*/) |
| { |
| return {4.0, 5.0, 6.0}; |
| } |
| }; |
| } // namespace nlohmann |
| |
| TEST_CASE("even supported types can be specialized" * doctest::test_suite("udt")) |
| { |
| json const j = std::vector<float>{1.0, 2.0, 3.0}; |
| CHECK(j.dump() == R"("hijacked!")"); |
| auto f = j.get<std::vector<float>>(); |
| // the single argument from_json method is preferred |
| CHECK((f == std::vector<float>{4.0, 5.0, 6.0})); |
| } |
| |
| namespace nlohmann |
| { |
| template<typename T> |
| struct adl_serializer<std::unique_ptr<T>> |
| { |
| static void to_json(json& j, const std::unique_ptr<T>& opt) |
| { |
| if (opt) |
| { |
| j = *opt; |
| } |
| else |
| { |
| j = nullptr; |
| } |
| } |
| |
| // this is the overload needed for non-copyable types, |
| static std::unique_ptr<T> from_json(const json& j) |
| { |
| if (j.is_null()) |
| { |
| return nullptr; |
| } |
| |
| return std::unique_ptr<T>(new T(j.get<T>())); |
| } |
| }; |
| } // namespace nlohmann |
| |
| TEST_CASE("Non-copyable types" * doctest::test_suite("udt")) |
| { |
| SECTION("to_json") |
| { |
| std::unique_ptr<udt::person> optPerson; |
| |
| json j = optPerson; |
| CHECK(j.is_null()); |
| |
| optPerson.reset(new udt::person{{42}, {"John Doe"}, udt::country::russia}); // NOLINT(cppcoreguidelines-owning-memory,modernize-make-unique) |
| j = optPerson; |
| CHECK_FALSE(j.is_null()); |
| |
| CHECK(j.get<udt::person>() == *optPerson); |
| } |
| |
| SECTION("from_json") |
| { |
| auto person = udt::person{{42}, {"John Doe"}, udt::country::russia}; |
| json j = person; |
| |
| auto optPerson = j.get<std::unique_ptr<udt::person>>(); |
| REQUIRE(optPerson); |
| CHECK(*optPerson == person); |
| |
| j = nullptr; |
| optPerson = j.get<std::unique_ptr<udt::person>>(); |
| CHECK(!optPerson); |
| } |
| } |
| |
| // custom serializer - advanced usage |
| // pack structs that are pod-types (but not scalar types) |
| // relies on adl for any other type |
| template<typename T, typename = void> |
| struct pod_serializer |
| { |
| // use adl for non-pods, or scalar types |
| template<typename BasicJsonType, typename U = T, typename std::enable_if<!(std::is_pod<U>::value && std::is_class<U>::value), int>::type = 0> |
| static void from_json(const BasicJsonType& j, U& t) |
| { |
| using nlohmann::from_json; |
| from_json(j, t); |
| } |
| |
| // special behaviour for pods |
| template<typename BasicJsonType, typename U = T, typename std::enable_if<std::is_pod<U>::value && std::is_class<U>::value, int>::type = 0> |
| static void from_json(const BasicJsonType& j, U& t) |
| { |
| std::uint64_t value = 0; |
| // 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<std::uint64_t>() ? |
| // 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<char*>(static_cast<void*>(&value)); // NOLINT(bugprone-casting-through-void) |
| std::memcpy(&t, bytes, sizeof(value)); |
| } |
| |
| template<typename BasicJsonType, typename U = T, typename std::enable_if<!(std::is_pod<U>::value && std::is_class<U>::value), int>::type = 0> |
| static void to_json(BasicJsonType& j, const T& t) |
| { |
| using nlohmann::to_json; |
| to_json(j, t); |
| } |
| |
| template<typename BasicJsonType, typename U = T, typename std::enable_if<std::is_pod<U>::value && std::is_class<U>::value, int>::type = 0> |
| static void to_json(BasicJsonType& j, const T& t) noexcept |
| { |
| const auto* bytes = static_cast<const unsigned char*>(static_cast<const void*>(&t)); // NOLINT(bugprone-casting-through-void) |
| std::uint64_t value = 0; |
| std::memcpy(&value, bytes, sizeof(value)); |
| nlohmann::to_json(j, value); |
| } |
| }; |
| |
| namespace udt |
| { |
| struct small_pod |
| { |
| int begin; |
| char middle; |
| short end; |
| }; |
| |
| struct non_pod |
| { |
| std::string s{}; |
| non_pod() = default; |
| non_pod(std::string S) |
| : s(std::move(S)) |
| {} |
| }; |
| |
| template<typename BasicJsonType> |
| static void to_json(BasicJsonType& j, const non_pod& np) |
| { |
| j = np.s; |
| } |
| |
| template<typename BasicJsonType> |
| static void from_json(const BasicJsonType& j, non_pod& np) |
| { |
| np.s = j.template get<std::string>(); |
| } |
| |
| static bool operator==(small_pod lhs, small_pod rhs) noexcept |
| { |
| return std::tie(lhs.begin, lhs.middle, lhs.end) == std::tie(rhs.begin, rhs.middle, rhs.end); |
| } |
| |
| static bool operator==(const non_pod& lhs, const non_pod& rhs) noexcept |
| { |
| return lhs.s == rhs.s; |
| } |
| |
| static std::ostream& operator<<(std::ostream& os, small_pod l) |
| { |
| return os << "begin: " << l.begin << ", middle: " << l.middle << ", end: " << l.end; |
| } |
| } // namespace udt |
| |
| TEST_CASE("custom serializer for pods" * doctest::test_suite("udt")) |
| { |
| using custom_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, double, std::allocator, pod_serializer>; |
| |
| auto p = udt::small_pod{42, '/', 42}; |
| custom_json const j = p; |
| |
| auto p2 = j.get<udt::small_pod>(); |
| |
| CHECK(p == p2); |
| |
| auto np = udt::non_pod{{"non-pod"}}; |
| custom_json const j2 = np; |
| auto np2 = j2.get<udt::non_pod>(); |
| CHECK(np == np2); |
| } |
| |
| template<typename T, typename> |
| struct another_adl_serializer; |
| |
| using custom_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, double, std::allocator, another_adl_serializer>; |
| |
| template<typename T, typename> |
| struct another_adl_serializer |
| { |
| static void from_json(const custom_json& j, T& t) |
| { |
| using nlohmann::from_json; |
| from_json(j, t); |
| } |
| |
| static void to_json(custom_json& j, const T& t) |
| { |
| using nlohmann::to_json; |
| to_json(j, t); |
| } |
| }; |
| |
| TEST_CASE("custom serializer that does adl by default" * doctest::test_suite("udt")) |
| { |
| auto me = udt::person{{23}, {"theo"}, udt::country::france}; |
| |
| json const j = me; |
| custom_json const cj = me; |
| |
| CHECK(j.dump() == cj.dump()); |
| |
| CHECK(me == j.get<udt::person>()); |
| CHECK(me == cj.get<udt::person>()); |
| } |
| |
| TEST_CASE("different basic_json types conversions") |
| { |
| SECTION("null") |
| { |
| json const j; |
| custom_json cj = j; |
| CHECK(cj == nullptr); |
| } |
| |
| SECTION("boolean") |
| { |
| json const j = true; |
| custom_json cj = j; |
| CHECK(cj == true); |
| } |
| |
| SECTION("discarded") |
| { |
| json const j(json::value_t::discarded); |
| custom_json cj; |
| CHECK_NOTHROW(cj = j); |
| CHECK(cj.type() == custom_json::value_t::discarded); |
| } |
| |
| SECTION("array") |
| { |
| json const j = {1, 2, 3}; |
| custom_json const cj = j; |
| CHECK((cj == std::vector<int>{1, 2, 3})); |
| } |
| |
| SECTION("integer") |
| { |
| json const j = 42; |
| custom_json cj = j; |
| CHECK(cj == 42); |
| } |
| |
| SECTION("float") |
| { |
| json const j = 42.0; |
| custom_json cj = j; |
| CHECK(cj == 42.0); |
| } |
| |
| SECTION("unsigned") |
| { |
| json const j = 42u; |
| custom_json cj = j; |
| CHECK(cj == 42u); |
| } |
| |
| SECTION("string") |
| { |
| json const j = "forty-two"; |
| custom_json cj = j; |
| CHECK(cj == "forty-two"); |
| } |
| |
| SECTION("binary") |
| { |
| json j = json::binary({1, 2, 3}, 42); |
| custom_json cj = j; |
| CHECK(cj.get_binary().subtype() == 42); |
| std::vector<std::uint8_t> cv = cj.get_binary(); |
| std::vector<std::uint8_t> v = j.get_binary(); |
| CHECK(cv == v); |
| } |
| |
| SECTION("object") |
| { |
| json const j = {{"forty", "two"}}; |
| custom_json cj = j; |
| auto m = j.get<std::map<std::string, std::string>>(); |
| CHECK(cj == m); |
| } |
| |
| SECTION("get<custom_json>") |
| { |
| json const j = 42; |
| custom_json cj = j.get<custom_json>(); |
| CHECK(cj == 42); |
| } |
| } |
| |
| namespace |
| { |
| struct incomplete; |
| |
| // std::is_constructible is broken on macOS' libc++ |
| // use the cppreference implementation |
| |
| template<typename T, typename = void> |
| struct is_constructible_patched : std::false_type |
| {}; |
| |
| template<typename T> |
| struct is_constructible_patched<T, decltype(void(json(std::declval<T>())))> : std::true_type |
| {}; |
| } // namespace |
| |
| TEST_CASE("an incomplete type does not trigger a compiler error in non-evaluated context" * doctest::test_suite("udt")) |
| { |
| static_assert(!is_constructible_patched<json, incomplete>::value, ""); |
| } |
| |
| namespace |
| { |
| class Evil |
| { |
| public: |
| Evil() = default; |
| template<typename T> |
| Evil(T t) |
| : m_i(sizeof(t)) |
| { |
| static_cast<void>(t); // fix MSVC's C4100 warning |
| } |
| |
| int m_i = 0; |
| }; |
| |
| void from_json(const json& /*unused*/, Evil& /*unused*/) {} |
| } // namespace |
| |
| TEST_CASE("Issue #924") |
| { |
| // Prevent get<std::vector<Evil>>() to throw |
| auto j = json::array(); |
| |
| CHECK_NOTHROW(j.get<Evil>()); |
| CHECK_NOTHROW(j.get<std::vector<Evil>>()); |
| |
| // silence Wunused-template warnings |
| Evil e(1); |
| CHECK(e.m_i >= 0); |
| } |
| |
| TEST_CASE("Issue #1237") |
| { |
| struct non_convertible_type |
| {}; |
| static_assert(!std::is_convertible<json, non_convertible_type>::value, ""); |
| } |
| |
| namespace |
| { |
| class no_iterator_type |
| { |
| public: |
| no_iterator_type(std::initializer_list<int> l) |
| : _v(l) |
| {} |
| |
| std::vector<int>::const_iterator begin() const |
| { |
| return _v.begin(); |
| } |
| |
| std::vector<int>::const_iterator end() const |
| { |
| return _v.end(); |
| } |
| |
| private: |
| std::vector<int> _v; |
| }; |
| } // namespace |
| |
| TEST_CASE("compatible array type, without iterator type alias") |
| { |
| no_iterator_type const vec{1, 2, 3}; |
| json const j = vec; |
| } |
| |
| DOCTEST_GCC_SUPPRESS_WARNING_POP |