1 #pragma once
2
3 #include <cstddef> // size_t
4 #include <iterator> // input_iterator_tag
5 #include <string> // string, to_string
6 #include <tuple> // tuple_size, get, tuple_element
7 #include <utility> // move
8
9 #include <nlohmann/detail/meta/type_traits.hpp>
10 #include <nlohmann/detail/value_t.hpp>
11
12 namespace nlohmann
13 {
14 namespace detail
15 {
16 template<typename string_type>
int_to_string(string_type & target,std::size_t value)17 void int_to_string( string_type& target, std::size_t value )
18 {
19 // For ADL
20 using std::to_string;
21 target = to_string(value);
22 }
23 template<typename IteratorType> class iteration_proxy_value
24 {
25 public:
26 using difference_type = std::ptrdiff_t;
27 using value_type = iteration_proxy_value;
28 using pointer = value_type * ;
29 using reference = value_type & ;
30 using iterator_category = std::input_iterator_tag;
31 using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;
32
33 private:
34 /// the iterator
35 IteratorType anchor;
36 /// an index for arrays (used to create key names)
37 std::size_t array_index = 0;
38 /// last stringified array index
39 mutable std::size_t array_index_last = 0;
40 /// a string representation of the array index
41 mutable string_type array_index_str = "0";
42 /// an empty string (to return a reference for primitive values)
43 const string_type empty_str{};
44
45 public:
iteration_proxy_value(IteratorType it)46 explicit iteration_proxy_value(IteratorType it) noexcept
47 : anchor(std::move(it))
48 {}
49
50 /// dereference operator (needed for range-based for)
operator *()51 iteration_proxy_value& operator*()
52 {
53 return *this;
54 }
55
56 /// increment operator (needed for range-based for)
operator ++()57 iteration_proxy_value& operator++()
58 {
59 ++anchor;
60 ++array_index;
61
62 return *this;
63 }
64
65 /// equality operator (needed for InputIterator)
operator ==(const iteration_proxy_value & o) const66 bool operator==(const iteration_proxy_value& o) const
67 {
68 return anchor == o.anchor;
69 }
70
71 /// inequality operator (needed for range-based for)
operator !=(const iteration_proxy_value & o) const72 bool operator!=(const iteration_proxy_value& o) const
73 {
74 return anchor != o.anchor;
75 }
76
77 /// return key of the iterator
key() const78 const string_type& key() const
79 {
80 JSON_ASSERT(anchor.m_object != nullptr);
81
82 switch (anchor.m_object->type())
83 {
84 // use integer array index as key
85 case value_t::array:
86 {
87 if (array_index != array_index_last)
88 {
89 int_to_string( array_index_str, array_index );
90 array_index_last = array_index;
91 }
92 return array_index_str;
93 }
94
95 // use key from the object
96 case value_t::object:
97 return anchor.key();
98
99 // use an empty key for all primitive types
100 case value_t::null:
101 case value_t::string:
102 case value_t::boolean:
103 case value_t::number_integer:
104 case value_t::number_unsigned:
105 case value_t::number_float:
106 case value_t::binary:
107 case value_t::discarded:
108 default:
109 return empty_str;
110 }
111 }
112
113 /// return value of the iterator
value() const114 typename IteratorType::reference value() const
115 {
116 return anchor.value();
117 }
118 };
119
120 /// proxy class for the items() function
121 template<typename IteratorType> class iteration_proxy
122 {
123 private:
124 /// the container to iterate
125 typename IteratorType::reference container;
126
127 public:
128 /// construct iteration proxy from a container
iteration_proxy(typename IteratorType::reference cont)129 explicit iteration_proxy(typename IteratorType::reference cont) noexcept
130 : container(cont) {}
131
132 /// return iterator begin (needed for range-based for)
begin()133 iteration_proxy_value<IteratorType> begin() noexcept
134 {
135 return iteration_proxy_value<IteratorType>(container.begin());
136 }
137
138 /// return iterator end (needed for range-based for)
end()139 iteration_proxy_value<IteratorType> end() noexcept
140 {
141 return iteration_proxy_value<IteratorType>(container.end());
142 }
143 };
144 // Structured Bindings Support
145 // For further reference see https://blog.tartanllama.xyz/structured-bindings/
146 // And see https://github.com/nlohmann/json/pull/1391
147 template<std::size_t N, typename IteratorType, enable_if_t<N == 0, int> = 0>
get(const nlohmann::detail::iteration_proxy_value<IteratorType> & i)148 auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.key())
149 {
150 return i.key();
151 }
152 // Structured Bindings Support
153 // For further reference see https://blog.tartanllama.xyz/structured-bindings/
154 // And see https://github.com/nlohmann/json/pull/1391
155 template<std::size_t N, typename IteratorType, enable_if_t<N == 1, int> = 0>
get(const nlohmann::detail::iteration_proxy_value<IteratorType> & i)156 auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.value())
157 {
158 return i.value();
159 }
160 } // namespace detail
161 } // namespace nlohmann
162
163 // The Addition to the STD Namespace is required to add
164 // Structured Bindings Support to the iteration_proxy_value class
165 // For further reference see https://blog.tartanllama.xyz/structured-bindings/
166 // And see https://github.com/nlohmann/json/pull/1391
167 namespace std
168 {
169 #if defined(__clang__)
170 // Fix: https://github.com/nlohmann/json/issues/1401
171 #pragma clang diagnostic push
172 #pragma clang diagnostic ignored "-Wmismatched-tags"
173 #endif
174 template<typename IteratorType>
175 class tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>>
176 : public std::integral_constant<std::size_t, 2> {};
177
178 template<std::size_t N, typename IteratorType>
179 class tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >>
180 {
181 public:
182 using type = decltype(
183 get<N>(std::declval <
184 ::nlohmann::detail::iteration_proxy_value<IteratorType >> ()));
185 };
186 #if defined(__clang__)
187 #pragma clang diagnostic pop
188 #endif
189 } // namespace std
190