Skip to content

Commit 25f4863

Browse files
authored
Reference tuple from json (#5016)
* Add reference handling to tuples Signed-off-by: Evelyn LePain <ava.lepain@gmail.com> * Remove template template type because pair isn't working Signed-off-by: Evelyn LePain <ava.lepain@gmail.com> * amalgamate std::tie changes Signed-off-by: Evelyn LePain <ava.lepain@gmail.com> * allow the elation of a move by removing the ref requirement Signed-off-by: Evelyn LePain <ava.lepain@gmail.com> * force all number_xxx_t to be interchangeable Signed-off-by: Evelyn LePain <ava.lepain@gmail.com> * Finally got amalgamate to work correctly Signed-off-by: Evelyn LePain <ava.lepain@gmail.com> * remove const version, add a test case for scrambled number representations. Signed-off-by: Evelyn LePain <ava.lepain@gmail.com> * Use the logical set of requirements instead of decltype because VS 2015 doesn't like it Signed-off-by: Evelyn LePain <ava.lepain@gmail.com> --------- Signed-off-by: Evelyn LePain <ava.lepain@gmail.com>
1 parent deb4c4f commit 25f4863

File tree

5 files changed

+291
-12
lines changed

5 files changed

+291
-12
lines changed

include/nlohmann/detail/conversions/from_json.hpp

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <nlohmann/detail/meta/identity_tag.hpp>
2727
#include <nlohmann/detail/meta/std_fs.hpp>
2828
#include <nlohmann/detail/meta/type_traits.hpp>
29+
#include <nlohmann/detail/meta/logic.hpp>
2930
#include <nlohmann/detail/string_concat.hpp>
3031
#include <nlohmann/detail/value_t.hpp>
3132

@@ -446,13 +447,36 @@ inline void from_json(const BasicJsonType& j, ArithmeticType& val)
446447
}
447448
}
448449

449-
template<typename BasicJsonType, typename... Args, std::size_t... Idx>
450-
std::tuple<Args...> from_json_tuple_impl_base(BasicJsonType&& j, index_sequence<Idx...> /*unused*/)
450+
template<typename BasicJsonType, typename Type>
451+
detail::uncvref_t<Type> from_json_tuple_get_impl(BasicJsonType&& j, detail::identity_tag<Type> /*unused*/, detail::priority_tag<0> /*unused*/)
451452
{
452-
return std::make_tuple(std::forward<BasicJsonType>(j).at(Idx).template get<Args>()...);
453+
return std::forward<BasicJsonType>(j).template get<detail::uncvref_t<Type>>();
453454
}
454455

455-
template<typename BasicJsonType>
456+
template<typename BasicJsonType, typename Type,
457+
detail::enable_if_t<detail::is_compatible_reference_type<BasicJsonType, Type>::value, int> = 0>
458+
Type from_json_tuple_get_impl(BasicJsonType && j, detail::identity_tag<Type> /*unused*/, detail::priority_tag<1> /*unused*/)
459+
{
460+
return std::forward<BasicJsonType>(j).template get_ref<Type>();
461+
}
462+
463+
template<typename BasicJsonType, typename Type,
464+
detail::enable_if_t<std::is_arithmetic<uncvref_t<Type>>::value, int> = 0>
465+
detail::uncvref_t<Type> from_json_tuple_get_impl(BasicJsonType && j, detail::identity_tag<Type> /*unused*/, detail::priority_tag<2> /*unused*/)
466+
{
467+
return std::forward<BasicJsonType>(j).template get<detail::uncvref_t<Type>>();
468+
}
469+
470+
template<std::size_t PTagValue, typename BasicJsonType, typename... Types>
471+
using tuple_type = std::tuple < decltype(from_json_tuple_get_impl(std::declval<BasicJsonType>(), detail::identity_tag<Types> {}, detail::priority_tag<PTagValue> {}))... >;
472+
473+
template<std::size_t PTagValue, typename... Args, typename BasicJsonType, std::size_t... Idx>
474+
tuple_type<PTagValue, BasicJsonType, Args...> from_json_tuple_impl_base(BasicJsonType&& j, index_sequence<Idx...> /*unused*/)
475+
{
476+
return tuple_type<PTagValue, BasicJsonType, Args...>(from_json_tuple_get_impl(std::forward<BasicJsonType>(j).at(Idx), detail::identity_tag<Args> {}, detail::priority_tag<PTagValue> {})...);
477+
}
478+
479+
template<std::size_t PTagValue, typename BasicJsonType>
456480
std::tuple<> from_json_tuple_impl_base(BasicJsonType& /*unused*/, index_sequence<> /*unused*/)
457481
{
458482
return {};
@@ -474,13 +498,15 @@ inline void from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priori
474498
template<typename BasicJsonType, typename... Args>
475499
std::tuple<Args...> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/)
476500
{
477-
return from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
501+
static_assert(cxpr_and<cxpr_or<cxpr_not<std::is_reference<Args>>, is_compatible_reference_type<BasicJsonType, Args>>...>::value,
502+
"Can not return a tuple containing references to types not contained in a Json, try Json::get_to()");
503+
return from_json_tuple_impl_base<1, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
478504
}
479505

