wip: add get_impl overload that uses free from_json method

This commit is contained in:
Théo DELRIEU 2016-10-18 13:36:43 +02:00 committed by Théo DELRIEU
parent f805dfcbbf
commit 733e95f4ae
2 changed files with 100 additions and 12 deletions

View File

@ -91,6 +91,14 @@ SOFTWARE.
*/ */
namespace nlohmann namespace nlohmann
{ {
// TODO add real documentation before PR
// Traits structure declaration, users can specialize it for their own types
//
// constructing a json object from a user-defined type will call the
// 'json to_json(T)' function
//
// whereas calling json::get<T> will call 'T from_json(json const&)'
template <typename T, typename = void> template <typename T, typename = void>
struct json_traits; struct json_traits;
@ -125,6 +133,7 @@ struct has_mapped_type
}; };
// taken from http://stackoverflow.com/questions/10711952/how-to-detect-existence-of-a-class-using-sfinae // taken from http://stackoverflow.com/questions/10711952/how-to-detect-existence-of-a-class-using-sfinae
// used to determine if json_traits is defined for a given type T
template <typename T> template <typename T>
struct has_destructor struct has_destructor
{ {
@ -184,6 +193,7 @@ struct DecimalSeparator : std::numpunct<char>
} }
// taken from ranges-v3 // taken from ranges-v3
// TODO add doc
template <typename T> template <typename T>
struct __static_const struct __static_const
{ {
@ -1280,6 +1290,20 @@ class basic_json
assert_invariant(); assert_invariant();
} }
// constructor chosen if json_traits is specialized for type T
// note: constructor is marked explicit to avoid the following issue:
//
// struct not_equality_comparable{};
//
// not_equality_comparable{} == not_equality_comparable{};
//
// this will construct implicitely 2 json objects and call operator== on them
// which can cause nasty bugs on the user's in json-unrelated code
//
// the trade-off is expressivety in initializer-lists
// auto j = json{{"a", json(not_equality_comparable{})}};
//
// we can remove this constraint though, since lots of ctor are not explicit already
template < template <
typename T, typename T,
typename = typename =
@ -2647,10 +2671,13 @@ class basic_json
// value access // // value access //
////////////////// //////////////////
// get_impl overload chosen if json_traits struct is specialized for type T
// simply returns json_traits<T>::from_json(*this);
// TODO add alias templates (enable_if_t etc)
template < template <
typename T, typename T,
typename = typename = typename std::enable_if<
typename std::enable_if<detail::has_json_traits<typename std::remove_cv< detail::has_json_traits<typename std::remove_cv<
typename std::remove_reference<T>::type>::type>::value>::type> typename std::remove_reference<T>::type>::type>::value>::type>
auto get_impl(T *) const -> decltype( auto get_impl(T *) const -> decltype(
json_traits<typename std::remove_cv<typename std::remove_reference< json_traits<typename std::remove_cv<typename std::remove_reference<
@ -2659,6 +2686,24 @@ class basic_json
typename std::remove_reference<T>::type>::type>::from_json(*this); typename std::remove_reference<T>::type>::type>::from_json(*this);
} }
// this one is quite atrocious
// this overload is chosen ONLY if json_traits struct is not specialized, and if the expression nlohmann::from_json(*this, T&) is valid
// I chose to prefer the json_traits specialization if it exists, since it's a more advanced use.
// But we can of course change this behaviour
template <typename T>
auto get_impl(T *) const -> typename std::enable_if<
not detail::has_json_traits<typename std::remove_cv<T>::type>::value,
typename std::remove_cv<typename std::remove_reference<
decltype(::nlohmann::from_json(std::declval<basic_json>(),
std::declval<T &>()),
std::declval<T>())>::type>::type>::type
{
typename std::remove_cv<typename std::remove_reference<T>::type>::type
ret;
::nlohmann::from_json(*this, ret);
return ret;
}
/// get an object (explicit) /// get an object (explicit)
template <class T, template <class T,
typename std::enable_if< typename std::enable_if<

View File

