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