480506
template<typename BasicJsonType, typename... Args>
481507
inline void from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)
482508
{
483-
t = from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
509+
t = from_json_tuple_impl_base<2, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
484510
}
485511

486512
template<typename BasicJsonType, typename TupleRelated>
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#pragma once
2+
3+
#include <nlohmann/detail/macro_scope.hpp>
4+
5+
NLOHMANN_JSON_NAMESPACE_BEGIN
6+
namespace detail
7+
{
8+
#ifdef JSON_HAS_CPP_17
9+
10+
template<bool... Booleans>
11+
struct cxpr_or_impl : std::integral_constant < bool, (Booleans || ...) > {};
12+
13+
template<bool... Booleans>
14+
struct cxpr_and_impl : std::integral_constant < bool, (Booleans &&...) > {};
15+
16+
#else
17+
18+
template<bool... Booleans>
19+
struct cxpr_or_impl : std::false_type {};
20+
21+
template<bool... Booleans>
22+
struct cxpr_or_impl<true, Booleans...> : std::true_type {};
23+
24+
template<bool... Booleans>
25+
struct cxpr_or_impl<false, Booleans...> : cxpr_or_impl<Booleans...> {};
26+
27+
template<bool... Booleans>
28+
struct cxpr_and_impl : std::true_type {};
29+
30+
template<bool... Booleans>
31+
struct cxpr_and_impl<true, Booleans...> : cxpr_and_impl<Booleans...> {};
32+
33+
template<bool... Booleans>
34+
struct cxpr_and_impl<false, Booleans...> : std::false_type {};
35+
36+
#endif
37+
38+
template<class Boolean>
39+
struct cxpr_not : std::integral_constant < bool, !Boolean::value > {};
40+
41+
template<class... Booleans>
42+
struct cxpr_or : cxpr_or_impl<Booleans::value...> {};
43+
44+
template<bool... Booleans>
45+
struct cxpr_or_c : cxpr_or_impl<Booleans...> {};
46+
47+
template<class... Booleans>
48+
struct cxpr_and : cxpr_and_impl<Booleans::value...> {};
49+
50+
template<bool... Booleans>
51+
struct cxpr_and_c : cxpr_and_impl<Booleans...> {};
52+
53+
} // namespace detail
54+
NLOHMANN_JSON_NAMESPACE_END

include/nlohmann/detail/meta/type_traits.hpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,28 @@ template<typename BasicJsonType, typename CompatibleType>
559559
struct is_compatible_type
560560
: is_compatible_type_impl<BasicJsonType, CompatibleType> {};
561561

