diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index 34270e2368..50a3b329b3 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -445,13 +446,36 @@ inline void from_json(const BasicJsonType& j, ArithmeticType& val) } } -template -std::tuple from_json_tuple_impl_base(BasicJsonType&& j, index_sequence /*unused*/) +template +detail::uncvref_t from_json_tuple_get_impl(BasicJsonType&& j, detail::identity_tag /*unused*/, detail::priority_tag<0> /*unused*/) { - return std::make_tuple(std::forward(j).at(Idx).template get()...); + return std::forward(j).template get>(); } -template +template::value, int> = 0> +Type from_json_tuple_get_impl(BasicJsonType && j, detail::identity_tag /*unused*/, detail::priority_tag<1> /*unused*/) +{ + return std::forward(j).template get_ref(); +} + +template>::value, int> = 0> +detail::uncvref_t from_json_tuple_get_impl(BasicJsonType && j, detail::identity_tag /*unused*/, detail::priority_tag<2> /*unused*/) +{ + return std::forward(j).template get>(); +} + +template +using tuple_type = std::tuple < decltype(from_json_tuple_get_impl(std::declval(), detail::identity_tag {}, detail::priority_tag {}))... >; + +template +tuple_type from_json_tuple_impl_base(BasicJsonType&& j, index_sequence /*unused*/) +{ + return tuple_type(from_json_tuple_get_impl(std::forward(j).at(Idx), detail::identity_tag {}, detail::priority_tag {})...); +} + +template std::tuple<> from_json_tuple_impl_base(BasicJsonType& /*unused*/, index_sequence<> /*unused*/) { return {}; @@ -473,13 +497,15 @@ inline void from_json_tuple_impl(BasicJsonType&& j, std::pair& p, priori template std::tuple from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<2> /*unused*/) { - return from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); + static_assert(cxpr_and>, is_compatible_reference_type>...>::value, + "Can not return a tuple containing references to types not contained in a Json, try Json::get_to()"); + return from_json_tuple_impl_base<1, Args...>(std::forward(j), index_sequence_for {}); } template inline void from_json_tuple_impl(BasicJsonType&& j, std::tuple& t, priority_tag<3> /*unused*/) { - t = from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); + t = from_json_tuple_impl_base<2, Args...>(std::forward(j), index_sequence_for {}); } template diff --git a/include/nlohmann/detail/meta/logic.hpp b/include/nlohmann/detail/meta/logic.hpp new file mode 100644 index 0000000000..cb50a5d19a --- /dev/null +++ b/include/nlohmann/detail/meta/logic.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ +#ifdef JSON_HAS_CPP_17 + +template +struct cxpr_or_impl : std::integral_constant < bool, (Booleans || ...) > {}; + +template +struct cxpr_and_impl : std::integral_constant < bool, (Booleans &&...) > {}; + +#else + +template +struct cxpr_or_impl : std::false_type {}; + +template +struct cxpr_or_impl : std::true_type {}; + +template +struct cxpr_or_impl : cxpr_or_impl {}; + +template +struct cxpr_and_impl : std::true_type {}; + +template +struct cxpr_and_impl : cxpr_and_impl {}; + +template +struct cxpr_and_impl : std::false_type {}; + +#endif + +template +struct cxpr_not : std::integral_constant < bool, !Boolean::value > {}; + +template +struct cxpr_or : cxpr_or_impl {}; + +template +struct cxpr_or_c : cxpr_or_impl {}; + +template +struct cxpr_and : cxpr_and_impl {}; + +template +struct cxpr_and_c : cxpr_and_impl {}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index c8e5e234f5..aa0810dce1 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -559,6 +559,28 @@ template struct is_compatible_type : is_compatible_type_impl {}; +template +struct is_compatible_reference_type_impl +{ + using JsonType = uncvref_t; + using CVType = typename std::remove_reference::type; + using Type = typename std::remove_cv::type; + constexpr static bool value = std::is_reference::value && + (!std::is_const::type>::value || std::is_const::value) && + (std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value); +}; + +template +struct is_compatible_reference_type + : is_compatible_reference_type_impl {}; + template struct is_constructible_tuple : std::false_type {}; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index ceb7a9f11d..e854a7d0b7 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4103,6 +4103,28 @@ template struct is_compatible_type : is_compatible_type_impl {}; +template +struct is_compatible_reference_type_impl +{ + using JsonType = uncvref_t; + using CVType = typename std::remove_reference::type; + using Type = typename std::remove_cv::type; + constexpr static bool value = std::is_reference::value && + (!std::is_const::type>::value || std::is_const::value) && + (std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value); +}; + +template +struct is_compatible_reference_type + : is_compatible_reference_type_impl {}; + template struct is_constructible_tuple : std::false_type {}; @@ -4857,6 +4879,63 @@ NLOHMANN_JSON_NAMESPACE_END // #include +// #include + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ +#ifdef JSON_HAS_CPP_17 + +template +struct cxpr_or_impl : std::integral_constant < bool, (Booleans || ...) > {}; + +template +struct cxpr_and_impl : std::integral_constant < bool, (Booleans &&...) > {}; + +#else + +template +struct cxpr_or_impl : std::false_type {}; + +template +struct cxpr_or_impl : std::true_type {}; + +template +struct cxpr_or_impl : cxpr_or_impl {}; + +template +struct cxpr_and_impl : std::true_type {}; + +template +struct cxpr_and_impl : cxpr_and_impl {}; + +template +struct cxpr_and_impl : std::false_type {}; + +#endif + +template +struct cxpr_not : std::integral_constant < bool, !Boolean::value > {}; + +template +struct cxpr_or : cxpr_or_impl {}; + +template +struct cxpr_or_c : cxpr_or_impl {}; + +template +struct cxpr_and : cxpr_and_impl {}; + +template +struct cxpr_and_c : cxpr_and_impl {}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + // #include // #include @@ -5278,13 +5357,36 @@ inline void from_json(const BasicJsonType& j, ArithmeticType& val) } } -template -std::tuple from_json_tuple_impl_base(BasicJsonType&& j, index_sequence /*unused*/) +template +detail::uncvref_t from_json_tuple_get_impl(BasicJsonType&& j, detail::identity_tag /*unused*/, detail::priority_tag<0> /*unused*/) { - return std::make_tuple(std::forward(j).at(Idx).template get()...); + return std::forward(j).template get>(); } -template +template::value, int> = 0> +Type from_json_tuple_get_impl(BasicJsonType && j, detail::identity_tag /*unused*/, detail::priority_tag<1> /*unused*/) +{ + return std::forward(j).template get_ref(); +} + +template>::value, int> = 0> +detail::uncvref_t from_json_tuple_get_impl(BasicJsonType && j, detail::identity_tag /*unused*/, detail::priority_tag<2> /*unused*/) +{ + return std::forward(j).template get>(); +} + +template +using tuple_type = std::tuple < decltype(from_json_tuple_get_impl(std::declval(), detail::identity_tag {}, detail::priority_tag {}))... >; + +template +tuple_type from_json_tuple_impl_base(BasicJsonType&& j, index_sequence /*unused*/) +{ + return tuple_type(from_json_tuple_get_impl(std::forward(j).at(Idx), detail::identity_tag {}, detail::priority_tag {})...); +} + +template std::tuple<> from_json_tuple_impl_base(BasicJsonType& /*unused*/, index_sequence<> /*unused*/) { return {}; @@ -5306,13 +5408,15 @@ inline void from_json_tuple_impl(BasicJsonType&& j, std::pair& p, priori template std::tuple from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<2> /*unused*/) { - return from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); + static_assert(cxpr_and>, is_compatible_reference_type>...>::value, + "Can not return a tuple containing references to types not contained in a Json, try Json::get_to()"); + return from_json_tuple_impl_base<1, Args...>(std::forward(j), index_sequence_for {}); } template inline void from_json_tuple_impl(BasicJsonType&& j, std::tuple& t, priority_tag<3> /*unused*/) { - t = from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); + t = from_json_tuple_impl_base<2, Args...>(std::forward(j), index_sequence_for {}); } template diff --git a/tests/src/unit-constructor1.cpp b/tests/src/unit-constructor1.cpp index 8eb7d6ab55..fba6b01040 100644 --- a/tests/src/unit-constructor1.cpp +++ b/tests/src/unit-constructor1.cpp @@ -281,6 +281,79 @@ TEST_CASE("constructors") // CHECK(std::get<2>(t) == j[2]); // commented out due to CI issue, see https://github.com/nlohmann/json/pull/3985 and https://github.com/nlohmann/json/issues/4025 } + SECTION("std::tuple tie") + { + const auto a = 1.0; + const auto* const b = "string"; + const auto c = 42; + const auto d = std::vector {0, 2}; + const size_t e = 1234; + auto t = std::tie(a, b, c, d, e); + json const j(t); + + double a_out = 0; + std::string b_out; + int c_out = 0; + std::vector d_out; + int64_t e_out = 0; + auto t_out = std::tie(a_out, b_out, c_out, d_out, e_out); + j.get_to(t_out); + CHECK(a_out == a); + CHECK(b_out == b); + CHECK(c_out == c); + CHECK(d_out == d); + CHECK(e_out == e); + } + + SECTION("std::tuple of references to elements") + { + const auto a = 1.0; + const auto* const b = "string"; + const auto c = 42; + const size_t d = 1234; + const auto t = std::tie(a, b, c, d); + json const j(t); + + auto t_out = j.get>(); + CHECK(&std::get<0>(t_out) == j[0].get_ptr()); + CHECK(&std::get<1>(t_out) == j[1].get_ptr()); + CHECK(&std::get<2>(t_out) == j[2].get_ptr()); + CHECK(&std::get<3>(t_out) == j[3].get_ptr()); + CHECK(std::get<0>(t_out) == a); + CHECK(std::get<1>(t_out) == b); + CHECK(std::get<2>(t_out) == c); + CHECK(std::get<3>(t_out) == d); + } + + SECTION("std::tuple mixed arithmetic types") + { + using j_float_t = json::number_float_t; + using j_int_t = json::number_integer_t; + using j_uint_t = json::number_unsigned_t; + const j_float_t a = 1.0; + const j_int_t b = 1234; + const j_uint_t c = 42; + json const j(std::tie(a, b, c, c)); + + auto t1 = j.get>(); + j_uint_t a2 = 0; + j_float_t b2 = 0; + j_int_t c2 = 0; + auto t2 = std::tie(a2, b2, c2); + j.get_to(t2); + + CHECK(std::get<0>(t1) == static_cast(a)); + CHECK(std::get<1>(t1) == static_cast(b)); + CHECK(std::get<2>(t1) == static_cast(c)); + // t1[3] exists only to force usage of the no-default-constructor version + CHECK(a2 == static_cast(a)); + CHECK(b2 == static_cast(b)); + CHECK(c2 == static_cast(c)); + } + SECTION("std::pair/tuple/array failures") { json const j{1};