add support for enum classes
This commit is contained in:
parent
8e43d476d3
commit
3d405c6883
2 changed files with 71 additions and 12 deletions
21
src/json.hpp
21
src/json.hpp
|
@ -122,6 +122,17 @@ using uncvref_t = remove_cv_t<remove_reference_t<T>>;
|
||||||
template <bool If, typename Then, typename Else>
|
template <bool If, typename Then, typename Else>
|
||||||
using conditional_t = typename std::conditional<If, Then, Else>::type;
|
using conditional_t = typename std::conditional<If, Then, Else>::type;
|
||||||
|
|
||||||
|
// Taken from http://stackoverflow.com/questions/26936640/how-to-implement-is-enum-class-type-trait
|
||||||
|
template <typename T>
|
||||||
|
using is_scoped_enum =
|
||||||
|
std::integral_constant<bool, not std::is_convertible<T, int>::value and
|
||||||
|
std::is_enum<T>::value>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using is_unscoped_enum =
|
||||||
|
std::integral_constant<bool, std::is_convertible<T, int>::value and
|
||||||
|
std::is_enum<T>::value>;
|
||||||
|
|
||||||
// TODO update this doc
|
// TODO update this doc
|
||||||
/*!
|
/*!
|
||||||
@brief unnamed namespace with internal helper functions
|
@brief unnamed namespace with internal helper functions
|
||||||
|
@ -130,6 +141,10 @@ using conditional_t = typename std::conditional<If, Then, Else>::type;
|
||||||
|
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
|
// Very useful construct against boilerplate (more boilerplate needed than in C++17: http://en.cppreference.com/w/cpp/types/void_t)
|
||||||
|
template <typename...> struct make_void { using type = void; };
|
||||||
|
template <typename... Ts> using void_t = typename make_void<Ts...>::type;
|
||||||
|
|
||||||
// Implementation of 3 C++17 constructs: conjunction, disjunction, negation.
|
// Implementation of 3 C++17 constructs: conjunction, disjunction, negation.
|
||||||
// This is needed to avoid evaluating all the traits in a condition
|
// This is needed to avoid evaluating all the traits in a condition
|
||||||
//
|
//
|
||||||
|
@ -277,6 +292,7 @@ template <typename T, typename BasicJson>
|
||||||
struct is_compatible_basic_json_type
|
struct is_compatible_basic_json_type
|
||||||
{
|
{
|
||||||
static auto constexpr value =
|
static auto constexpr value =
|
||||||
|
is_unscoped_enum<T>::value or
|
||||||
std::is_same<T, BasicJson>::value or
|
std::is_same<T, BasicJson>::value or
|
||||||
std::is_constructible<typename BasicJson::string_t, T>::value or
|
std::is_constructible<typename BasicJson::string_t, T>::value or
|
||||||
std::is_same<typename BasicJson::boolean_t, T>::value or
|
std::is_same<typename BasicJson::boolean_t, T>::value or
|
||||||
|
@ -1601,7 +1617,6 @@ class basic_json
|
||||||
not detail::is_compatible_basic_json_type<
|
not detail::is_compatible_basic_json_type<
|
||||||
uncvref_t<T>, basic_json_t>::value and
|
uncvref_t<T>, basic_json_t>::value and
|
||||||
not detail::is_basic_json_nested_class<uncvref_t<T>, basic_json_t, primitive_iterator_t>::value and
|
not detail::is_basic_json_nested_class<uncvref_t<T>, basic_json_t, primitive_iterator_t>::value and
|
||||||
not std::is_enum<uncvref_t<T>>::value and
|
|
||||||
not std::is_same<uncvref_t<T>, typename basic_json_t::array_t::iterator>::value and
|
not std::is_same<uncvref_t<T>, typename basic_json_t::array_t::iterator>::value and
|
||||||
not std::is_same<uncvref_t<T>, typename basic_json_t::object_t::iterator>::value and
|
not std::is_same<uncvref_t<T>, typename basic_json_t::object_t::iterator>::value and
|
||||||
detail::has_to_json<JSONSerializer, basic_json,
|
detail::has_to_json<JSONSerializer, basic_json,
|
||||||
|
@ -1774,8 +1789,8 @@ class basic_json
|
||||||
@since version 1.0.0
|
@since version 1.0.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Quickfix, accept every enum type, without looking if a to_json method is provided...
|
// Constructor for unscoped enums (not enum classes)
|
||||||
template <typename T, enable_if_t<std::is_enum<T>::value, int> = 0>
|
template <typename T, enable_if_t<is_unscoped_enum<T>::value, int> = 0>
|
||||||
basic_json(T val) noexcept
|
basic_json(T val) noexcept
|
||||||
: m_type(value_t::number_integer),
|
: m_type(value_t::number_integer),
|
||||||
m_value(static_cast<number_integer_t>(val))
|
m_value(static_cast<number_integer_t>(val))
|
||||||
|
|
|
@ -35,6 +35,13 @@ SOFTWARE.
|
||||||
|
|
||||||
namespace udt
|
namespace udt
|
||||||
{
|
{
|
||||||
|
enum class country
|
||||||
|
{
|
||||||
|
china,
|
||||||
|
france,
|
||||||
|
russia
|
||||||
|
};
|
||||||
|
|
||||||
struct age
|
struct age
|
||||||
{
|
{
|
||||||
int m_val;
|
int m_val;
|
||||||
|
@ -54,6 +61,7 @@ namespace udt
|
||||||
{
|
{
|
||||||
age m_age;
|
age m_age;
|
||||||
name m_name;
|
name m_name;
|
||||||
|
country m_country;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct contact
|
struct contact
|
||||||
|
@ -69,7 +77,7 @@ namespace udt
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// to_json methods for default basic_json
|
// to_json methods
|
||||||
namespace udt
|
namespace udt
|
||||||
{
|
{
|
||||||
void to_json(nlohmann::json& j, age a)
|
void to_json(nlohmann::json& j, age a)
|
||||||
|
@ -82,10 +90,26 @@ namespace udt
|
||||||
j = n.m_val;
|
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)
|
void to_json(nlohmann::json& j, person const& p)
|
||||||
{
|
{
|
||||||
using nlohmann::json;
|
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)
|
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
|
namespace udt
|
||||||
{
|
{
|
||||||
void from_json(nlohmann::json const& j, age &a)
|
void from_json(nlohmann::json const& j, age &a)
|
||||||
|
@ -152,10 +176,24 @@ namespace udt
|
||||||
n.m_val = j.get<std::string>();
|
n.m_val = j.get<std::string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void from_json(nlohmann::json const &j, country &c)
|
||||||
|
{
|
||||||
|
const auto str = j.get<std::string>();
|
||||||
|
static const std::map<std::string, country> 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)
|
void from_json(nlohmann::json const& j, person &p)
|
||||||
{
|
{
|
||||||
p.m_age = j["age"].get<age>();
|
p.m_age = j["age"].get<age>();
|
||||||
p.m_name = j["name"].get<name>();
|
p.m_name = j["name"].get<name>();
|
||||||
|
p.m_country = j["country"].get<country>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void from_json(nlohmann::json const &j, address &a)
|
void from_json(nlohmann::json const &j, address &a)
|
||||||
|
@ -183,29 +221,33 @@ TEST_CASE("basic usage", "[udt]")
|
||||||
// a bit narcissic maybe :) ?
|
// a bit narcissic maybe :) ?
|
||||||
const udt::age a{23};
|
const udt::age a{23};
|
||||||
const udt::name n{"theo"};
|
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::address addr{"Paris"};
|
||||||
const udt::contact cpp_programmer{sfinae_addict, addr};
|
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")
|
SECTION("conversion to json via free-functions")
|
||||||
{
|
{
|
||||||
CHECK(json(a) == json(23));
|
CHECK(json(a) == json(23));
|
||||||
CHECK(json(n) == json("theo"));
|
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("Paris") == json(addr));
|
||||||
CHECK(json(cpp_programmer) ==
|
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(
|
CHECK(
|
||||||
json(book) ==
|
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")
|
SECTION("conversion from json via free-functions")
|
||||||
{
|
{
|
||||||
const auto big_json =
|
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<udt::contact_book>();
|
const auto parsed_book = big_json.get<udt::contact_book>();
|
||||||
const auto book_name = big_json["name"].get<udt::name>();
|
const auto book_name = big_json["name"].get<udt::name>();
|
||||||
const auto contacts = big_json["contacts"].get<std::vector<udt::contact>>();
|
const auto contacts = big_json["contacts"].get<std::vector<udt::contact>>();
|
||||||
|
@ -214,10 +256,12 @@ TEST_CASE("basic usage", "[udt]")
|
||||||
const auto person = contact_json["person"].get<udt::person>();
|
const auto person = contact_json["person"].get<udt::person>();
|
||||||
const auto address = contact_json["address"].get<udt::address>();
|
const auto address = contact_json["address"].get<udt::address>();
|
||||||
const auto age = contact_json["person"]["age"].get<udt::age>();
|
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>();
|
const auto name = contact_json["person"]["name"].get<udt::name>();
|
||||||
|
|
||||||
CHECK(age == a);
|
CHECK(age == a);
|
||||||
CHECK(name == n);
|
CHECK(name == n);
|
||||||
|
CHECK(country == c);
|
||||||
CHECK(address == addr);
|
CHECK(address == addr);
|
||||||
CHECK(person == sfinae_addict);
|
CHECK(person == sfinae_addict);
|
||||||
CHECK(contact == cpp_programmer);
|
CHECK(contact == cpp_programmer);
|
||||||
|
|
Loading…
Add table
Reference in a new issue