562+
template<typename BasicJsonType, typename CompatibleReferenceType>
563+
struct is_compatible_reference_type_impl
564+
{
565+
using JsonType = uncvref_t<BasicJsonType>;
566+
using CVType = typename std::remove_reference<CompatibleReferenceType>::type;
567+
using Type = typename std::remove_cv<CVType>::type;
568+
constexpr static bool value = std::is_reference<CompatibleReferenceType>::value &&
569+
(!std::is_const<typename std::remove_reference<BasicJsonType>::type>::value || std::is_const<CVType>::value) &&
570+
(std::is_same<typename JsonType::boolean_t, Type>::value ||
571+
std::is_same<typename JsonType::number_float_t, Type>::value ||
572+
std::is_same<typename JsonType::number_integer_t, Type>::value ||
573+
std::is_same<typename JsonType::number_unsigned_t, Type>::value ||
574+
std::is_same<typename JsonType::string_t, Type>::value ||
575+
std::is_same<typename JsonType::binary_t, Type>::value ||
576+
std::is_same<typename JsonType::object_t, Type>::value ||
577+
std::is_same<typename JsonType::array_t, Type>::value);
578+
};
579+
580+
template<typename BasicJsonType, typename CompatibleReferenceType>
581+
struct is_compatible_reference_type
582+
: is_compatible_reference_type_impl<BasicJsonType, CompatibleReferenceType> {};
583+
562584
template<typename T1, typename T2>
563585
struct is_constructible_tuple : std::false_type {};
564586

single_include/nlohmann/json.hpp

Lines changed: 110 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4103,6 +4103,28 @@ template<typename BasicJsonType, typename CompatibleType>
41034103
struct is_compatible_type
41044104
: is_compatible_type_impl<BasicJsonType, CompatibleType> {};
41054105

4106+
template<typename BasicJsonType, typename CompatibleReferenceType>
4107+
struct is_compatible_reference_type_impl
4108+
{
4109+
using JsonType = uncvref_t<BasicJsonType>;
4110+
using CVType = typename std::remove_reference<CompatibleReferenceType>::type;
4111+
using Type = typename std::remove_cv<CVType>::type;
4112+
constexpr static bool value = std::is_reference<CompatibleReferenceType>::value &&
4113+
(!std::is_const<typename std::remove_reference<BasicJsonType>::type>::value || std::is_const<CVType>::value) &&
4114+
(std::is_same<typename JsonType::boolean_t, Type>::value ||
4115+
std::is_same<typename JsonType::number_float_t, Type>::value ||
4116+
std::is_same<typename JsonType::number_integer_t, Type>::value ||
4117+
std::is_same<typename JsonType::number_unsigned_t, Type>::value ||
4118+
std::is_same<typename JsonType::string_t, Type>::value ||
4119+
std::is_same<typename JsonType::binary_t, Type>::value ||
4120+
std::is_same<typename JsonType::object_t, Type>::value ||
4121+
std::is_same<typename JsonType::array_t, Type>::value);
4122+
};
4123+
4124+
template<typename BasicJsonType, typename CompatibleReferenceType>
4125+
struct is_compatible_reference_type
4126+
: is_compatible_reference_type_impl<BasicJsonType, CompatibleReferenceType> {};
4127+
41064128
template<typename T1, typename T2>
41074129
struct is_constructible_tuple : std::false_type {};
41084130

@@ -4860,6 +4882,63 @@ NLOHMANN_JSON_NAMESPACE_END
48604882

48614883
// #include <nlohmann/detail/meta/type_traits.hpp>
48624884

4885+
// #include <nlohmann/detail/meta/logic.hpp>
4886+
4887+
4888+
// #include <nlohmann/detail/macro_scope.hpp>
4889+
4890+
4891+
NLOHMANN_JSON_NAMESPACE_BEGIN
4892+
namespace detail
4893+
{
4894+
#ifdef JSON_HAS_CPP_17
4895+
4896+
template<bool... Booleans>
4897+
struct cxpr_or_impl : std::integral_constant < bool, (Booleans || ...) > {};
4898+
4899+
template<bool... Booleans>
4900+
struct cxpr_and_impl : std::integral_constant < bool, (Booleans &&...) > {};
4901+
4902+
#else
4903+
4904+
template<bool... Booleans>
4905+
struct cxpr_or_impl : std::false_type {};
4906+
4907+
template<bool... Booleans>
4908+
struct cxpr_or_impl<true, Booleans...> : std::true_type {};
4909+
4910+
template<bool... Booleans>
4911+
struct cxpr_or_impl<false, Booleans...> : cxpr_or_impl<Booleans...> {};
4912+
4913+
template<bool... Booleans>
4914+
struct cxpr_and_impl : std::true_type {};
4915+
4916+
template<bool... Booleans>
4917+
struct cxpr_and_impl<true, Booleans...> : cxpr_and_impl<Booleans...> {};
4918+
4919+
template<bool... Booleans>
4920+
struct cxpr_and_impl<false, Booleans...> : std::false_type {};
4921+
4922+
#endif
4923+
4924+
template<class Boolean>
4925+
struct cxpr_not : std::integral_constant < bool, !Boolean::value > {};
4926+
4927+
template<class... Booleans>
4928+
struct cxpr_or : cxpr_or_impl<Booleans::value...> {};
4929+
4930+
template<bool... Booleans>
4931+
struct cxpr_or_c : cxpr_or_impl<Booleans...> {};
4932+
4933+
template<class... Booleans>
4934+
struct cxpr_and : cxpr_and_impl<Booleans::value...> {};
4935+
4936+
template<bool... Booleans>
4937+
struct cxpr_and_c : cxpr_and_impl<Booleans...> {};
4938+
4939+
} // namespace detail
4940+
NLOHMANN_JSON_NAMESPACE_END
4941+
48634942
// #include <nlohmann/detail/string_concat.hpp>
48644943

