anonymous namespace renamed to detail
This commit is contained in:
parent
b443edf49e
commit
fe628b585b
2 changed files with 292 additions and 19 deletions
93
src/json.hpp
93
src/json.hpp
|
@ -106,6 +106,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;
|
||||||
|
|
||||||
|
@ -113,8 +121,8 @@ struct json_traits;
|
||||||
@brief unnamed namespace with internal helper functions
|
@brief unnamed namespace with internal helper functions
|
||||||
@since version 1.0.0
|
@since version 1.0.0
|
||||||
*/
|
*/
|
||||||
// TODO transform this anon ns to detail?
|
|
||||||
namespace
|
namespace detail
|
||||||
{
|
{
|
||||||
/*!
|
/*!
|
||||||
@brief Helper to determine whether there's a key_type for T.
|
@brief Helper to determine whether there's a key_type for T.
|
||||||
|
@ -140,6 +148,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
|
||||||
{
|
{
|
||||||
|
@ -158,7 +167,23 @@ struct has_json_traits
|
||||||
static constexpr bool value = has_destructor<json_traits<T>>::value;
|
static constexpr bool value = has_destructor<json_traits<T>>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <> struct has_json_traits<void> : std::false_type {};
|
struct to_json_fn
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
constexpr auto operator()(T&& val) const -> decltype(to_json(std::forward<T>(val)))
|
||||||
|
{
|
||||||
|
return to_json(std::forward<T>(val));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct from_json_fn
|
||||||
|
{
|
||||||
|
template <typename Json, typename T>
|
||||||
|
constexpr auto operator()(Json const& from, T& to) const -> decltype(from_json(from, to))
|
||||||
|
{
|
||||||
|
return from_json(from, to);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief helper class to create locales with decimal point
|
@brief helper class to create locales with decimal point
|
||||||
|
@ -181,6 +206,23 @@ struct DecimalSeparator : std::numpunct<char>
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// taken from ranges-v3
|
||||||
|
// TODO add doc
|
||||||
|
template <typename T>
|
||||||
|
struct __static_const
|
||||||
|
{
|
||||||
|
static constexpr T value{};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr T __static_const<T>::value;
|
||||||
|
|
||||||
|
inline namespace
|
||||||
|
{
|
||||||
|
constexpr auto const& to_json = __static_const<detail::to_json_fn>::value;
|
||||||
|
constexpr auto const& from_json = __static_const<detail::from_json_fn>::value;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief a class to store JSON values
|
@brief a class to store JSON values
|
||||||
|
|
||||||
|
@ -1337,10 +1379,24 @@ 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 =
|
||||||
typename std::enable_if<has_json_traits<typename std::remove_cv<
|
typename std::enable_if<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>
|
||||||
explicit basic_json(T &&val)
|
explicit basic_json(T &&val)
|
||||||
: basic_json(json_traits<typename std::remove_cv<
|
: basic_json(json_traits<typename std::remove_cv<
|
||||||
|
@ -2705,10 +2761,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<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<
|
||||||
|
@ -2717,6 +2776,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<
|
||||||
|
@ -2750,7 +2827,7 @@ class basic_json
|
||||||
not std::is_same<basic_json_t, typename T::value_type>::value and
|
not std::is_same<basic_json_t, typename T::value_type>::value and
|
||||||
not std::is_arithmetic<T>::value and
|
not std::is_arithmetic<T>::value and
|
||||||
not std::is_convertible<std::string, T>::value and
|
not std::is_convertible<std::string, T>::value and
|
||||||
not has_mapped_type<T>::value, int>::type = 0>
|
not detail::has_mapped_type<T>::value, int>::type = 0>
|
||||||
T get_impl(T* /*unused*/) const
|
T get_impl(T* /*unused*/) const
|
||||||
{
|
{
|
||||||
if (is_array())
|
if (is_array())
|
||||||
|
@ -2791,7 +2868,7 @@ class basic_json
|
||||||
/// get an array (explicit)
|
/// get an array (explicit)
|
||||||
template<class T, typename std::enable_if<
|
template<class T, typename std::enable_if<
|
||||||
std::is_same<basic_json, typename T::value_type>::value and
|
std::is_same<basic_json, typename T::value_type>::value and
|
||||||
not has_mapped_type<T>::value, int>::type = 0>
|
not detail::has_mapped_type<T>::value, int>::type = 0>
|
||||||
T get_impl(T* /*unused*/) const
|
T get_impl(T* /*unused*/) const
|
||||||
{
|
{
|
||||||
if (is_array())
|
if (is_array())
|
||||||
|
|
|
@ -42,22 +42,12 @@ struct pod_type {
|
||||||
short c;
|
short c;
|
||||||
};
|
};
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct bit_more_complex_type {
|
struct bit_more_complex_type {
|
||||||
pod_type a;
|
pod_type a;
|
||||||
pod_type b;
|
pod_type b;
|
||||||
std::string c;
|
std::string c;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool operator==(bit_more_complex_type const &lhs,
|
|
||||||
bit_more_complex_type const &rhs) noexcept {
|
|
||||||
return std::tie(lhs.a, lhs.b, lhs.c) == std::tie(rhs.a, rhs.b, rhs.c);
|
|
||||||
}
|
|
||||||
|
|
||||||
// best optional implementation ever
|
// best optional implementation ever
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class optional_type
|
class optional_type
|
||||||
|
@ -68,11 +58,97 @@ public:
|
||||||
explicit operator bool() const noexcept { return _val != nullptr; }
|
explicit operator bool() const noexcept { return _val != nullptr; }
|
||||||
|
|
||||||
T const &operator*() const { return *_val; }
|
T const &operator*() const { return *_val; }
|
||||||
|
optional_type& operator=(T const& t)
|
||||||
|
{
|
||||||
|
_val = std::make_shared<T>(t);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<T> _val;
|
std::shared_ptr<T> _val;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct no_json_traits_type
|
||||||
|
{
|
||||||
|
int a;
|
||||||
|
};
|
||||||
|
|
||||||
|
// free to/from_json functions
|
||||||
|
|
||||||
|
json to_json(empty_type)
|
||||||
|
{
|
||||||
|
return json::object();
|
||||||
|
}
|
||||||
|
|
||||||
|
json to_json(pod_type const& p)
|
||||||
|
{
|
||||||
|
return {{"a", p.a}, {"b", p.b}, {"c", p.c}};
|
||||||
|
}
|
||||||
|
|
||||||
|
json to_json(bit_more_complex_type const& p)
|
||||||
|
{
|
||||||
|
using nlohmann::to_json;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
assert(j.empty());
|
||||||
|
t = empty_type{};
|
||||||
|
}
|
||||||
|
|
||||||
|
void from_json(json const&j, pod_type& t)
|
||||||
|
{
|
||||||
|
t = {j["a"].get<int>(), j["b"].get<char>(), j["c"].get<short>()};
|
||||||
|
}
|
||||||
|
|
||||||
|
void from_json(json const&j, bit_more_complex_type& t)
|
||||||
|
{
|
||||||
|
// relying on json_traits struct here..
|
||||||
|
t = {j["a"].get<udt::pod_type>(), j["b"].get<udt::pod_type>(),
|
||||||
|
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>
|
||||||
|
void from_json(json const& j, optional_type<T>& t)
|
||||||
|
{
|
||||||
|
if (j.is_null())
|
||||||
|
t = optional_type<T>{};
|
||||||
|
else
|
||||||
|
t = j.get<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator==(bit_more_complex_type const &lhs,
|
||||||
|
bit_more_complex_type const &rhs) noexcept {
|
||||||
|
return std::tie(lhs.a, lhs.b, lhs.c) == std::tie(rhs.a, rhs.b, rhs.c);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline bool operator==(optional_type<T> const& lhs, optional_type<T> const& rhs)
|
inline bool operator==(optional_type<T> const& lhs, optional_type<T> const& rhs)
|
||||||
{
|
{
|
||||||
|
@ -82,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
|
||||||
|
@ -163,7 +244,7 @@ TEST_CASE("constructors for user-defined types", "[udt]")
|
||||||
{
|
{
|
||||||
SECTION("empty type")
|
SECTION("empty type")
|
||||||
{
|
{
|
||||||
udt::empty_type const e;
|
udt::empty_type const e{};
|
||||||
auto const j = json{e};
|
auto const j = json{e};
|
||||||
auto k = json::object();
|
auto k = json::object();
|
||||||
CHECK(j == k);
|
CHECK(j == k);
|
||||||
|
@ -300,3 +381,118 @@ TEST_CASE("get<> for user-defined types", "[udt]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("to_json free function", "[udt]")
|
||||||
|
{
|
||||||
|
SECTION("pod_type")
|
||||||
|
{
|
||||||
|
auto const e = udt::pod_type{42, 42, 42};
|
||||||
|
auto const expected = json{{"a", 42}, {"b", 42}, {"c", 42}};
|
||||||
|
|
||||||
|
auto const j = nlohmann::to_json(e);
|
||||||
|
CHECK(j == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("bit_more_complex_type")
|
||||||
|
{
|
||||||
|
auto const e =
|
||||||
|
udt::bit_more_complex_type{{42, 42, 42}, {41, 41, 41}, "forty"};
|
||||||
|
auto const expected = json{{"a", {{"a", 42}, {"b", 42}, {"c", 42}}},
|
||||||
|
{"b", {{"a", 41}, {"b", 41}, {"c", 41}}},
|
||||||
|
{"c", "forty"}};
|
||||||
|
auto const j = nlohmann::to_json(e);
|
||||||
|
CHECK(j == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("optional_type")
|
||||||
|
{
|
||||||
|
SECTION("from null")
|
||||||
|
{
|
||||||
|
udt::optional_type<udt::pod_type> o;
|
||||||
|
|
||||||
|
json expected;
|
||||||
|
auto const j = nlohmann::to_json(o);
|
||||||
|
CHECK(expected == j);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("from value")
|
||||||
|
{
|
||||||
|
udt::optional_type<udt::pod_type> o{{42, 42, 42}};
|
||||||
|
|
||||||
|
auto const expected = json{{"a", 42}, {"b", 42}, {"c", 42}};
|
||||||
|
auto const j = nlohmann::to_json(o);
|
||||||
|
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]")
|
||||||
|
{
|
||||||
|
SECTION("pod_type")
|
||||||
|
{
|
||||||
|
auto const expected = udt::pod_type{42, 42, 42};
|
||||||
|
auto const j = json{{"a", 42}, {"b", 42}, {"c", 42}};
|
||||||
|
|
||||||
|
udt::pod_type p;
|
||||||
|
nlohmann::from_json(j, p);
|
||||||
|
CHECK(p == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("bit_more_complex_type")
|
||||||
|
{
|
||||||
|
auto const expected =
|
||||||
|
udt::bit_more_complex_type{{42, 42, 42}, {41, 41, 41}, "forty"};
|
||||||
|
auto const j = json{{"a", {{"a", 42}, {"b", 42}, {"c", 42}}},
|
||||||
|
{"b", {{"a", 41}, {"b", 41}, {"c", 41}}},
|
||||||
|
{"c", "forty"}};
|
||||||
|
udt::bit_more_complex_type p;
|
||||||
|
nlohmann::from_json(j, p);
|
||||||
|
CHECK(p == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("optional_type")
|
||||||
|
{
|
||||||
|
SECTION("from null")
|
||||||
|
{
|
||||||
|
udt::optional_type<udt::pod_type> expected;
|
||||||
|
json j;
|
||||||
|
udt::optional_type<udt::pod_type> o;
|
||||||
|
|
||||||
|
nlohmann::from_json(j, o);
|
||||||
|
CHECK(expected == o);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("from value")
|
||||||
|
{
|
||||||
|
udt::optional_type<udt::pod_type> expected{{42, 42, 42}};
|
||||||
|
auto const j = json{{"a", 42}, {"b", 42}, {"c", 42}};
|
||||||
|
udt::optional_type<udt::pod_type> o;
|
||||||
|
|
||||||
|
nlohmann::from_json(j, 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue