1 | #pragma once |
2 | |
3 | #include <ciso646> // not |
4 | #include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next |
5 | #include <type_traits> // conditional, is_const, remove_const |
6 | |
7 | #include <nlohmann/detail/exceptions.hpp> |
8 | #include <nlohmann/detail/iterators/internal_iterator.hpp> |
9 | #include <nlohmann/detail/iterators/primitive_iterator.hpp> |
10 | #include <nlohmann/detail/macro_scope.hpp> |
11 | #include <nlohmann/detail/meta/cpp_future.hpp> |
12 | #include <nlohmann/detail/meta/type_traits.hpp> |
13 | #include <nlohmann/detail/value_t.hpp> |
14 | |
15 | namespace nlohmann |
16 | { |
17 | namespace detail |
18 | { |
19 | // forward declare, to be able to friend it later on |
20 | template<typename IteratorType> class iteration_proxy; |
21 | template<typename IteratorType> class iteration_proxy_value; |
22 | |
23 | /*! |
24 | @brief a template for a bidirectional iterator for the @ref basic_json class |
25 | This class implements a both iterators (iterator and const_iterator) for the |
26 | @ref basic_json class. |
27 | @note An iterator is called *initialized* when a pointer to a JSON value has |
28 | been set (e.g., by a constructor or a copy assignment). If the iterator is |
29 | default-constructed, it is *uninitialized* and most methods are undefined. |
30 | **The library uses assertions to detect calls on uninitialized iterators.** |
31 | @requirement The class satisfies the following concept requirements: |
32 | - |
33 | [BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): |
34 | The iterator that can be moved can be moved in both directions (i.e. |
35 | incremented and decremented). |
36 | @since version 1.0.0, simplified in version 2.0.9, change to bidirectional |
37 | iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593) |
38 | */ |
39 | template<typename BasicJsonType> |
40 | class iter_impl |
41 | { |
42 | /// allow basic_json to access private members |
43 | friend iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>; |
44 | friend BasicJsonType; |
45 | friend iteration_proxy<iter_impl>; |
46 | friend iteration_proxy_value<iter_impl>; |
47 | |
48 | using object_t = typename BasicJsonType::object_t; |
49 | using array_t = typename BasicJsonType::array_t; |
50 | // make sure BasicJsonType is basic_json or const basic_json |
51 | static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value, |
52 | "iter_impl only accepts (const) basic_json" ); |
53 | |
54 | public: |
55 | |
56 | /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. |
57 | /// The C++ Standard has never required user-defined iterators to derive from std::iterator. |
58 | /// A user-defined iterator should provide publicly accessible typedefs named |
59 | /// iterator_category, value_type, difference_type, pointer, and reference. |
60 | /// Note that value_type is required to be non-const, even for constant iterators. |
61 | using iterator_category = std::bidirectional_iterator_tag; |
62 | |
63 | /// the type of the values when the iterator is dereferenced |
64 | using value_type = typename BasicJsonType::value_type; |
65 | /// a type to represent differences between iterators |
66 | using difference_type = typename BasicJsonType::difference_type; |
67 | /// defines a pointer to the type iterated over (value_type) |
68 | using pointer = typename std::conditional<std::is_const<BasicJsonType>::value, |
69 | typename BasicJsonType::const_pointer, |
70 | typename BasicJsonType::pointer>::type; |
71 | /// defines a reference to the type iterated over (value_type) |
72 | using reference = |
73 | typename std::conditional<std::is_const<BasicJsonType>::value, |
74 | typename BasicJsonType::const_reference, |
75 | typename BasicJsonType::reference>::type; |
76 | |
77 | /// default constructor |
78 | iter_impl() = default; |
79 | |
80 | /*! |
81 | @brief constructor for a given JSON instance |
82 | @param[in] object pointer to a JSON object for this iterator |
83 | @pre object != nullptr |
84 | @post The iterator is initialized; i.e. `m_object != nullptr`. |
85 | */ |
86 | explicit iter_impl(pointer object) noexcept : m_object(object) |
87 | { |
88 | assert(m_object != nullptr); |
89 | |
90 | switch (m_object->m_type) |
91 | { |
92 | case value_t::object: |
93 | { |
94 | m_it.object_iterator = typename object_t::iterator(); |
95 | break; |
96 | } |
97 | |
98 | case value_t::array: |
99 | { |
100 | m_it.array_iterator = typename array_t::iterator(); |
101 | break; |
102 | } |
103 | |
104 | default: |
105 | { |
106 | m_it.primitive_iterator = primitive_iterator_t(); |
107 | break; |
108 | } |
109 | } |
110 | } |
111 | |
112 | /*! |
113 | @note The conventional copy constructor and copy assignment are implicitly |
114 | defined. Combined with the following converting constructor and |
115 | assignment, they support: (1) copy from iterator to iterator, (2) |
116 | copy from const iterator to const iterator, and (3) conversion from |
117 | iterator to const iterator. However conversion from const iterator |
118 | to iterator is not defined. |
119 | */ |
120 | |
121 | /*! |
122 | @brief const copy constructor |
123 | @param[in] other const iterator to copy from |
124 | @note This copy constructor had to be defined explicitly to circumvent a bug |
125 | occurring on msvc v19.0 compiler (VS 2015) debug build. For more |
126 | information refer to: https://github.com/nlohmann/json/issues/1608 |
127 | */ |
128 | iter_impl(const iter_impl<const BasicJsonType>& other) noexcept |
129 | : m_object(other.m_object), m_it(other.m_it) |
130 | {} |
131 | |
132 | /*! |
133 | @brief converting assignment |
134 | @param[in] other const iterator to copy from |
135 | @return const/non-const iterator |
136 | @note It is not checked whether @a other is initialized. |
137 | */ |
138 | iter_impl& operator=(const iter_impl<const BasicJsonType>& other) noexcept |
139 | { |
140 | m_object = other.m_object; |
141 | m_it = other.m_it; |
142 | return *this; |
143 | } |
144 | |
145 | /*! |
146 | @brief converting constructor |
147 | @param[in] other non-const iterator to copy from |
148 | @note It is not checked whether @a other is initialized. |
149 | */ |
150 | iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept |
151 | : m_object(other.m_object), m_it(other.m_it) |
152 | {} |
153 | |
154 | /*! |
155 | @brief converting assignment |
156 | @param[in] other non-const iterator to copy from |
157 | @return const/non-const iterator |
158 | @note It is not checked whether @a other is initialized. |
159 | */ |
160 | iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept |
161 | { |
162 | m_object = other.m_object; |
163 | m_it = other.m_it; |
164 | return *this; |
165 | } |
166 | |
167 | private: |
168 | /*! |
169 | @brief set the iterator to the first value |
170 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
171 | */ |
172 | void set_begin() noexcept |
173 | { |
174 | assert(m_object != nullptr); |
175 | |
176 | switch (m_object->m_type) |
177 | { |
178 | case value_t::object: |
179 | { |
180 | m_it.object_iterator = m_object->m_value.object->begin(); |
181 | break; |
182 | } |
183 | |
184 | case value_t::array: |
185 | { |
186 | m_it.array_iterator = m_object->m_value.array->begin(); |
187 | break; |
188 | } |
189 | |
190 | case value_t::null: |
191 | { |
192 | // set to end so begin()==end() is true: null is empty |
193 | m_it.primitive_iterator.set_end(); |
194 | break; |
195 | } |
196 | |
197 | default: |
198 | { |
199 | m_it.primitive_iterator.set_begin(); |
200 | break; |
201 | } |
202 | } |
203 | } |
204 | |
205 | /*! |
206 | @brief set the iterator past the last value |
207 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
208 | */ |
209 | void set_end() noexcept |
210 | { |
211 | assert(m_object != nullptr); |
212 | |
213 | switch (m_object->m_type) |
214 | { |
215 | case value_t::object: |
216 | { |
217 | m_it.object_iterator = m_object->m_value.object->end(); |
218 | break; |
219 | } |
220 | |
221 | case value_t::array: |
222 | { |
223 | m_it.array_iterator = m_object->m_value.array->end(); |
224 | break; |
225 | } |
226 | |
227 | default: |
228 | { |
229 | m_it.primitive_iterator.set_end(); |
230 | break; |
231 | } |
232 | } |
233 | } |
234 | |
235 | public: |
236 | /*! |
237 | @brief return a reference to the value pointed to by the iterator |
238 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
239 | */ |
240 | reference operator*() const |
241 | { |
242 | assert(m_object != nullptr); |
243 | |
244 | switch (m_object->m_type) |
245 | { |
246 | case value_t::object: |
247 | { |
248 | assert(m_it.object_iterator != m_object->m_value.object->end()); |
249 | return m_it.object_iterator->second; |
250 | } |
251 | |
252 | case value_t::array: |
253 | { |
254 | assert(m_it.array_iterator != m_object->m_value.array->end()); |
255 | return *m_it.array_iterator; |
256 | } |
257 | |
258 | case value_t::null: |
259 | JSON_THROW(invalid_iterator::create(214, "cannot get value" )); |
260 | |
261 | default: |
262 | { |
263 | if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) |
264 | { |
265 | return *m_object; |
266 | } |
267 | |
268 | JSON_THROW(invalid_iterator::create(214, "cannot get value" )); |
269 | } |
270 | } |
271 | } |
272 | |
273 | /*! |
274 | @brief dereference the iterator |
275 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
276 | */ |
277 | pointer operator->() const |
278 | { |
279 | assert(m_object != nullptr); |
280 | |
281 | switch (m_object->m_type) |
282 | { |
283 | case value_t::object: |
284 | { |
285 | assert(m_it.object_iterator != m_object->m_value.object->end()); |
286 | return &(m_it.object_iterator->second); |
287 | } |
288 | |
289 | case value_t::array: |
290 | { |
291 | assert(m_it.array_iterator != m_object->m_value.array->end()); |
292 | return &*m_it.array_iterator; |
293 | } |
294 | |
295 | default: |
296 | { |
297 | if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) |
298 | { |
299 | return m_object; |
300 | } |
301 | |
302 | JSON_THROW(invalid_iterator::create(214, "cannot get value" )); |
303 | } |
304 | } |
305 | } |
306 | |
307 | /*! |
308 | @brief post-increment (it++) |
309 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
310 | */ |
311 | iter_impl const operator++(int) |
312 | { |
313 | auto result = *this; |
314 | ++(*this); |
315 | return result; |
316 | } |
317 | |
318 | /*! |
319 | @brief pre-increment (++it) |
320 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
321 | */ |
322 | iter_impl& operator++() |
323 | { |
324 | assert(m_object != nullptr); |
325 | |
326 | switch (m_object->m_type) |
327 | { |
328 | case value_t::object: |
329 | { |
330 | std::advance(m_it.object_iterator, 1); |
331 | break; |
332 | } |
333 | |
334 | case value_t::array: |
335 | { |
336 | std::advance(m_it.array_iterator, 1); |
337 | break; |
338 | } |
339 | |
340 | default: |
341 | { |
342 | ++m_it.primitive_iterator; |
343 | break; |
344 | } |
345 | } |
346 | |
347 | return *this; |
348 | } |
349 | |
350 | /*! |
351 | @brief post-decrement (it--) |
352 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
353 | */ |
354 | iter_impl const operator--(int) |
355 | { |
356 | auto result = *this; |
357 | --(*this); |
358 | return result; |
359 | } |
360 | |
361 | /*! |
362 | @brief pre-decrement (--it) |
363 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
364 | */ |
365 | iter_impl& operator--() |
366 | { |
367 | assert(m_object != nullptr); |
368 | |
369 | switch (m_object->m_type) |
370 | { |
371 | case value_t::object: |
372 | { |
373 | std::advance(m_it.object_iterator, -1); |
374 | break; |
375 | } |
376 | |
377 | case value_t::array: |
378 | { |
379 | std::advance(m_it.array_iterator, -1); |
380 | break; |
381 | } |
382 | |
383 | default: |
384 | { |
385 | --m_it.primitive_iterator; |
386 | break; |
387 | } |
388 | } |
389 | |
390 | return *this; |
391 | } |
392 | |
393 | /*! |
394 | @brief comparison: equal |
395 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
396 | */ |
397 | bool operator==(const iter_impl& other) const |
398 | { |
399 | // if objects are not the same, the comparison is undefined |
400 | if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) |
401 | { |
402 | JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers" )); |
403 | } |
404 | |
405 | assert(m_object != nullptr); |
406 | |
407 | switch (m_object->m_type) |
408 | { |
409 | case value_t::object: |
410 | return (m_it.object_iterator == other.m_it.object_iterator); |
411 | |
412 | case value_t::array: |
413 | return (m_it.array_iterator == other.m_it.array_iterator); |
414 | |
415 | default: |
416 | return (m_it.primitive_iterator == other.m_it.primitive_iterator); |
417 | } |
418 | } |
419 | |
420 | /*! |
421 | @brief comparison: not equal |
422 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
423 | */ |
424 | bool operator!=(const iter_impl& other) const |
425 | { |
426 | return not operator==(other); |
427 | } |
428 | |
429 | /*! |
430 | @brief comparison: smaller |
431 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
432 | */ |
433 | bool operator<(const iter_impl& other) const |
434 | { |
435 | // if objects are not the same, the comparison is undefined |
436 | if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) |
437 | { |
438 | JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers" )); |
439 | } |
440 | |
441 | assert(m_object != nullptr); |
442 | |
443 | switch (m_object->m_type) |
444 | { |
445 | case value_t::object: |
446 | JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators" )); |
447 | |
448 | case value_t::array: |
449 | return (m_it.array_iterator < other.m_it.array_iterator); |
450 | |
451 | default: |
452 | return (m_it.primitive_iterator < other.m_it.primitive_iterator); |
453 | } |
454 | } |
455 | |
456 | /*! |
457 | @brief comparison: less than or equal |
458 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
459 | */ |
460 | bool operator<=(const iter_impl& other) const |
461 | { |
462 | return not other.operator < (*this); |
463 | } |
464 | |
465 | /*! |
466 | @brief comparison: greater than |
467 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
468 | */ |
469 | bool operator>(const iter_impl& other) const |
470 | { |
471 | return not operator<=(other); |
472 | } |
473 | |
474 | /*! |
475 | @brief comparison: greater than or equal |
476 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
477 | */ |
478 | bool operator>=(const iter_impl& other) const |
479 | { |
480 | return not operator<(other); |
481 | } |
482 | |
483 | /*! |
484 | @brief add to iterator |
485 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
486 | */ |
487 | iter_impl& operator+=(difference_type i) |
488 | { |
489 | assert(m_object != nullptr); |
490 | |
491 | switch (m_object->m_type) |
492 | { |
493 | case value_t::object: |
494 | JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators" )); |
495 | |
496 | case value_t::array: |
497 | { |
498 | std::advance(m_it.array_iterator, i); |
499 | break; |
500 | } |
501 | |
502 | default: |
503 | { |
504 | m_it.primitive_iterator += i; |
505 | break; |
506 | } |
507 | } |
508 | |
509 | return *this; |
510 | } |
511 | |
512 | /*! |
513 | @brief subtract from iterator |
514 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
515 | */ |
516 | iter_impl& operator-=(difference_type i) |
517 | { |
518 | return operator+=(-i); |
519 | } |
520 | |
521 | /*! |
522 | @brief add to iterator |
523 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
524 | */ |
525 | iter_impl operator+(difference_type i) const |
526 | { |
527 | auto result = *this; |
528 | result += i; |
529 | return result; |
530 | } |
531 | |
532 | /*! |
533 | @brief addition of distance and iterator |
534 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
535 | */ |
536 | friend iter_impl operator+(difference_type i, const iter_impl& it) |
537 | { |
538 | auto result = it; |
539 | result += i; |
540 | return result; |
541 | } |
542 | |
543 | /*! |
544 | @brief subtract from iterator |
545 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
546 | */ |
547 | iter_impl operator-(difference_type i) const |
548 | { |
549 | auto result = *this; |
550 | result -= i; |
551 | return result; |
552 | } |
553 | |
554 | /*! |
555 | @brief return difference |
556 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
557 | */ |
558 | difference_type operator-(const iter_impl& other) const |
559 | { |
560 | assert(m_object != nullptr); |
561 | |
562 | switch (m_object->m_type) |
563 | { |
564 | case value_t::object: |
565 | JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators" )); |
566 | |
567 | case value_t::array: |
568 | return m_it.array_iterator - other.m_it.array_iterator; |
569 | |
570 | default: |
571 | return m_it.primitive_iterator - other.m_it.primitive_iterator; |
572 | } |
573 | } |
574 | |
575 | /*! |
576 | @brief access to successor |
577 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
578 | */ |
579 | reference operator[](difference_type n) const |
580 | { |
581 | assert(m_object != nullptr); |
582 | |
583 | switch (m_object->m_type) |
584 | { |
585 | case value_t::object: |
586 | JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators" )); |
587 | |
588 | case value_t::array: |
589 | return *std::next(m_it.array_iterator, n); |
590 | |
591 | case value_t::null: |
592 | JSON_THROW(invalid_iterator::create(214, "cannot get value" )); |
593 | |
594 | default: |
595 | { |
596 | if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n)) |
597 | { |
598 | return *m_object; |
599 | } |
600 | |
601 | JSON_THROW(invalid_iterator::create(214, "cannot get value" )); |
602 | } |
603 | } |
604 | } |
605 | |
606 | /*! |
607 | @brief return the key of an object iterator |
608 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
609 | */ |
610 | const typename object_t::key_type& key() const |
611 | { |
612 | assert(m_object != nullptr); |
613 | |
614 | if (JSON_HEDLEY_LIKELY(m_object->is_object())) |
615 | { |
616 | return m_it.object_iterator->first; |
617 | } |
618 | |
619 | JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators" )); |
620 | } |
621 | |
622 | /*! |
623 | @brief return the value of an iterator |
624 | @pre The iterator is initialized; i.e. `m_object != nullptr`. |
625 | */ |
626 | reference value() const |
627 | { |
628 | return operator*(); |
629 | } |
630 | |
631 | private: |
632 | /// associated JSON instance |
633 | pointer m_object = nullptr; |
634 | /// the actual iterator of the associated instance |
635 | internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it {}; |
636 | }; |
637 | } // namespace detail |
638 | } // namespace nlohmann |
639 | |