48654944
// #include <nlohmann/detail/value_t.hpp>
@@ -5282,13 +5361,36 @@ inline void from_json(const BasicJsonType& j, ArithmeticType& val)
52825361
}
52835362
}
52845363

5285-
template<typename BasicJsonType, typename... Args, std::size_t... Idx>
5286-
std::tuple<Args...> from_json_tuple_impl_base(BasicJsonType&& j, index_sequence<Idx...> /*unused*/)
5364+
template<typename BasicJsonType, typename Type>
5365+
detail::uncvref_t<Type> from_json_tuple_get_impl(BasicJsonType&& j, detail::identity_tag<Type> /*unused*/, detail::priority_tag<0> /*unused*/)
52875366
{
5288-
return std::make_tuple(std::forward<BasicJsonType>(j).at(Idx).template get<Args>()...);
5367+
return std::forward<BasicJsonType>(j).template get<detail::uncvref_t<Type>>();
52895368
}
52905369

5291-
template<typename BasicJsonType>
5370+
template<typename BasicJsonType, typename Type,
5371+
detail::enable_if_t<detail::is_compatible_reference_type<BasicJsonType, Type>::value, int> = 0>
5372+
Type from_json_tuple_get_impl(BasicJsonType && j, detail::identity_tag<Type> /*unused*/, detail::priority_tag<1> /*unused*/)
5373+
{
5374+
return std::forward<BasicJsonType>(j).template get_ref<Type>();
5375+
}
5376+
5377+
template<typename BasicJsonType, typename Type,
5378+
detail::enable_if_t<std::is_arithmetic<uncvref_t<Type>>::value, int> = 0>
5379+
detail::uncvref_t<Type> from_json_tuple_get_impl(BasicJsonType && j, detail::identity_tag<Type> /*unused*/, detail::priority_tag<2> /*unused*/)
5380+
{
5381+
return std::forward<BasicJsonType>(j).template get<detail::uncvref_t<Type>>();
5382+
}
5383+
5384+
template<std::size_t PTagValue, typename BasicJsonType, typename... Types>
5385+
using tuple_type = std::tuple < decltype(from_json_tuple_get_impl(std::declval<BasicJsonType>(), detail::identity_tag<Types> {}, detail::priority_tag<PTagValue> {}))... >;
5386+
5387+
template<std::size_t PTagValue, typename... Args, typename BasicJsonType, std::size_t... Idx>
5388+
tuple_type<PTagValue, BasicJsonType, Args...> from_json_tuple_impl_base(BasicJsonType&& j, index_sequence<Idx...> /*unused*/)
5389+
{
5390+
return tuple_type<PTagValue, BasicJsonType, Args...>(from_json_tuple_get_impl(std::forward<BasicJsonType>(j).at(Idx), detail::identity_tag<Args> {}, detail::priority_tag<PTagValue> {})...);
5391+
}
5392+
5393+
template<std::size_t PTagValue, typename BasicJsonType>
52925394
std::tuple<> from_json_tuple_impl_base(BasicJsonType& /*unused*/, index_sequence<> /*unused*/)
52935395
{
52945396
return {};
@@ -5310,13 +5412,15 @@ inline void from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priori
53105412
template<typename BasicJsonType, typename... Args>
53115413
std::tuple<Args...> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/)
53125414
{
5313-
return from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
5415+
static_assert(cxpr_and<cxpr_or<cxpr_not<std::is_reference<Args>>, is_compatible_reference_type<BasicJsonType, Args>>...>::value,
5416+
"Can not return a tuple containing references to types not contained in a Json, try Json::get_to()");
5417+
return from_json_tuple_impl_base<1, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
53145418
}
53155419

