From c136f67398319f63762b6cb14e9885fe10edfad2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Delrieu?= Date: Sun, 16 Oct 2016 17:29:57 +0200 Subject: [PATCH 01/14] add first version support for user-defined types --- src/json.hpp | 81 ++++++--- test/CMakeLists.txt | 1 + test/src/unit-constructor3.cpp | 300 +++++++++++++++++++++++++++++++++ 3 files changed, 363 insertions(+), 19 deletions(-) create mode 100644 test/src/unit-constructor3.cpp diff --git a/src/json.hpp b/src/json.hpp index 9c0df9d497..1cc516ab5b 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -91,12 +91,14 @@ SOFTWARE. */ namespace nlohmann { - +template +struct json_traits; /*! @brief unnamed namespace with internal helper functions @since version 1.0.0 */ +// TODO transform this anon ns to detail? namespace { /*! @@ -122,6 +124,27 @@ struct has_mapped_type std::is_integral()))>::value; }; +// taken from http://stackoverflow.com/questions/10711952/how-to-detect-existence-of-a-class-using-sfinae +template +struct has_destructor +{ + template + static std::true_type detect(decltype(std::declval().~U())*); + + template + static std::false_type detect(...); + + static constexpr bool value = decltype(detect(0))::value; +}; + +template +struct has_json_traits +{ + static constexpr bool value = has_destructor>::value; +}; + +template <> struct has_json_traits : std::false_type {}; + /*! @brief helper class to create locales with decimal point @@ -1225,6 +1248,15 @@ class basic_json assert_invariant(); } + template < + typename T, + typename = + typename std::enable_if::type>::type>::value>::type> + explicit basic_json(T &&val) + : basic_json(json_traits::type>::type>:: + to_json(std::forward(val))) {} /*! @brief create a string (explicit) @@ -1241,15 +1273,14 @@ class basic_json @sa @ref basic_json(const typename string_t::value_type*) -- create a string value from a character pointer - @sa @ref basic_json(const CompatibleStringType&) -- create a string value + @sa @ref basic_json(const CompatibleStringType&) -- create a string + value from a compatible string container @since version 1.0.0 */ - basic_json(const string_t& val) - : m_type(value_t::string), m_value(val) - { - assert_invariant(); + basic_json(const string_t &val) : m_type(value_t::string), m_value(val) { + assert_invariant(); } /*! @@ -2584,20 +2615,32 @@ class basic_json // value access // ////////////////// + template < + typename T, + typename = + typename std::enable_if::type>::type>::value>::type> + auto get_impl(T *) const -> decltype( + json_traits::type>::type>::from_json(std::declval())) { + return json_traits::type>::type>::from_json(*this); + } + /// get an object (explicit) - template::value and - std::is_convertible::value, int>::type = 0> - T get_impl(T*) const - { - if (is_object()) - { - return T(m_value.object->begin(), m_value.object->end()); - } - else - { - throw std::domain_error("type must be object, but is " + type_name()); - } + template ::value and + std::is_convertible::value, + int>::type = 0> + T get_impl(T *) const { + if (is_object()) { + return T(m_value.object->begin(), m_value.object->end()); + } else { + throw std::domain_error("type must be object, but is " + type_name()); + } } /// get an object (explicit) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 782d5b53f9..b4b85a92a4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -14,6 +14,7 @@ add_executable(${JSON_UNITTEST_TARGET_NAME} "src/unit-concepts.cpp" "src/unit-constructor1.cpp" "src/unit-constructor2.cpp" + "src/unit-constructor3.cpp" "src/unit-convenience.cpp" "src/unit-conversions.cpp" "src/unit-deserialization.cpp" diff --git a/test/src/unit-constructor3.cpp b/test/src/unit-constructor3.cpp new file mode 100644 index 0000000000..357585ff1e --- /dev/null +++ b/test/src/unit-constructor3.cpp @@ -0,0 +1,300 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.5 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include +#include +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +namespace udt +{ +struct empty_type {}; +struct pod_type { + int a; + char b; + 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 { + pod_type a; + pod_type b; + 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 +template +class optional_type +{ +public: + optional_type() = default; + explicit optional_type(T val) : _val(std::make_shared(std::move(val))) {} + explicit operator bool() const noexcept { return _val != nullptr; } + + T const &operator*() const { return *_val; } + +private: + std::shared_ptr _val; +}; + +template +inline bool operator==(optional_type const& lhs, optional_type const& rhs) +{ + if (!lhs && !rhs) + return true; + if (!lhs || !rhs) + return false; + return *lhs == *rhs; +} +} + +template +struct type_helper +{ + using type = T; +}; + +namespace nlohmann +{ +template <> +struct json_traits : type_helper +{ + static json to_json(type) + { + return json::object(); + } + + static type from_json(json const& j) + { + assert(j.is_object() and j.empty()); + return {}; + } +}; + +template <> +struct json_traits : type_helper +{ + static json to_json(type const& t) + { + return {{"a", t.a}, {"b", t.b}, {"c", t.c}}; + } + + static type from_json(json const& j) + { + assert(j.is_object()); + return {j["a"].get(), j["b"].get(), j["c"].get()}; + } +}; + +template <> +struct json_traits + : type_helper +{ + static json to_json(type const& t) + { + return json{{"a", json{t.a}}, {"b", json{t.b}}, {"c", t.c}}; + } + + static type from_json(json const& j) + { + return {j["a"].get(), j["b"].get(), + j["c"].get()}; + } +}; + +template +struct json_traits> : type_helper> { + static json to_json(type const&t) + { + if (t) + return json(*t); + return {}; + } + + static type from_json(json const& j) + { + if (j.is_null()) + return {}; + return type{j.get()}; + } +}; +} + + +TEST_CASE("constructors for user-defined types", "[udt]") +{ + SECTION("empty type") + { + udt::empty_type const e; + auto const j = json{e}; + auto k = json::object(); + CHECK(j == k); + } + + SECTION("pod type") + { + auto const e = udt::pod_type{42, 42, 42}; + auto j = json{e}; + auto k = json{{"a", 42}, {"b", 42}, {"c", 42}}; + CHECK(j == k); + } + + SECTION("bit more complex type") + { + auto const e = + udt::bit_more_complex_type{{42, 42, 42}, {41, 41, 41}, "forty"}; + + auto j = json{e}; + auto k = json{{"a", {{"a", 42}, {"b", 42}, {"c", 42}}}, + {"b", {{"a", 41}, {"b", 41}, {"c", 41}}}, + {"c", "forty"}}; + CHECK(j == k); + } + + SECTION("vector of udt") + { + std::vector v; + auto const e = + udt::bit_more_complex_type{{42, 42, 42}, {41, 41, 41}, "forty"}; + + v.emplace_back(e); + v.emplace_back(e); + v.emplace_back(e); + + json j = v; + auto k = json{{"a", {{"a", 42}, {"b", 42}, {"c", 42}}}, + {"b", {{"a", 41}, {"b", 41}, {"c", 41}}}, + {"c", "forty"}}; + auto l = json{k, k, k}; + CHECK(j == l); + } + + SECTION("optional type") { + SECTION("regular case") { + udt::optional_type u{3}; + CHECK(json{u} == json(3)); + } + + SECTION("nullopt case") { + udt::optional_type v; + CHECK(json{v} == json{}); + } + + SECTION("optional of json convertible type") + { + auto const e = + udt::bit_more_complex_type{{42, 42, 42}, {41, 41, 41}, "forty"}; + udt::optional_type o{e}; + auto k = json{{"a", {{"a", 42}, {"b", 42}, {"c", 42}}}, + {"b", {{"a", 41}, {"b", 41}, {"c", 41}}}, + {"c", "forty"}}; + CHECK(json{o} == k); + } + + SECTION("optional of vector of json convertible type") + { + std::vector v; + auto const e = + udt::bit_more_complex_type{{42, 42, 42}, {41, 41, 41}, "forty"}; + v.emplace_back(e); + v.emplace_back(e); + v.emplace_back(e); + udt::optional_type> o{v}; + auto k = json{{"a", {{"a", 42}, {"b", 42}, {"c", 42}}}, + {"b", {{"a", 41}, {"b", 41}, {"c", 41}}}, + {"c", "forty"}}; + auto l = json{k, k, k}; + CHECK(json{o} == l); + } + } +} + +TEST_CASE("get<> for user-defined types", "[udt]") +{ + SECTION("pod type") + { + auto const e = udt::pod_type{42, 42, 42}; + auto const j = json{{"a", 42}, {"b", 42}, {"c", 42}}; + + auto const obj = j.get(); + CHECK(e == obj); + } + + SECTION("bit more complex type") + { + auto const e = + 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"}}; + + auto const obj = j.get(); + CHECK(e == obj); + } + + SECTION("vector of udt") + { + auto const e = + udt::bit_more_complex_type{{42, 42, 42}, {41, 41, 41}, "forty"}; + std::vector v{e, e, e}; + auto const j = json(v); + + auto const obj = j.get(); + CHECK(v == obj); + } + + SECTION("optional") + { + SECTION("from null") + { + udt::optional_type o; + json j; + CHECK(j.get() == o); + } + + SECTION("from value") + { + json j{{"a", 42}, {"b", 42}, {"c", 42}}; + auto v = j.get>(); + auto expected = udt::pod_type{42,42,42}; + REQUIRE(v); + CHECK(*v == expected); + } + } +} \ No newline at end of file From 2ee7399b6fdce0bec2ed834cf7d0cf2915a92323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20DELRIEU?= Date: Mon, 17 Oct 2016 23:30:15 +0200 Subject: [PATCH 02/14] fix compilation on gcc-6 --- test/src/unit-constructor3.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/test/src/unit-constructor3.cpp b/test/src/unit-constructor3.cpp index 357585ff1e..d119625fa6 100644 --- a/test/src/unit-constructor3.cpp +++ b/test/src/unit-constructor3.cpp @@ -84,17 +84,13 @@ inline bool operator==(optional_type const& lhs, optional_type const& rhs) } } -template -struct type_helper -{ - using type = T; -}; - namespace nlohmann { template <> -struct json_traits : type_helper +struct json_traits { + using type = udt::empty_type; + static json to_json(type) { return json::object(); @@ -108,8 +104,10 @@ struct json_traits : type_helper }; template <> -struct json_traits : type_helper -{ +struct json_traits +{ + using type = udt::pod_type; + static json to_json(type const& t) { return {{"a", t.a}, {"b", t.b}, {"c", t.c}}; @@ -124,8 +122,9 @@ struct json_traits : type_helper template <> struct json_traits - : type_helper { + using type = udt::bit_more_complex_type; + static json to_json(type const& t) { return json{{"a", json{t.a}}, {"b", json{t.b}}, {"c", t.c}}; @@ -139,8 +138,11 @@ struct json_traits }; template -struct json_traits> : type_helper> { - static json to_json(type const&t) +struct json_traits> +{ + using type = udt::optional_type; + + static json to_json(type const& t) { if (t) return json(*t); @@ -297,4 +299,4 @@ TEST_CASE("get<> for user-defined types", "[udt]") CHECK(*v == expected); } } -} \ No newline at end of file +} From 0001032d42f44e8d087a0437b32d350c3b3a01f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20DELRIEU?= Date: Mon, 17 Oct 2016 23:41:53 +0200 Subject: [PATCH 03/14] anonymous namespace renamed to detail --- src/json.hpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 1cc516ab5b..00af6274c2 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -98,8 +98,8 @@ struct json_traits; @brief unnamed namespace with internal helper functions @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. @@ -143,8 +143,6 @@ struct has_json_traits static constexpr bool value = has_destructor>::value; }; -template <> struct has_json_traits : std::false_type {}; - /*! @brief helper class to create locales with decimal point @@ -1251,7 +1249,7 @@ class basic_json template < typename T, typename = - typename std::enable_if::type>::type>::value>::type> explicit basic_json(T &&val) : basic_json(json_traits::type>::type>::value>::type> auto get_impl(T *) const -> decltype( json_traits::value and not std::is_arithmetic::value and not std::is_convertible::value and - not has_mapped_type::value, int>::type = 0> + not detail::has_mapped_type::value, int>::type = 0> T get_impl(T*) const { if (is_array()) @@ -2707,7 +2705,7 @@ class basic_json /// get an array (explicit) template::value and - not has_mapped_type::value, int>::type = 0> + not detail::has_mapped_type::value, int>::type = 0> T get_impl(T*) const { if (is_array()) @@ -5872,7 +5870,7 @@ class basic_json o.width(0); // fix locale problems - const auto old_locale = o.imbue(std::locale(std::locale(), new DecimalSeparator)); + const auto old_locale = o.imbue(std::locale(std::locale(), new detail::DecimalSeparator)); // set precision // 6, 15 or 16 digits of precision allows round-trip IEEE 754 From 63241db76e92db2dcdfd7f10c969cc26298ebdb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20DELRIEU?= Date: Tue, 18 Oct 2016 00:37:35 +0200 Subject: [PATCH 04/14] wip: add free function to_json/from_json support --- src/json.hpp | 34 ++++++++++++++ test/src/unit-constructor3.cpp | 84 +++++++++++++++++++++++++++++----- 2 files changed, 107 insertions(+), 11 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 00af6274c2..962f7f135e 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -143,6 +143,24 @@ struct has_json_traits static constexpr bool value = has_destructor>::value; }; +struct to_json_fn +{ + template + constexpr auto operator()(T&& val) const -> decltype(to_json(std::forward(val))) + { + return to_json(std::forward(val)); + } +}; + +struct from_json_fn +{ + template + constexpr auto operator()(T&& val) const -> decltype(from_json(std::forward(val))) + { + return from_json(std::forward(val)); + } +}; + /*! @brief helper class to create locales with decimal point @@ -165,6 +183,22 @@ struct DecimalSeparator : std::numpunct } +// taken from ranges-v3 +template +struct __static_const +{ + static constexpr T value{}; +}; + +template +constexpr T __static_const::value; + +inline namespace +{ + constexpr auto const& to_json = __static_const::value; + constexpr auto const& from_json = __static_const::value; +} + /*! @brief a class to store JSON values diff --git a/test/src/unit-constructor3.cpp b/test/src/unit-constructor3.cpp index d119625fa6..912772f3d3 100644 --- a/test/src/unit-constructor3.cpp +++ b/test/src/unit-constructor3.cpp @@ -42,22 +42,12 @@ struct pod_type { 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 { pod_type a; pod_type b; 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 template class optional_type @@ -73,6 +63,43 @@ class optional_type std::shared_ptr _val; }; +// 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 +json to_json(optional_type 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 +{ + 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 inline bool operator==(optional_type const& lhs, optional_type const& rhs) { @@ -163,7 +190,7 @@ TEST_CASE("constructors for user-defined types", "[udt]") { SECTION("empty type") { - udt::empty_type const e; + udt::empty_type const e{}; auto const j = json{e}; auto k = json::object(); CHECK(j == k); @@ -300,3 +327,38 @@ 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 o; + + json expected; + auto const j = nlohmann::to_json(o); + CHECK(expected == j); + } + } +} From f805dfcbbf3d2120b471e805effa6153354b5521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20DELRIEU?= Date: Tue, 18 Oct 2016 11:37:32 +0200 Subject: [PATCH 05/14] wip: modify from_json prototype --- src/json.hpp | 6 +-- test/src/unit-constructor3.cpp | 91 ++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 962f7f135e..a58d38a7c4 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -154,10 +154,10 @@ struct to_json_fn struct from_json_fn { - template - constexpr auto operator()(T&& val) const -> decltype(from_json(std::forward(val))) + template + constexpr auto operator()(Json const& from, T& to) const -> decltype(from_json(from, to)) { - return from_json(std::forward(val)); + return from_json(from, to); } }; diff --git a/test/src/unit-constructor3.cpp b/test/src/unit-constructor3.cpp index 912772f3d3..3e025c1544 100644 --- a/test/src/unit-constructor3.cpp +++ b/test/src/unit-constructor3.cpp @@ -58,6 +58,11 @@ class optional_type explicit operator bool() const noexcept { return _val != nullptr; } T const &operator*() const { return *_val; } + optional_type& operator=(T const& t) + { + _val = std::make_shared(t); + return *this; + } private: std::shared_ptr _val; @@ -81,6 +86,33 @@ json to_json(bit_more_complex_type const& p) return json{{"a", to_json(p.a)}, {"b", to_json(p.b)}, {"c", p.c}}; } +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(), j["b"].get(), j["c"].get()}; +} + +void from_json(json const&j, bit_more_complex_type& t) +{ + // relying on json_traits struct here.. + t = {j["a"].get(), j["b"].get(), + j["c"].get()}; +} + +template +void from_json(json const& j, optional_type& t) +{ + if (j.is_null()) + t = optional_type{}; + else + t = j.get(); +} + template json to_json(optional_type const& opt) { @@ -360,5 +392,64 @@ TEST_CASE("to_json free function", "[udt]") auto const j = nlohmann::to_json(o); CHECK(expected == j); } + + SECTION("from value") + { + udt::optional_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); + } + } +} + +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}}; + + // i really dislike this output parameter + 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 expected; + json j; + udt::optional_type o; + + nlohmann::from_json(j, o); + CHECK(expected == o); + } + + SECTION("from value") + { + udt::optional_type expected{{42, 42, 42}}; + auto const j = json{{"a", 42}, {"b", 42}, {"c", 42}}; + udt::optional_type o; + + nlohmann::from_json(j, o); + CHECK(expected == o); + } + } } From 733e95f4ae486dc1e4a35fe29f57d680f22c57d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20DELRIEU?= Date: Tue, 18 Oct 2016 13:36:43 +0200 Subject: [PATCH 06/14] wip: add get_impl overload that uses free from_json method --- src/json.hpp | 49 ++++++++++++++++++++++++-- test/src/unit-constructor3.cpp | 63 ++++++++++++++++++++++++++++------ 2 files changed, 100 insertions(+), 12 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index a58d38a7c4..e6bf9a4048 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -91,6 +91,14 @@ SOFTWARE. */ 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 will call 'T from_json(json const&)' template 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 +// used to determine if json_traits is defined for a given type T template struct has_destructor { @@ -184,6 +193,7 @@ struct DecimalSeparator : std::numpunct } // taken from ranges-v3 +// TODO add doc template struct __static_const { @@ -1280,6 +1290,20 @@ class basic_json 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 < typename T, typename = @@ -2647,10 +2671,13 @@ class basic_json // value access // ////////////////// + // get_impl overload chosen if json_traits struct is specialized for type T + // simply returns json_traits::from_json(*this); + // TODO add alias templates (enable_if_t etc) template < typename T, - typename = - typename std::enable_if::type>::type>::value>::type> auto get_impl(T *) const -> decltype( json_traits::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 + auto get_impl(T *) const -> typename std::enable_if< + not detail::has_json_traits::type>::value, + typename std::remove_cv(), + std::declval()), + std::declval())>::type>::type>::type + { + typename std::remove_cv::type>::type + ret; + ::nlohmann::from_json(*this, ret); + return ret; + } + /// get an object (explicit) template _val; }; +struct no_json_traits_type +{ + int a; +}; + // free to/from_json functions 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}}; } +template +json to_json(optional_type 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()); @@ -104,6 +125,11 @@ void from_json(json const&j, bit_more_complex_type& t) j["c"].get()}; } +void from_json(json const& j, no_json_traits_type& t) +{ + t.a = j["a"].get(); +} + template void from_json(json const& j, optional_type& t) { @@ -113,15 +139,6 @@ void from_json(json const& j, optional_type& t) t = j.get(); } -template -json to_json(optional_type 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 { 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 const& lhs, optional_type const& rhs) return false; 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 @@ -402,6 +424,16 @@ TEST_CASE("to_json free function", "[udt]") 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]") @@ -411,7 +443,6 @@ TEST_CASE("from_json free function", "[udt]") auto const expected = udt::pod_type{42, 42, 42}; auto const j = json{{"a", 42}, {"b", 42}, {"c", 42}}; - // i really dislike this output parameter udt::pod_type p; nlohmann::from_json(j, p); CHECK(p == expected); @@ -450,6 +481,18 @@ TEST_CASE("from_json free function", "[udt]") 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(); + CHECK(res == expected); } } From 06d60de3aac723f370231b219b3904f10e3ea152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20DELRIEU?= Date: Tue, 18 Oct 2016 23:45:58 +0200 Subject: [PATCH 07/14] add alias templates to reduce boilerplate --- src/json.hpp | 62 ++++++++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index e6bf9a4048..1d88cd1143 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -102,6 +102,17 @@ namespace nlohmann template struct json_traits; +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +template +using remove_cv_t = typename std::remove_cv::type; + +template +using remove_reference_t = typename std::remove_reference::type; + +// TODO update this doc /*! @brief unnamed namespace with internal helper functions @since version 1.0.0 @@ -1304,15 +1315,11 @@ class basic_json // auto j = json{{"a", json(not_equality_comparable{})}}; // // we can remove this constraint though, since lots of ctor are not explicit already - template < - typename T, - typename = - typename std::enable_if::type>::type>::value>::type> + template >>::value>> explicit basic_json(T &&val) - : basic_json(json_traits::type>::type>:: - to_json(std::forward(val))) {} + : basic_json(json_traits>>::to_json( + std::forward(val))) {} /*! @brief create a string (explicit) @@ -2673,17 +2680,12 @@ class basic_json // get_impl overload chosen if json_traits struct is specialized for type T // simply returns json_traits::from_json(*this); - // TODO add alias templates (enable_if_t etc) - template < - typename T, - typename = typename std::enable_if< - detail::has_json_traits::type>::type>::value>::type> - auto get_impl(T *) const -> decltype( - json_traits::type>::type>::from_json(std::declval())) { - return json_traits::type>::type>::from_json(*this); + template >>::value>> + auto get_impl(T *) const + -> decltype(json_traits>>::from_json( + std::declval())) { + return json_traits>>::from_json(*this); } // this one is quite atrocious @@ -2691,15 +2693,19 @@ class basic_json // 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 - auto get_impl(T *) const -> typename std::enable_if< - not detail::has_json_traits::type>::value, - typename std::remove_cv(), - std::declval()), - std::declval())>::type>::type>::type - { - typename std::remove_cv::type>::type - ret; + auto get_impl(T *) const + -> enable_if_t>::value, + remove_cv_t(), + std::declval()), + std::declval())>>> + { + remove_cv_t ret; + // I guess this output parameter is the only way to get ADL + // Even if users can use the get method to have a more 'functional' behaviour + // i.e. having a return type, could there be a way to have the same behaviour with from_json? + // e.g. auto t = nlohmann::from_json(json{}); + // this seems to require variable templates though... (at least it did when I tried to implement it) ::nlohmann::from_json(*this, ret); return ret; } From 24078332269763f7ad201d9a623fe5d92628edc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20DELRIEU?= Date: Thu, 20 Oct 2016 13:37:50 +0200 Subject: [PATCH 08/14] revert useless formatting --- src/json.hpp | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 1d88cd1143..bfb2b147cc 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -1336,14 +1336,15 @@ class basic_json @sa @ref basic_json(const typename string_t::value_type*) -- create a string value from a character pointer - @sa @ref basic_json(const CompatibleStringType&) -- create a string - value + @sa @ref basic_json(const CompatibleStringType&) -- create a string value from a compatible string container @since version 1.0.0 */ - basic_json(const string_t &val) : m_type(value_t::string), m_value(val) { - assert_invariant(); + basic_json(const string_t& val) + : m_type(value_t::string), m_value(val) + { + assert_invariant(); } /*! @@ -2710,20 +2711,19 @@ class basic_json return ret; } - /// get an object (explicit) - template ::value and - std::is_convertible::value, - int>::type = 0> - T get_impl(T *) const { - if (is_object()) { - return T(m_value.object->begin(), m_value.object->end()); - } else { - throw std::domain_error("type must be object, but is " + type_name()); - } + template::value and + std::is_convertible::value, int>::type = 0> + T get_impl(T*) const + { + if (is_object()) + { + return T(m_value.object->begin(), m_value.object->end()); + } + else + { + throw std::domain_error("type must be object, but is " + type_name()); + } } /// get an object (explicit) From db4806b247b0cf93c70d31f2314112aee7199fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20DELRIEU?= Date: Thu, 20 Oct 2016 13:45:48 +0200 Subject: [PATCH 09/14] rename __static_const to _static_const (reserved identifier) --- src/json.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index bfb2b147cc..f2c7302bab 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -206,18 +206,18 @@ struct DecimalSeparator : std::numpunct // taken from ranges-v3 // TODO add doc template -struct __static_const +struct _static_const { static constexpr T value{}; }; template -constexpr T __static_const::value; +constexpr T _static_const::value; inline namespace { - constexpr auto const& to_json = __static_const::value; - constexpr auto const& from_json = __static_const::value; + constexpr auto const& to_json = _static_const::value; + constexpr auto const& from_json = _static_const::value; } /*! From 9d290c6d99f3a5d308d5a45c6067ba73fc0d5a2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20DELRIEU?= Date: Thu, 20 Oct 2016 14:02:31 +0200 Subject: [PATCH 10/14] use uncvref_t instead of remove_cv_t>> --- src/json.hpp | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index f2c7302bab..b286b0acee 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -112,6 +112,9 @@ using remove_cv_t = typename std::remove_cv::type; template using remove_reference_t = typename std::remove_reference::type; +template +using uncvref_t = remove_cv_t>; + // TODO update this doc /*! @brief unnamed namespace with internal helper functions @@ -1315,11 +1318,12 @@ class basic_json // auto j = json{{"a", json(not_equality_comparable{})}}; // // we can remove this constraint though, since lots of ctor are not explicit already - template >>::value>> + template >::value>> explicit basic_json(T &&val) - : basic_json(json_traits>>::to_json( - std::forward(val))) {} + : basic_json(json_traits>::to_json(std::forward(val))) + { + } /*! @brief create a string (explicit) @@ -2681,12 +2685,12 @@ class basic_json // get_impl overload chosen if json_traits struct is specialized for type T // simply returns json_traits::from_json(*this); - template >>::value>> - auto get_impl(T *) const - -> decltype(json_traits>>::from_json( - std::declval())) { - return json_traits>>::from_json(*this); + template >::value>> + auto get_impl(T *) const -> decltype( + json_traits>::from_json(std::declval())) + { + return json_traits>::from_json(*this); } // this one is quite atrocious @@ -2694,12 +2698,11 @@ class basic_json // 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 - auto get_impl(T *) const - -> enable_if_t>::value, - remove_cv_t(), + auto get_impl(T *) const -> enable_if_t< + not detail::has_json_traits>::value, + uncvref_t(), std::declval()), - std::declval())>>> + std::declval())>> { remove_cv_t ret; // I guess this output parameter is the only way to get ADL From 5451aa5373551568a7fceb6ec5827db7cb804de7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20DELRIEU?= Date: Thu, 20 Oct 2016 18:02:07 +0200 Subject: [PATCH 11/14] remove has_destructor and has_json_traits, use decltype instead --- src/json.hpp | 45 ++++++++++++--------------------------------- 1 file changed, 12 insertions(+), 33 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index b286b0acee..731e06cc30 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -146,26 +146,6 @@ struct has_mapped_type std::is_integral()))>::value; }; -// 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 -struct has_destructor -{ - template - static std::true_type detect(decltype(std::declval().~U())*); - - template - static std::false_type detect(...); - - static constexpr bool value = decltype(detect(0))::value; -}; - -template -struct has_json_traits -{ - static constexpr bool value = has_destructor>::value; -}; - struct to_json_fn { template @@ -1318,8 +1298,7 @@ class basic_json // auto j = json{{"a", json(not_equality_comparable{})}}; // // we can remove this constraint though, since lots of ctor are not explicit already - template >::value>> + template >::to_json(std::declval>()))> explicit basic_json(T &&val) : basic_json(json_traits>::to_json(std::forward(val))) { @@ -2685,24 +2664,19 @@ class basic_json // get_impl overload chosen if json_traits struct is specialized for type T // simply returns json_traits::from_json(*this); - template >::value>> - auto get_impl(T *) const -> decltype( - json_traits>::from_json(std::declval())) + // dual argument to avoid conflicting with get_impl overloads taking a pointer + template + auto get_impl(int, int) const -> decltype(json_traits>::from_json(*this)) { return json_traits>::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 - auto get_impl(T *) const -> enable_if_t< - not detail::has_json_traits>::value, - uncvref_t(), - std::declval()), - std::declval())>> + auto get_impl(long, long) const -> uncvref_t()), + std::declval())> { remove_cv_t ret; // I guess this output parameter is the only way to get ADL @@ -3027,11 +3001,16 @@ class basic_json */ template::value, int>::type = 0> - ValueType get() const + auto get() const -> decltype(get_impl(static_cast(nullptr))) { return get_impl(static_cast(nullptr)); } + template + auto get() const -> decltype(get_impl(0, 0)) + { + return get_impl(0, 0); + } /*! @brief get a pointer value (explicit) From 15507d1e2bcf972ddd1ad79bd288d067c0c2a9da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20DELRIEU?= Date: Fri, 21 Oct 2016 16:28:01 +0200 Subject: [PATCH 12/14] move most SFINAE trickery in to/from_json_fn --- src/json.hpp | 123 +++++++++++++++++++++------------ test/src/unit-constructor3.cpp | 68 +++++++++++++++--- 2 files changed, 138 insertions(+), 53 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 731e06cc30..0e14ec8d5e 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -148,20 +148,65 @@ struct has_mapped_type struct to_json_fn { - template - constexpr auto operator()(T&& val) const -> decltype(to_json(std::forward(val))) - { - return to_json(std::forward(val)); - } + private: + // fallback overload + template + static constexpr auto + impl(T &&val, long) noexcept(noexcept(to_json(std::forward(val)))) + -> decltype(to_json(std::forward(val))) + { + return to_json(std::forward(val)); + } + + // preferred overload + template + static constexpr auto impl(T &&val, int) noexcept( + noexcept(json_traits>::to_json(std::forward(val)))) + -> decltype(json_traits>::to_json(std::forward(val))) + { + return json_traits>::to_json(std::forward(val)); + } + + public: + template + constexpr auto operator()(T &&val) const + noexcept(noexcept(to_json_fn::impl(std::forward(val), 0))) + -> decltype(to_json_fn::impl(std::forward(val), 0)) + { + // decltype(0) -> int, so the compiler will try to take the 'preferred overload' + // if there is no specialization, the 'fallback overload' will be taken by converting 0 to long + return to_json_fn::impl(std::forward(val), 0); + } }; struct from_json_fn { - template - constexpr auto operator()(Json const& from, T& to) const -> decltype(from_json(from, to)) - { - return from_json(from, to); - } + private: + template + static constexpr auto impl(Json const &j, T &val, + long) noexcept(noexcept(from_json(j, val))) + -> decltype(from_json(j, val)) + { + return from_json(j, val); + } + + template + static constexpr auto + impl(Json const &j, T &val, + int) noexcept(noexcept(json_traits::from_json(j, val))) + -> decltype(json_traits::from_json(j, val)) + { + return json_traits::from_json(j, val); + } + + public: + template + constexpr auto operator()(Json const &j, T &val) const + noexcept(noexcept(from_json_fn::impl(j, val, 0))) + -> decltype(from_json_fn::impl(j, val, 0)) + { + return from_json_fn::impl(j, val, 0); + } }; /*! @@ -1284,7 +1329,13 @@ class basic_json assert_invariant(); } - // constructor chosen if json_traits is specialized for type T + // constructor chosen for user-defined types that either have: + // - a to_json free function in their type's namespace + // - a json_traits specialization for their type + // + // If there is both a free function and a specialization, the latter will be chosen, + // since it is a more advanced use + // // note: constructor is marked explicit to avoid the following issue: // // struct not_equality_comparable{}; @@ -1294,15 +1345,15 @@ class basic_json // 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 + // the trade-off is expressiveness 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 >::to_json(std::declval>()))> + template >()))> explicit basic_json(T &&val) - : basic_json(json_traits>::to_json(std::forward(val))) - { - } + : basic_json(::nlohmann::to_json(std::forward(val))) {} + /*! @brief create a string (explicit) @@ -2662,32 +2713,6 @@ class basic_json // value access // ////////////////// - // get_impl overload chosen if json_traits struct is specialized for type T - // simply returns json_traits::from_json(*this); - // dual argument to avoid conflicting with get_impl overloads taking a pointer - template - auto get_impl(int, int) const -> decltype(json_traits>::from_json(*this)) - { - return json_traits>::from_json(*this); - } - - // 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 - auto get_impl(long, long) const -> uncvref_t()), - std::declval())> - { - remove_cv_t ret; - // I guess this output parameter is the only way to get ADL - // Even if users can use the get method to have a more 'functional' behaviour - // i.e. having a return type, could there be a way to have the same behaviour with from_json? - // e.g. auto t = nlohmann::from_json(json{}); - // this seems to require variable templates though... (at least it did when I tried to implement it) - ::nlohmann::from_json(*this, ret); - return ret; - } - template::value and std::is_convertible::value, int>::type = 0> @@ -3007,10 +3032,18 @@ class basic_json } template - auto get() const -> decltype(get_impl(0, 0)) - { - return get_impl(0, 0); + auto get() const -> remove_reference_t< + decltype(::nlohmann::from_json(*this, std::declval()), + std::declval())> + { + static_assert(std::is_default_constructible::value, + "ValueType must be default-constructible when user-defined " + "from_json method is used"); + ValueType ret; + ::nlohmann::from_json(*this, ret); + return ret; } + /*! @brief get a pointer value (explicit) diff --git a/test/src/unit-constructor3.cpp b/test/src/unit-constructor3.cpp index cfe6386730..de52762f67 100644 --- a/test/src/unit-constructor3.cpp +++ b/test/src/unit-constructor3.cpp @@ -35,6 +35,9 @@ using nlohmann::json; namespace udt { +// only used by counter_type +auto nb_free_function_calls = 0; + struct empty_type {}; struct pod_type { int a; @@ -48,6 +51,10 @@ struct bit_more_complex_type { std::string c; }; +struct counter_type +{ +}; + // best optional implementation ever template class optional_type @@ -97,14 +104,18 @@ json to_json(optional_type const& opt) using nlohmann::to_json; if (!opt) return nullptr; - return to_json(*opt); + return json(*opt); } json to_json(no_json_traits_type const& p) { - json ret; - ret["a"] = p.a; - return ret; + return {{"a", p.a}}; +} + +json to_json(counter_type) +{ + ++nb_free_function_calls; + return json::object(); } void from_json(json const&j, empty_type& t) @@ -139,6 +150,11 @@ void from_json(json const& j, optional_type& t) t = j.get(); } +void from_json(json const&, counter_type&) +{ + ++nb_free_function_calls; +} + 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); @@ -176,7 +192,7 @@ struct json_traits { return json::object(); } - + static type from_json(json const& j) { assert(j.is_object() and j.empty()); @@ -193,7 +209,7 @@ struct json_traits { return {{"a", t.a}, {"b", t.b}, {"c", t.c}}; } - + static type from_json(json const& j) { assert(j.is_object()); @@ -237,6 +253,25 @@ struct json_traits> return type{j.get()}; } }; + +template <> +struct json_traits +{ + using type = udt::counter_type; + static int nb_calls; + + static json to_json(type) + { + ++nb_calls; + return json::object(); + } + + static void from_json(json const&, type&) + { + ++nb_calls; + } +}; +int json_traits::nb_calls{0}; } @@ -380,6 +415,24 @@ TEST_CASE("get<> for user-defined types", "[udt]") CHECK(*v == expected); } } + + SECTION("no json_traits specialization, use of ADL") + { + udt::no_json_traits_type val{42}; + auto const expected = json{{"a", 42}}; + auto const j = json(val); + CHECK(j == expected); + } + + SECTION("counter_type") + { + // check that the traits specialization is chosen + auto const j = json{udt::counter_type{}}; + CHECK(nlohmann::json_traits::nb_calls == 1); + auto const elem = j.get(); + CHECK(nlohmann::json_traits::nb_calls == 2); + CHECK(udt::nb_free_function_calls == 0); + } } TEST_CASE("to_json free function", "[udt]") @@ -487,8 +540,7 @@ TEST_CASE("from_json free function", "[udt]") { udt::no_json_traits_type expected{42}; udt::no_json_traits_type res; - json j; - j["a"] = 42; + auto const j = json{{"a", 42}}; nlohmann::from_json(j, res); CHECK(res == expected); From 56492ce9c3addf43d8138da458849b9b190462f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Delrieu?= Date: Sat, 22 Oct 2016 19:04:59 +0200 Subject: [PATCH 13/14] fix compilation on vs2015 (taken from MS-ranges) --- src/json.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/json.hpp b/src/json.hpp index 0e14ec8d5e..7a8d76ae0f 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -146,6 +146,9 @@ struct has_mapped_type std::is_integral()))>::value; }; +void to_json(); +void from_json(); + struct to_json_fn { private: From b81c2a22ea0a0387f040f67e1613757e65cd62cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20DELRIEU?= Date: Sun, 23 Oct 2016 17:56:13 +0200 Subject: [PATCH 14/14] fix compilation on gcc 4.9 --- src/json.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/json.hpp b/src/json.hpp index 7a8d76ae0f..3c035ef3b9 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -3029,7 +3029,7 @@ class basic_json */ template::value, int>::type = 0> - auto get() const -> decltype(get_impl(static_cast(nullptr))) + auto get() const -> decltype(this->get_impl(static_cast(nullptr))) { return get_impl(static_cast(nullptr)); }