diff --git a/src/json.hpp b/src/json.hpp index 35d3d0a6..e6034029 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -122,6 +122,17 @@ using uncvref_t = remove_cv_t>; template using conditional_t = typename std::conditional::type; +// Taken from http://stackoverflow.com/questions/26936640/how-to-implement-is-enum-class-type-trait +template +using is_scoped_enum = + std::integral_constant::value and + std::is_enum::value>; + +template +using is_unscoped_enum = + std::integral_constant::value and + std::is_enum::value>; + // TODO update this doc /*! @brief unnamed namespace with internal helper functions @@ -130,6 +141,10 @@ using conditional_t = typename std::conditional::type; namespace detail { +// Very useful construct against boilerplate (more boilerplate needed than in C++17: http://en.cppreference.com/w/cpp/types/void_t) +template struct make_void { using type = void; }; +template using void_t = typename make_void::type; + // Implementation of 3 C++17 constructs: conjunction, disjunction, negation. // This is needed to avoid evaluating all the traits in a condition // @@ -277,6 +292,7 @@ template struct is_compatible_basic_json_type { static auto constexpr value = + is_unscoped_enum::value or std::is_same::value or std::is_constructible::value or std::is_same::value or @@ -1601,7 +1617,6 @@ class basic_json not detail::is_compatible_basic_json_type< uncvref_t, basic_json_t>::value and not detail::is_basic_json_nested_class, basic_json_t, primitive_iterator_t>::value and - not std::is_enum>::value and not std::is_same, typename basic_json_t::array_t::iterator>::value and not std::is_same, typename basic_json_t::object_t::iterator>::value and detail::has_to_json::value, int> = 0> + // Constructor for unscoped enums (not enum classes) + template ::value, int> = 0> basic_json(T val) noexcept : m_type(value_t::number_integer), m_value(static_cast(val)) diff --git a/test/src/unit-udt.cpp b/test/src/unit-udt.cpp index 81fae3ee..df0e47dd 100644 --- a/test/src/unit-udt.cpp +++ b/test/src/unit-udt.cpp @@ -35,6 +35,13 @@ SOFTWARE. namespace udt { + enum class country + { + china, + france, + russia + }; + struct age { int m_val; @@ -54,6 +61,7 @@ namespace udt { age m_age; name m_name; + country m_country; }; struct contact @@ -69,7 +77,7 @@ namespace udt }; } -// to_json methods for default basic_json +// to_json methods namespace udt { void to_json(nlohmann::json& j, age a) @@ -82,10 +90,26 @@ namespace udt j = n.m_val; } + void to_json(nlohmann::json& j, country c) + { + switch (c) + { + case country::china: + j = u8"中华人民共和国"; + return; + case country::france: + j = "France"; + return; + case country::russia: + j = u8"Российская Федерация"; + return; + } + } + void to_json(nlohmann::json& j, person const& p) { using nlohmann::json; - j = json{{"age", p.m_age}, {"name", p.m_name}}; + j = json{{"age", p.m_age}, {"name", p.m_name}, {"country", p.m_country}}; } void to_json(nlohmann::json& j, address const& a) @@ -139,7 +163,7 @@ namespace udt } } -// from_json methods for default basic_json +// from_json methods namespace udt { void from_json(nlohmann::json const& j, age &a) @@ -152,10 +176,24 @@ namespace udt n.m_val = j.get(); } + void from_json(nlohmann::json const &j, country &c) + { + const auto str = j.get(); + static const std::map m = { + {u8"中华人民共和国", country::china}, + {"France", country::france}, + {"Российская Федерация", country::russia}}; + + const auto it = m.find(str); + // TODO test exceptions + c = it->second; + } + void from_json(nlohmann::json const& j, person &p) { p.m_age = j["age"].get(); p.m_name = j["name"].get(); + p.m_country = j["country"].get(); } void from_json(nlohmann::json const &j, address &a) @@ -183,29 +221,33 @@ TEST_CASE("basic usage", "[udt]") // a bit narcissic maybe :) ? const udt::age a{23}; const udt::name n{"theo"}; - const udt::person sfinae_addict{a, n}; + const udt::country c{udt::country::france}; + const udt::person sfinae_addict{a, n, c}; + const udt::person senior_programmer{{42}, {u8"王芳"}, udt::country::china}; const udt::address addr{"Paris"}; const udt::contact cpp_programmer{sfinae_addict, addr}; - const udt::contact_book book{{"C++"}, {cpp_programmer, cpp_programmer}}; + 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(sfinae_addict) == R"({"name":"theo", "age":23})"_json); + 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"}, "address":"Paris"})"_json); + R"({"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"})"_json); CHECK( json(book) == - R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo"}, "address":"Paris"}, {"person" : {"age":23, "name":"theo"}, "address":"Paris"}]})"_json); + 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"}, "address":"Paris"}, {"person" : {"age":23, "name":"theo"}, "address":"Paris"}]})"_json; + R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"中华人民共和国", "name":"王芳"}, "address":"Paris"}]})"_json; const auto parsed_book = big_json.get(); const auto book_name = big_json["name"].get(); const auto contacts = big_json["contacts"].get>(); @@ -214,10 +256,12 @@ TEST_CASE("basic usage", "[udt]") const auto person = contact_json["person"].get(); const auto address = contact_json["address"].get(); const auto age = contact_json["person"]["age"].get(); + const auto country = contact_json["person"]["country"].get(); const auto name = contact_json["person"]["name"].get(); CHECK(age == a); CHECK(name == n); + CHECK(country == c); CHECK(address == addr); CHECK(person == sfinae_addict); CHECK(contact == cpp_programmer);