@ -68,6 +68,11 @@ private:
std::shared_ptr<T> _val; std::shared_ptr<T> _val;
}; };
struct no_json_traits_type
{
int a;
};
// free to/from_json functions // free to/from_json functions
json to_json(empty_type) json to_json(empty_type)
@ -86,6 +91,22 @@ json to_json(bit_more_complex_type const& p)
return json{{"a", to_json(p.a)}, {"b", to_json(p.b)}, {"c", p.c}}; return json{{"a", to_json(p.a)}, {"b", to_json(p.b)}, {"c", p.c}};
} }
template <typename T>
json to_json(optional_type<T> const& opt)
{
using nlohmann::to_json;
if (!opt)
return nullptr;
return to_json(*opt);
}
json to_json(no_json_traits_type const& p)
{
json ret;
ret["a"] = p.a;
return ret;
}
void from_json(json const&j, empty_type& t) void from_json(json const&j, empty_type& t)
{ {
assert(j.empty()); assert(j.empty());
@ -104,6 +125,11 @@ void from_json(json const&j, bit_more_complex_type& t)
j["c"].get<std::string>()}; j["c"].get<std::string>()};
} }
void from_json(json const& j, no_json_traits_type& t)
{
t.a = j["a"].get<int>();
}
template <typename T> template <typename T>
void from_json(json const& j, optional_type<T>& t) void from_json(json const& j, optional_type<T>& t)
{ {
@ -113,15 +139,6 @@ void from_json(json const& j, optional_type<T>& t)
t = j.get<T>(); t = j.get<T>();
} }
template <typename T>
json to_json(optional_type<T> const& opt)
{
using nlohmann::to_json;
if (!opt)
return nullptr;
return to_json(*opt);
}
inline bool operator==(pod_type const& lhs, pod_type const& rhs) noexcept inline bool operator==(pod_type const& lhs, pod_type const& rhs) noexcept
{ {
return std::tie(lhs.a, lhs.b, lhs.c) == std::tie(rhs.a, rhs.b, rhs.c); return std::tie(lhs.a, lhs.b, lhs.c) == std::tie(rhs.a, rhs.b, rhs.c);
@ -141,6 +158,11 @@ inline bool operator==(optional_type<T> const& lhs, optional_type<T> const& rhs)
return false; return false;
return *lhs == *rhs; return *lhs == *rhs;
} }
inline bool operator==(no_json_traits_type const& lhs, no_json_traits_type const& rhs)
{
return lhs.a == rhs.a;
}
} }
namespace nlohmann namespace nlohmann
@ -402,6 +424,16 @@ TEST_CASE("to_json free function", "[udt]")
CHECK(expected == j); CHECK(expected == j);
} }
} }
SECTION("no json_traits specialization")
{
udt::no_json_traits_type t{42};
json expected;
expected["a"] = 42;
auto const j = nlohmann::to_json(t);
CHECK(j == expected);
}
} }
TEST_CASE("from_json free function", "[udt]") TEST_CASE("from_json free function", "[udt]")
@ -411,7 +443,6 @@ TEST_CASE("from_json free function", "[udt]")
auto const expected = udt::pod_type{42, 42, 42}; auto const expected = udt::pod_type{42, 42, 42};
auto const j = json{{"a", 42}, {"b", 42}, {"c", 42}}; auto const j = json{{"a", 42}, {"b", 42}, {"c", 42}};
// i really dislike this output parameter
udt::pod_type p; udt::pod_type p;
nlohmann::from_json(j, p); nlohmann::from_json(j, p);
CHECK(p == expected); CHECK(p == expected);
@ -450,6 +481,18 @@ TEST_CASE("from_json free function", "[udt]")
nlohmann::from_json(j, o); nlohmann::from_json(j, o);
CHECK(expected == o); CHECK(expected == o);
} }
}
SECTION("no json_traits specialization")
{
udt::no_json_traits_type expected{42};
udt::no_json_traits_type res;
json j;
j["a"] = 42;
nlohmann::from_json(j, res);
CHECK(res == expected);
res = j.get<udt::no_json_traits_type>();
CHECK(res == expected);
} }
} }