53165420
template<typename BasicJsonType, typename... Args>
53175421
inline void from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)
53185422
{
5319-
t = from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
5423+
t = from_json_tuple_impl_base<2, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
53205424
}
53215425

53225426
template<typename BasicJsonType, typename TupleRelated>

tests/src/unit-constructor1.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,79 @@ TEST_CASE("constructors")
281281
// 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
282282
}
283283

284+
SECTION("std::tuple tie")
285+
{
286+
const auto a = 1.0;
287+
const auto* const b = "string";
288+
const auto c = 42;
289+
const auto d = std::vector<int> {0, 2};
290+
const size_t e = 1234;
291+
auto t = std::tie(a, b, c, d, e);
292+
json const j(t);
293+
294+
double a_out = 0;
295+
std::string b_out;
296+
int c_out = 0;
297+
std::vector<int> d_out;
298+
int64_t e_out = 0;
299+
auto t_out = std::tie(a_out, b_out, c_out, d_out, e_out);
300+
j.get_to(t_out);
301+
CHECK(a_out == a);
302+
CHECK(b_out == b);
303+
CHECK(c_out == c);
304+
CHECK(d_out == d);
305+
CHECK(e_out == e);
306+
}
307+
308+
SECTION("std::tuple of references to elements")
309+
{
310+
const auto a = 1.0;
311+
const auto* const b = "string";
312+
const auto c = 42;
313+
const size_t d = 1234;
314+
const auto t = std::tie(a, b, c, d);
315+
json const j(t);
316+
317+
auto t_out = j.get<std::tuple<const json::number_float_t&,
318+
const json::string_t&,
319+
const json::number_integer_t&,
320+
const json::number_unsigned_t&>>();
321+
CHECK(&std::get<0>(t_out) == j[0].get_ptr<const json::number_float_t*>());
322+
CHECK(&std::get<1>(t_out) == j[1].get_ptr<const json::string_t*>());
323+
CHECK(&std::get<2>(t_out) == j[2].get_ptr<const json::number_integer_t*>());
324+
CHECK(&std::get<3>(t_out) == j[3].get_ptr<const json::number_unsigned_t*>());
325+
CHECK(std::get<0>(t_out) == a);
326+
CHECK(std::get<1>(t_out) == b);
327+
CHECK(std::get<2>(t_out) == c);
328+
CHECK(std::get<3>(t_out) == d);
329+
}
330+
331+
SECTION("std::tuple mixed arithmetic types")
332+
{
333+
using j_float_t = json::number_float_t;
334+
using j_int_t = json::number_integer_t;
335+
using j_uint_t = json::number_unsigned_t;
336+
const j_float_t a = 1.0;
337+
const j_int_t b = 1234;
338+
const j_uint_t c = 42;
339+
json const j(std::tie(a, b, c, c));
340+
341+
auto t1 = j.get<std::tuple<j_int_t, j_uint_t, j_float_t, const j_uint_t&>>();
342+
j_uint_t a2 = 0;
343+
j_float_t b2 = 0;
344+
j_int_t c2 = 0;
345+
auto t2 = std::tie(a2, b2, c2);
346+
j.get_to(t2);
347+
348+
CHECK(std::get<0>(t1) == static_cast<j_int_t>(a));
349+
CHECK(std::get<1>(t1) == static_cast<j_uint_t>(b));
350+
CHECK(std::get<2>(t1) == static_cast<j_float_t>(c));
351+
// t1[3] exists only to force usage of the no-default-constructor version
352+
CHECK(a2 == static_cast<j_uint_t>(a));
353+
CHECK(b2 == static_cast<j_float_t>(b));
354+
CHECK(c2 == static_cast<j_int_t>(c));
355+
}
356+
284357
SECTION("std::pair/tuple/array failures")
285358
{
286359
json const j{1};

0 commit comments

Comments
 (0)