diff --git a/README.md b/README.md index 9b1c6f07..28d2dc3d 100644 --- a/README.md +++ b/README.md @@ -214,6 +214,14 @@ j.empty(); // false j.type(); // json::value_t::array j.clear(); // the array is empty again +// convenience type checkers +j.is_null(); +j.is_boolean(); +j.is_number(); +j.is_object(); +j.is_array(); +j.is_string(); + // comparison j == "[\"foo\", 1, true]"_json; // true diff --git a/json.gif b/json.gif new file mode 100644 index 00000000..7e52693c Binary files /dev/null and b/json.gif differ diff --git a/src/json.hpp b/src/json.hpp index 14517db8..66756d90 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -7,6 +7,26 @@ @see https://github.com/nlohmann/json */ +/*! +@defgroup container Container +@brief C++ Container concept + +A Container is an object used to store other objects and taking care of the +management of the memory used by the objects it contains. + +@see http://en.cppreference.com/w/cpp/concept/Container + +@defgroup reversiblecontainer Reversible Container +@brief C++ Reversible Container concept +@ingroup container + +A ReversibleContainer is a Container that has iterators that meet the +requirements of either BidirectionalIterator or RandomAccessIterator. Such +iterators allow a ReversibleContainer to be iterated over in reverse. + +@see http://en.cppreference.com/w/cpp/concept/ReversibleContainer +*/ + #ifndef _NLOHMANN_JSON #define _NLOHMANN_JSON @@ -27,6 +47,7 @@ #include /*! +@brief namespace for Niels Lohmann @see https://github.com/nlohmann */ namespace nlohmann @@ -75,30 +96,76 @@ class basic_json class iterator; class const_iterator; - /// the type of elements in a basic_json container + /*! + @brief the type of elements in a basic_json container + @ingroup container + */ using value_type = basic_json; - /// the type of an element reference + + /*! + @brief the type of an element reference + @ingroup container + */ using reference = basic_json&; - /// the type of an element const reference + + /*! + @brief the type of an element const reference + @ingroup container + */ using const_reference = const basic_json&; + + /*! + @brief a type to represent differences between iterators + @ingroup container + */ + using difference_type = std::ptrdiff_t; + + /*! + @brief a type to represent container sizes + @ingroup container + */ + using size_type = std::size_t; + + /// the allocator type + using allocator_type = Allocator; + /// the type of an element pointer using pointer = basic_json*; /// the type of an element const pointer using const_pointer = const basic_json*; - /// a type to represent differences between iterators - using difference_type = std::ptrdiff_t; - /// a type to represent container sizes - using size_type = std::size_t; - /// an iterator for a basic_json container + + /*! + @brief an iterator for a basic_json container + @ingroup container + */ using iterator = basic_json::iterator; - /// a const iterator for a basic_json container + + /*! + @brief a const iterator for a basic_json container + @ingroup container + */ using const_iterator = basic_json::const_iterator; - /// a reverse iterator for a basic_json container + + /*! + @brief a reverse iterator for a basic_json container + @ingroup reversiblecontainer + */ using reverse_iterator = std::reverse_iterator; - /// a const reverse iterator for a basic_json container + + /*! + @brief a const reverse iterator for a basic_json container + @ingroup reversiblecontainer + */ using const_reverse_iterator = std::reverse_iterator; + /// returns the allocator associated with the container + inline allocator_type get_allocator() const + { + return allocator_type(); + } + + /////////////////////////// // JSON value data types // /////////////////////////// @@ -166,6 +233,85 @@ class basic_json number_float ///< number value (floating-point) }; + /*! + @brief comparison operator for JSON value types + + Returns an ordering that is similar to Python: + - order: null < boolean < number < object < array < string + - furthermore, each type is not smaller than itself + */ + friend bool operator<(const value_t lhs, const value_t rhs) + { + // no type is smaller than itself + if (lhs == rhs) + { + return false; + } + + switch (lhs) + { + case (value_t::null): + { + // nulls are smaller than all other types + return true; + } + + case (value_t::boolean): + { + // only nulls are smaller than booleans + return (rhs != value_t::null); + } + + case (value_t::number_float): + case (value_t::number_integer): + { + switch (rhs) + { + // numbers are smaller than objects, arrays, and string + case (value_t::object): + case (value_t::array): + case (value_t::string): + { + return true; + } + default: + { + return false; + } + } + } + + case (value_t::object): + { + switch (rhs) + { + // objects are smaller than arrays and string + case (value_t::array): + case (value_t::string): + { + return true; + } + default: + { + return false; + } + } + } + + case (value_t::array): + { + // arrays are smaller than strings + return (rhs == value_t::string); + } + + default: + { + // a string is not smaller than any other types + return false; + } + } + } + ////////////////// // constructors // @@ -226,10 +372,11 @@ class basic_json } } - /// create a null object (implicitly) - inline basic_json() noexcept - : m_type(value_t::null) - {} + /*! + @brief create a null object (implicitly) + @ingroup container + */ + inline basic_json() noexcept = default; /// create a null object (explicitly) inline basic_json(std::nullptr_t) noexcept @@ -424,11 +571,15 @@ class basic_json return basic_json(l, false, value_t::object); } + /////////////////////////////////////// // other constructors and destructor // /////////////////////////////////////// - /// copy constructor + /*! + @brief copy constructor + @ingroup container + */ inline basic_json(const basic_json& other) : m_type(other.m_type) { @@ -487,7 +638,10 @@ class basic_json other.m_value = {}; } - /// copy assignment + /*! + @brief copy assignment + @ingroup container + */ inline reference& operator=(basic_json other) noexcept ( std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value and @@ -500,7 +654,10 @@ class basic_json return *this; } - /// destructor + /*! + @brief destructor + @ingroup container + */ inline ~basic_json() noexcept { switch (m_type) @@ -577,6 +734,42 @@ class basic_json return m_type; } + // return whether value is null + inline bool is_null() const noexcept + { + return m_type == value_t::null; + } + + // return whether value is boolean + inline bool is_boolean() const noexcept + { + return m_type == value_t::boolean; + } + + // return whether value is number + inline bool is_number() const noexcept + { + return (m_type == value_t::number_integer) or (m_type == value_t::number_float); + } + + // return whether value is object + inline bool is_object() const noexcept + { + return m_type == value_t::object; + } + + // return whether value is array + inline bool is_array() const noexcept + { + return m_type == value_t::array; + } + + // return whether value is string + inline bool is_string() const noexcept + { + return m_type == value_t::string; + } + /// return the type of the object (implicit) operator value_t() const noexcept { @@ -871,7 +1064,10 @@ class basic_json // iterators // /////////////// - /// returns an iterator to the beginning of the container + /*! + @brief returns an iterator to the first element + @ingroup container + */ inline iterator begin() noexcept { iterator result(this); @@ -879,7 +1075,10 @@ class basic_json return result; } - /// returns a const iterator to the beginning of the container + /*! + @brief returns a const iterator to the first element + @ingroup container + */ inline const_iterator begin() const noexcept { const_iterator result(this); @@ -887,7 +1086,10 @@ class basic_json return result; } - /// returns a const iterator to the beginning of the container + /*! + @brief returns a const iterator to the first element + @ingroup container + */ inline const_iterator cbegin() const noexcept { const_iterator result(this); @@ -895,7 +1097,10 @@ class basic_json return result; } - /// returns an iterator to the end of the container + /*! + @brief returns an iterator to one past the last element + @ingroup container + */ inline iterator end() noexcept { iterator result(this); @@ -903,7 +1108,10 @@ class basic_json return result; } - /// returns a const iterator to the end of the container + /*! + @brief returns a const iterator to one past the last element + @ingroup container + */ inline const_iterator end() const noexcept { const_iterator result(this); @@ -911,7 +1119,10 @@ class basic_json return result; } - /// returns a const iterator to the end of the container + /*! + @brief returns a const iterator to one past the last element + @ingroup container + */ inline const_iterator cend() const noexcept { const_iterator result(this); @@ -919,37 +1130,55 @@ class basic_json return result; } - /// returns a reverse iterator to the beginning + /*! + @brief returns a reverse iterator to the first element + @ingroup reversiblecontainer + */ inline reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } - /// returns a reverse iterator to the beginning + /*! + @brief returns a const reverse iterator to the first element + @ingroup reversiblecontainer + */ inline const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } - /// returns a reverse iterator to the end + /*! + @brief returns a reverse iterator to one past the last element + @ingroup reversiblecontainer + */ inline reverse_iterator rend() noexcept { return reverse_iterator(begin()); } - /// returns a reverse iterator to the end + /*! + @brief returns a const reverse iterator to one past the last element + @ingroup reversiblecontainer + */ inline const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } - /// returns a reverse iterator to the beginning + /*! + @brief returns a const reverse iterator to the first element + @ingroup reversiblecontainer + */ inline const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(cend()); } - /// returns a reverse iterator to the end + /*! + @brief returns a const reverse iterator to one past the last element + @ingroup reversiblecontainer + */ inline const_reverse_iterator crend() const noexcept { return const_reverse_iterator(cbegin()); @@ -960,7 +1189,10 @@ class basic_json // capacity // ////////////// - /// checks whether the container is empty + /*! + @brief checks whether the container is empty + @ingroup container + */ inline bool empty() const noexcept { switch (m_type) @@ -988,7 +1220,10 @@ class basic_json } } - /// returns the number of elements + /*! + @brief returns the number of elements + @ingroup container + */ inline size_type size() const noexcept { switch (m_type) @@ -1016,7 +1251,10 @@ class basic_json } } - /// returns the maximum possible number of elements + /*! + @brief returns the maximum possible number of elements + @ingroup container + */ inline size_type max_size() const noexcept { switch (m_type) @@ -1186,7 +1424,10 @@ class basic_json return operator[](value.first); } - /// swaps the contents + /*! + @brief exchanges the values + @ingroup container + */ inline void swap(reference other) noexcept ( std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value and @@ -1242,7 +1483,10 @@ class basic_json // lexicographical comparison operators // ////////////////////////////////////////// - /// comparison: equal + /*! + @brief comparison: equal + @ingroup container + */ friend bool operator==(const_reference lhs, const_reference rhs) { switch (lhs.type()) @@ -1316,7 +1560,10 @@ class basic_json return false; } - /// comparison: not equal + /*! + @brief comparison: not equal + @ingroup container + */ friend bool operator!=(const_reference lhs, const_reference rhs) { return not (lhs == rhs); @@ -1393,7 +1640,9 @@ class basic_json } } - return false; + // We only reach this line if we cannot compare values. In that case, + // we compare types. + return lhs.type() < rhs.type(); } /// comparison: less than or equal @@ -1743,19 +1992,6 @@ class basic_json // iterators // /////////////// - /// values of a generic iterator type of non-container JSON values - enum class generic_iterator_value - { - /// the iterator was not initialized - uninitialized, - /// the iterator points to the only value - begin, - /// the iterator points past the only value - end, - /// the iterator points to an invalid value - invalid - }; - /// an iterator value template union internal_iterator @@ -1765,21 +2001,15 @@ class basic_json /// iterator for JSON arrays array_iterator_t array_iterator; /// generic iteraotr for all other value types - generic_iterator_value generic_iterator; + difference_type generic_iterator; /// default constructor - internal_iterator() : generic_iterator(generic_iterator_value::uninitialized) {} - /// constructor for object iterators - internal_iterator(object_iterator_t v) : object_iterator(v) {} - /// constructor for array iterators - internal_iterator(array_iterator_t v) : array_iterator(v) {} - /// constructor for generic iterators - internal_iterator(generic_iterator_value v) : generic_iterator(v) {} + internal_iterator() : generic_iterator(-1) {} }; public: - /// a bidirectional iterator for the basic_json class - class iterator : public std::iterator + /// a random access iterator for the basic_json class + class iterator : public std::iterator { public: /// the type of the values when the iterator is dereferenced @@ -1813,7 +2043,7 @@ class basic_json } default: { - m_it.generic_iterator = generic_iterator_value::uninitialized; + m_it.generic_iterator = -1; break; } } @@ -1847,13 +2077,13 @@ class basic_json case (basic_json::value_t::null): { // set to end so begin()==end() is true: null is empty - m_it.generic_iterator = generic_iterator_value::end; + m_it.generic_iterator = 1; break; } default: { - m_it.generic_iterator = generic_iterator_value::begin; + m_it.generic_iterator = 0; break; } } @@ -1878,14 +2108,14 @@ class basic_json default: { - m_it.generic_iterator = generic_iterator_value::end; + m_it.generic_iterator = 1; break; } } } /// return a reference to the value pointed to by the iterator - inline reference operator*() const + inline reference operator*() { switch (m_object->m_type) { @@ -1906,7 +2136,7 @@ class basic_json default: { - if (m_it.generic_iterator == generic_iterator_value::begin) + if (m_it.generic_iterator == 0) { return *m_object; } @@ -1919,7 +2149,7 @@ class basic_json } /// dereference the iterator - inline pointer operator->() const + inline pointer operator->() { switch (m_object->m_type) { @@ -1933,9 +2163,14 @@ class basic_json return &*m_it.array_iterator; } + case (basic_json::value_t::null): + { + throw std::out_of_range("cannot get value"); + } + default: { - if (m_it.generic_iterator == generic_iterator_value::begin) + if (m_it.generic_iterator == 0) { return m_object; } @@ -1950,7 +2185,7 @@ class basic_json /// post-increment (it++) inline iterator operator++(int) { - iterator result = *this; + auto result = *this; switch (m_object->m_type) { @@ -1968,14 +2203,7 @@ class basic_json default: { - if (m_it.generic_iterator == generic_iterator_value::begin) - { - m_it.generic_iterator = generic_iterator_value::end; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } + m_it.generic_iterator++; break; } } @@ -2002,14 +2230,7 @@ class basic_json default: { - if (m_it.generic_iterator == generic_iterator_value::begin) - { - m_it.generic_iterator = generic_iterator_value::end; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } + ++m_it.generic_iterator; break; } } @@ -2020,7 +2241,7 @@ class basic_json /// post-decrement (it--) inline iterator operator--(int) { - iterator result = *this; + auto result = *this; switch (m_object->m_type) { @@ -2036,22 +2257,9 @@ class basic_json break; } - case (basic_json::value_t::null): - { - m_it.generic_iterator = generic_iterator_value::invalid; - break; - } - default: { - if (m_it.generic_iterator == generic_iterator_value::end) - { - m_it.generic_iterator = generic_iterator_value::begin; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } + m_it.generic_iterator--; break; } } @@ -2076,22 +2284,9 @@ class basic_json break; } - case (basic_json::value_t::null): - { - m_it.generic_iterator = generic_iterator_value::invalid; - break; - } - default: { - if (m_it.generic_iterator == generic_iterator_value::end) - { - m_it.generic_iterator = generic_iterator_value::begin; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } + --m_it.generic_iterator; break; } } @@ -2102,9 +2297,10 @@ class basic_json /// comparison: equal inline bool operator==(const iterator& other) const { - if (m_object != other.m_object or m_object->m_type != other.m_object->m_type) + // if objects are not the same, the comparison is undefined + if (m_object != other.m_object) { - return false; + throw std::domain_error("cannot compare iterators of different containers"); } switch (m_object->m_type) @@ -2132,6 +2328,157 @@ class basic_json return not operator==(other); } + /// comparison: smaller + inline bool operator<(const iterator& other) const + { + // if objects are not the same, the comparison is undefined + if (m_object != other.m_object) + { + throw std::domain_error("cannot compare iterators of different containers"); + } + + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + throw std::domain_error("cannot use operator< for object iterators"); + } + + case (basic_json::value_t::array): + { + return (m_it.array_iterator < other.m_it.array_iterator); + } + + default: + { + return (m_it.generic_iterator < other.m_it.generic_iterator); + } + } + } + + /// comparison: less than or equal + inline bool operator<=(const iterator& other) const + { + return not other.operator < (*this); + } + + /// comparison: greater than + inline bool operator>(const iterator& other) const + { + return not operator<=(other); + } + + /// comparison: greater than or equal + inline bool operator>=(const iterator& other) const + { + return not operator<(other); + } + + /// add to iterator + inline iterator& operator+=(difference_type i) + { + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + throw std::domain_error("cannot use operator+= for object iterators"); + } + + case (basic_json::value_t::array): + { + m_it.array_iterator += i; + break; + } + + default: + { + m_it.generic_iterator += i; + break; + } + } + + return *this; + } + + /// subtract from iterator + inline iterator& operator-=(difference_type i) + { + return operator+=(-i); + } + + /// add to iterator + inline iterator operator+(difference_type i) + { + auto result = *this; + result += i; + return result; + } + + /// subtract from iterator + inline iterator operator-(difference_type i) + { + auto result = *this; + result -= i; + return result; + } + + /// return difference + inline difference_type operator-(const iterator& other) const + { + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + throw std::domain_error("cannot use operator- for object iterators"); + return 0; + } + + case (basic_json::value_t::array): + { + return m_it.array_iterator - other.m_it.array_iterator; + } + + default: + { + return m_it.generic_iterator - other.m_it.generic_iterator; + } + } + } + + /// access to successor + inline reference operator[](difference_type n) + { + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + throw std::domain_error("cannot use operator[] for object iterators"); + } + + case (basic_json::value_t::array): + { + return *(m_it.array_iterator + n); + } + + case (basic_json::value_t::null): + { + throw std::out_of_range("cannot get value"); + } + + default: + { + if (m_it.generic_iterator == -n) + { + return *m_object; + } + else + { + throw std::out_of_range("cannot get value"); + } + } + } + } + private: /// associated JSON instance pointer m_object = nullptr; @@ -2139,8 +2486,8 @@ class basic_json internal_iterator m_it; }; - /// a const bidirectional iterator for the basic_json class - class const_iterator : public std::iterator + /// a const random access iterator for the basic_json class + class const_iterator : public std::iterator { public: /// the type of the values when the iterator is dereferenced @@ -2174,7 +2521,7 @@ class basic_json } default: { - m_it.generic_iterator = generic_iterator_value::uninitialized; + m_it.generic_iterator = -1; break; } } @@ -2233,13 +2580,13 @@ class basic_json case (basic_json::value_t::null): { // set to end so begin()==end() is true: null is empty - m_it.generic_iterator = generic_iterator_value::end; + m_it.generic_iterator = 1; break; } default: { - m_it.generic_iterator = generic_iterator_value::begin; + m_it.generic_iterator = 0; break; } } @@ -2264,7 +2611,7 @@ class basic_json default: { - m_it.generic_iterator = generic_iterator_value::end; + m_it.generic_iterator = 1; break; } } @@ -2292,7 +2639,7 @@ class basic_json default: { - if (m_it.generic_iterator == generic_iterator_value::begin) + if (m_it.generic_iterator == 0) { return *m_object; } @@ -2321,7 +2668,7 @@ class basic_json default: { - if (m_it.generic_iterator == generic_iterator_value::begin) + if (m_it.generic_iterator == 0) { return m_object; } @@ -2336,7 +2683,7 @@ class basic_json /// post-increment (it++) inline const_iterator operator++(int) { - const_iterator result = *this; + auto result = *this; switch (m_object->m_type) { @@ -2354,14 +2701,7 @@ class basic_json default: { - if (m_it.generic_iterator == generic_iterator_value::begin) - { - m_it.generic_iterator = generic_iterator_value::end; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } + m_it.generic_iterator++; break; } } @@ -2388,14 +2728,7 @@ class basic_json default: { - if (m_it.generic_iterator == generic_iterator_value::begin) - { - m_it.generic_iterator = generic_iterator_value::end; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } + ++m_it.generic_iterator; break; } } @@ -2406,7 +2739,7 @@ class basic_json /// post-decrement (it--) inline const_iterator operator--(int) { - const_iterator result = *this; + auto result = *this; switch (m_object->m_type) { @@ -2422,22 +2755,9 @@ class basic_json break; } - case (basic_json::value_t::null): - { - m_it.generic_iterator = generic_iterator_value::invalid; - break; - } - default: { - if (m_it.generic_iterator == generic_iterator_value::end) - { - m_it.generic_iterator = generic_iterator_value::begin; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } + m_it.generic_iterator--; break; } } @@ -2462,22 +2782,9 @@ class basic_json break; } - case (basic_json::value_t::null): - { - m_it.generic_iterator = generic_iterator_value::invalid; - break; - } - default: { - if (m_it.generic_iterator == generic_iterator_value::end) - { - m_it.generic_iterator = generic_iterator_value::begin; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } + --m_it.generic_iterator; break; } } @@ -2488,9 +2795,10 @@ class basic_json /// comparison: equal inline bool operator==(const const_iterator& other) const { - if (m_object != other.m_object or m_object->m_type != other.m_object->m_type) + // if objects are not the same, the comparison is undefined + if (m_object != other.m_object) { - return false; + throw std::domain_error("cannot compare iterators of different containers"); } switch (m_object->m_type) @@ -2518,6 +2826,157 @@ class basic_json return not operator==(other); } + /// comparison: smaller + inline bool operator<(const const_iterator& other) const + { + // if objects are not the same, the comparison is undefined + if (m_object != other.m_object) + { + throw std::domain_error("cannot compare iterators of different containers"); + } + + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + throw std::domain_error("cannot use operator< for object iterators"); + } + + case (basic_json::value_t::array): + { + return (m_it.array_iterator < other.m_it.array_iterator); + } + + default: + { + return (m_it.generic_iterator < other.m_it.generic_iterator); + } + } + } + + /// comparison: less than or equal + inline bool operator<=(const const_iterator& other) const + { + return not other.operator < (*this); + } + + /// comparison: greater than + inline bool operator>(const const_iterator& other) const + { + return not operator<=(other); + } + + /// comparison: greater than or equal + inline bool operator>=(const const_iterator& other) const + { + return not operator<(other); + } + + /// add to iterator + inline const_iterator& operator+=(difference_type i) + { + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + throw std::domain_error("cannot use operator+= for object iterators"); + break; + } + + case (basic_json::value_t::array): + { + m_it.array_iterator += i; + break; + } + + default: + { + m_it.generic_iterator += i; + break; + } + } + + return *this; + } + + /// subtract from iterator + inline const_iterator& operator-=(difference_type i) + { + return operator+=(-i); + } + + /// add to iterator + inline const_iterator operator+(difference_type i) + { + auto result = *this; + result += i; + return result; + } + + /// subtract from iterator + inline const_iterator operator-(difference_type i) + { + auto result = *this; + result -= i; + return result; + } + + /// return difference + inline difference_type operator-(const const_iterator& other) const + { + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + throw std::domain_error("cannot use operator- for object iterators"); + } + + case (basic_json::value_t::array): + { + return m_it.array_iterator - other.m_it.array_iterator; + } + + default: + { + return m_it.generic_iterator - other.m_it.generic_iterator; + } + } + } + + /// access to successor + inline reference operator[](difference_type n) const + { + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + throw std::domain_error("cannot use operator[] for object iterators"); + } + + case (basic_json::value_t::array): + { + return *(m_it.array_iterator + n); + } + + case (basic_json::value_t::null): + { + throw std::out_of_range("cannot get value"); + } + + default: + { + if (m_it.generic_iterator == -n) + { + return *m_object; + } + else + { + throw std::out_of_range("cannot get value"); + } + } + } + } + private: /// associated JSON instance pointer m_object = nullptr; @@ -2577,7 +3036,8 @@ class basic_json /*! @brief create a string from a Unicode code point - @param codepoint the code point (must be in [0x0, 0x10ffff] + @param codepoint1 the code point (can be high surrogate) + @param codepoint2 the code point (can be low surrogate or 0) @return string representation of the code point @exception std::out_of_range if code point is >0x10ffff @exception std::invalid_argument if the low surrogate is invalid @@ -3581,6 +4041,9 @@ basic_json_parser_59: const lexer_char_t* m_limit = nullptr; }; + /*! + @brief syntax analysis + */ class parser { public: @@ -3816,7 +4279,10 @@ using json = basic_json<>; // specialization of std::swap, and std::hash namespace std { -/// swaps the values of two JSON objects +/*! +@brief exchanges the values of two JSON objects +@ingroup container +*/ template <> inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index cff9b0dc..add2e5c1 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -7,6 +7,26 @@ @see https://github.com/nlohmann/json */ +/*! +@defgroup container Container +@brief C++ Container concept + +A Container is an object used to store other objects and taking care of the +management of the memory used by the objects it contains. + +@see http://en.cppreference.com/w/cpp/concept/Container + +@defgroup reversiblecontainer Reversible Container +@brief C++ Reversible Container concept +@ingroup container + +A ReversibleContainer is a Container that has iterators that meet the +requirements of either BidirectionalIterator or RandomAccessIterator. Such +iterators allow a ReversibleContainer to be iterated over in reverse. + +@see http://en.cppreference.com/w/cpp/concept/ReversibleContainer +*/ + #ifndef _NLOHMANN_JSON #define _NLOHMANN_JSON @@ -27,6 +47,7 @@ #include /*! +@brief namespace for Niels Lohmann @see https://github.com/nlohmann */ namespace nlohmann @@ -75,30 +96,76 @@ class basic_json class iterator; class const_iterator; - /// the type of elements in a basic_json container + /*! + @brief the type of elements in a basic_json container + @ingroup container + */ using value_type = basic_json; - /// the type of an element reference + + /*! + @brief the type of an element reference + @ingroup container + */ using reference = basic_json&; - /// the type of an element const reference + + /*! + @brief the type of an element const reference + @ingroup container + */ using const_reference = const basic_json&; + + /*! + @brief a type to represent differences between iterators + @ingroup container + */ + using difference_type = std::ptrdiff_t; + + /*! + @brief a type to represent container sizes + @ingroup container + */ + using size_type = std::size_t; + + /// the allocator type + using allocator_type = Allocator; + /// the type of an element pointer using pointer = basic_json*; /// the type of an element const pointer using const_pointer = const basic_json*; - /// a type to represent differences between iterators - using difference_type = std::ptrdiff_t; - /// a type to represent container sizes - using size_type = std::size_t; - /// an iterator for a basic_json container + + /*! + @brief an iterator for a basic_json container + @ingroup container + */ using iterator = basic_json::iterator; - /// a const iterator for a basic_json container + + /*! + @brief a const iterator for a basic_json container + @ingroup container + */ using const_iterator = basic_json::const_iterator; - /// a reverse iterator for a basic_json container + + /*! + @brief a reverse iterator for a basic_json container + @ingroup reversiblecontainer + */ using reverse_iterator = std::reverse_iterator; - /// a const reverse iterator for a basic_json container + + /*! + @brief a const reverse iterator for a basic_json container + @ingroup reversiblecontainer + */ using const_reverse_iterator = std::reverse_iterator; + /// returns the allocator associated with the container + inline allocator_type get_allocator() const + { + return allocator_type(); + } + + /////////////////////////// // JSON value data types // /////////////////////////// @@ -166,6 +233,85 @@ class basic_json number_float ///< number value (floating-point) }; + /*! + @brief comparison operator for JSON value types + + Returns an ordering that is similar to Python: + - order: null < boolean < number < object < array < string + - furthermore, each type is not smaller than itself + */ + friend bool operator<(const value_t lhs, const value_t rhs) + { + // no type is smaller than itself + if (lhs == rhs) + { + return false; + } + + switch (lhs) + { + case (value_t::null): + { + // nulls are smaller than all other types + return true; + } + + case (value_t::boolean): + { + // only nulls are smaller than booleans + return (rhs != value_t::null); + } + + case (value_t::number_float): + case (value_t::number_integer): + { + switch (rhs) + { + // numbers are smaller than objects, arrays, and string + case (value_t::object): + case (value_t::array): + case (value_t::string): + { + return true; + } + default: + { + return false; + } + } + } + + case (value_t::object): + { + switch (rhs) + { + // objects are smaller than arrays and string + case (value_t::array): + case (value_t::string): + { + return true; + } + default: + { + return false; + } + } + } + + case (value_t::array): + { + // arrays are smaller than strings + return (rhs == value_t::string); + } + + default: + { + // a string is not smaller than any other types + return false; + } + } + } + ////////////////// // constructors // @@ -226,10 +372,11 @@ class basic_json } } - /// create a null object (implicitly) - inline basic_json() noexcept - : m_type(value_t::null) - {} + /*! + @brief create a null object (implicitly) + @ingroup container + */ + inline basic_json() noexcept = default; /// create a null object (explicitly) inline basic_json(std::nullptr_t) noexcept @@ -424,11 +571,15 @@ class basic_json return basic_json(l, false, value_t::object); } + /////////////////////////////////////// // other constructors and destructor // /////////////////////////////////////// - /// copy constructor + /*! + @brief copy constructor + @ingroup container + */ inline basic_json(const basic_json& other) : m_type(other.m_type) { @@ -487,7 +638,10 @@ class basic_json other.m_value = {}; } - /// copy assignment + /*! + @brief copy assignment + @ingroup container + */ inline reference& operator=(basic_json other) noexcept ( std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value and @@ -500,7 +654,10 @@ class basic_json return *this; } - /// destructor + /*! + @brief destructor + @ingroup container + */ inline ~basic_json() noexcept { switch (m_type) @@ -577,6 +734,42 @@ class basic_json return m_type; } + // return whether value is null + inline bool is_null() const noexcept + { + return m_type == value_t::null; + } + + // return whether value is boolean + inline bool is_boolean() const noexcept + { + return m_type == value_t::boolean; + } + + // return whether value is number + inline bool is_number() const noexcept + { + return (m_type == value_t::number_integer) or (m_type == value_t::number_float); + } + + // return whether value is object + inline bool is_object() const noexcept + { + return m_type == value_t::object; + } + + // return whether value is array + inline bool is_array() const noexcept + { + return m_type == value_t::array; + } + + // return whether value is string + inline bool is_string() const noexcept + { + return m_type == value_t::string; + } + /// return the type of the object (implicit) operator value_t() const noexcept { @@ -871,7 +1064,10 @@ class basic_json // iterators // /////////////// - /// returns an iterator to the beginning of the container + /*! + @brief returns an iterator to the first element + @ingroup container + */ inline iterator begin() noexcept { iterator result(this); @@ -879,7 +1075,10 @@ class basic_json return result; } - /// returns a const iterator to the beginning of the container + /*! + @brief returns a const iterator to the first element + @ingroup container + */ inline const_iterator begin() const noexcept { const_iterator result(this); @@ -887,7 +1086,10 @@ class basic_json return result; } - /// returns a const iterator to the beginning of the container + /*! + @brief returns a const iterator to the first element + @ingroup container + */ inline const_iterator cbegin() const noexcept { const_iterator result(this); @@ -895,7 +1097,10 @@ class basic_json return result; } - /// returns an iterator to the end of the container + /*! + @brief returns an iterator to one past the last element + @ingroup container + */ inline iterator end() noexcept { iterator result(this); @@ -903,7 +1108,10 @@ class basic_json return result; } - /// returns a const iterator to the end of the container + /*! + @brief returns a const iterator to one past the last element + @ingroup container + */ inline const_iterator end() const noexcept { const_iterator result(this); @@ -911,7 +1119,10 @@ class basic_json return result; } - /// returns a const iterator to the end of the container + /*! + @brief returns a const iterator to one past the last element + @ingroup container + */ inline const_iterator cend() const noexcept { const_iterator result(this); @@ -919,37 +1130,55 @@ class basic_json return result; } - /// returns a reverse iterator to the beginning + /*! + @brief returns a reverse iterator to the first element + @ingroup reversiblecontainer + */ inline reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } - /// returns a reverse iterator to the beginning + /*! + @brief returns a const reverse iterator to the first element + @ingroup reversiblecontainer + */ inline const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } - /// returns a reverse iterator to the end + /*! + @brief returns a reverse iterator to one past the last element + @ingroup reversiblecontainer + */ inline reverse_iterator rend() noexcept { return reverse_iterator(begin()); } - /// returns a reverse iterator to the end + /*! + @brief returns a const reverse iterator to one past the last element + @ingroup reversiblecontainer + */ inline const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } - /// returns a reverse iterator to the beginning + /*! + @brief returns a const reverse iterator to the first element + @ingroup reversiblecontainer + */ inline const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(cend()); } - /// returns a reverse iterator to the end + /*! + @brief returns a const reverse iterator to one past the last element + @ingroup reversiblecontainer + */ inline const_reverse_iterator crend() const noexcept { return const_reverse_iterator(cbegin()); @@ -960,7 +1189,10 @@ class basic_json // capacity // ////////////// - /// checks whether the container is empty + /*! + @brief checks whether the container is empty + @ingroup container + */ inline bool empty() const noexcept { switch (m_type) @@ -988,7 +1220,10 @@ class basic_json } } - /// returns the number of elements + /*! + @brief returns the number of elements + @ingroup container + */ inline size_type size() const noexcept { switch (m_type) @@ -1016,7 +1251,10 @@ class basic_json } } - /// returns the maximum possible number of elements + /*! + @brief returns the maximum possible number of elements + @ingroup container + */ inline size_type max_size() const noexcept { switch (m_type) @@ -1186,7 +1424,10 @@ class basic_json return operator[](value.first); } - /// swaps the contents + /*! + @brief exchanges the values + @ingroup container + */ inline void swap(reference other) noexcept ( std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value and @@ -1242,7 +1483,10 @@ class basic_json // lexicographical comparison operators // ////////////////////////////////////////// - /// comparison: equal + /*! + @brief comparison: equal + @ingroup container + */ friend bool operator==(const_reference lhs, const_reference rhs) { switch (lhs.type()) @@ -1316,7 +1560,10 @@ class basic_json return false; } - /// comparison: not equal + /*! + @brief comparison: not equal + @ingroup container + */ friend bool operator!=(const_reference lhs, const_reference rhs) { return not (lhs == rhs); @@ -1393,7 +1640,9 @@ class basic_json } } - return false; + // We only reach this line if we cannot compare values. In that case, + // we compare types. + return lhs.type() < rhs.type(); } /// comparison: less than or equal @@ -1743,19 +1992,6 @@ class basic_json // iterators // /////////////// - /// values of a generic iterator type of non-container JSON values - enum class generic_iterator_value - { - /// the iterator was not initialized - uninitialized, - /// the iterator points to the only value - begin, - /// the iterator points past the only value - end, - /// the iterator points to an invalid value - invalid - }; - /// an iterator value template union internal_iterator @@ -1765,21 +2001,15 @@ class basic_json /// iterator for JSON arrays array_iterator_t array_iterator; /// generic iteraotr for all other value types - generic_iterator_value generic_iterator; + difference_type generic_iterator; /// default constructor - internal_iterator() : generic_iterator(generic_iterator_value::uninitialized) {} - /// constructor for object iterators - internal_iterator(object_iterator_t v) : object_iterator(v) {} - /// constructor for array iterators - internal_iterator(array_iterator_t v) : array_iterator(v) {} - /// constructor for generic iterators - internal_iterator(generic_iterator_value v) : generic_iterator(v) {} + internal_iterator() : generic_iterator(-1) {} }; public: - /// a bidirectional iterator for the basic_json class - class iterator : public std::iterator + /// a random access iterator for the basic_json class + class iterator : public std::iterator { public: /// the type of the values when the iterator is dereferenced @@ -1813,7 +2043,7 @@ class basic_json } default: { - m_it.generic_iterator = generic_iterator_value::uninitialized; + m_it.generic_iterator = -1; break; } } @@ -1847,13 +2077,13 @@ class basic_json case (basic_json::value_t::null): { // set to end so begin()==end() is true: null is empty - m_it.generic_iterator = generic_iterator_value::end; + m_it.generic_iterator = 1; break; } default: { - m_it.generic_iterator = generic_iterator_value::begin; + m_it.generic_iterator = 0; break; } } @@ -1878,14 +2108,14 @@ class basic_json default: { - m_it.generic_iterator = generic_iterator_value::end; + m_it.generic_iterator = 1; break; } } } /// return a reference to the value pointed to by the iterator - inline reference operator*() const + inline reference operator*() { switch (m_object->m_type) { @@ -1906,7 +2136,7 @@ class basic_json default: { - if (m_it.generic_iterator == generic_iterator_value::begin) + if (m_it.generic_iterator == 0) { return *m_object; } @@ -1919,7 +2149,7 @@ class basic_json } /// dereference the iterator - inline pointer operator->() const + inline pointer operator->() { switch (m_object->m_type) { @@ -1933,9 +2163,14 @@ class basic_json return &*m_it.array_iterator; } + case (basic_json::value_t::null): + { + throw std::out_of_range("cannot get value"); + } + default: { - if (m_it.generic_iterator == generic_iterator_value::begin) + if (m_it.generic_iterator == 0) { return m_object; } @@ -1950,7 +2185,7 @@ class basic_json /// post-increment (it++) inline iterator operator++(int) { - iterator result = *this; + auto result = *this; switch (m_object->m_type) { @@ -1968,14 +2203,7 @@ class basic_json default: { - if (m_it.generic_iterator == generic_iterator_value::begin) - { - m_it.generic_iterator = generic_iterator_value::end; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } + m_it.generic_iterator++; break; } } @@ -2002,14 +2230,7 @@ class basic_json default: { - if (m_it.generic_iterator == generic_iterator_value::begin) - { - m_it.generic_iterator = generic_iterator_value::end; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } + ++m_it.generic_iterator; break; } } @@ -2020,7 +2241,7 @@ class basic_json /// post-decrement (it--) inline iterator operator--(int) { - iterator result = *this; + auto result = *this; switch (m_object->m_type) { @@ -2036,22 +2257,9 @@ class basic_json break; } - case (basic_json::value_t::null): - { - m_it.generic_iterator = generic_iterator_value::invalid; - break; - } - default: { - if (m_it.generic_iterator == generic_iterator_value::end) - { - m_it.generic_iterator = generic_iterator_value::begin; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } + m_it.generic_iterator--; break; } } @@ -2076,22 +2284,9 @@ class basic_json break; } - case (basic_json::value_t::null): - { - m_it.generic_iterator = generic_iterator_value::invalid; - break; - } - default: { - if (m_it.generic_iterator == generic_iterator_value::end) - { - m_it.generic_iterator = generic_iterator_value::begin; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } + --m_it.generic_iterator; break; } } @@ -2102,9 +2297,10 @@ class basic_json /// comparison: equal inline bool operator==(const iterator& other) const { - if (m_object != other.m_object or m_object->m_type != other.m_object->m_type) + // if objects are not the same, the comparison is undefined + if (m_object != other.m_object) { - return false; + throw std::domain_error("cannot compare iterators of different containers"); } switch (m_object->m_type) @@ -2132,6 +2328,157 @@ class basic_json return not operator==(other); } + /// comparison: smaller + inline bool operator<(const iterator& other) const + { + // if objects are not the same, the comparison is undefined + if (m_object != other.m_object) + { + throw std::domain_error("cannot compare iterators of different containers"); + } + + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + throw std::domain_error("cannot use operator< for object iterators"); + } + + case (basic_json::value_t::array): + { + return (m_it.array_iterator < other.m_it.array_iterator); + } + + default: + { + return (m_it.generic_iterator < other.m_it.generic_iterator); + } + } + } + + /// comparison: less than or equal + inline bool operator<=(const iterator& other) const + { + return not other.operator < (*this); + } + + /// comparison: greater than + inline bool operator>(const iterator& other) const + { + return not operator<=(other); + } + + /// comparison: greater than or equal + inline bool operator>=(const iterator& other) const + { + return not operator<(other); + } + + /// add to iterator + inline iterator& operator+=(difference_type i) + { + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + throw std::domain_error("cannot use operator+= for object iterators"); + } + + case (basic_json::value_t::array): + { + m_it.array_iterator += i; + break; + } + + default: + { + m_it.generic_iterator += i; + break; + } + } + + return *this; + } + + /// subtract from iterator + inline iterator& operator-=(difference_type i) + { + return operator+=(-i); + } + + /// add to iterator + inline iterator operator+(difference_type i) + { + auto result = *this; + result += i; + return result; + } + + /// subtract from iterator + inline iterator operator-(difference_type i) + { + auto result = *this; + result -= i; + return result; + } + + /// return difference + inline difference_type operator-(const iterator& other) const + { + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + throw std::domain_error("cannot use operator- for object iterators"); + return 0; + } + + case (basic_json::value_t::array): + { + return m_it.array_iterator - other.m_it.array_iterator; + } + + default: + { + return m_it.generic_iterator - other.m_it.generic_iterator; + } + } + } + + /// access to successor + inline reference operator[](difference_type n) + { + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + throw std::domain_error("cannot use operator[] for object iterators"); + } + + case (basic_json::value_t::array): + { + return *(m_it.array_iterator + n); + } + + case (basic_json::value_t::null): + { + throw std::out_of_range("cannot get value"); + } + + default: + { + if (m_it.generic_iterator == -n) + { + return *m_object; + } + else + { + throw std::out_of_range("cannot get value"); + } + } + } + } + private: /// associated JSON instance pointer m_object = nullptr; @@ -2139,8 +2486,8 @@ class basic_json internal_iterator m_it; }; - /// a const bidirectional iterator for the basic_json class - class const_iterator : public std::iterator + /// a const random access iterator for the basic_json class + class const_iterator : public std::iterator { public: /// the type of the values when the iterator is dereferenced @@ -2174,7 +2521,7 @@ class basic_json } default: { - m_it.generic_iterator = generic_iterator_value::uninitialized; + m_it.generic_iterator = -1; break; } } @@ -2233,13 +2580,13 @@ class basic_json case (basic_json::value_t::null): { // set to end so begin()==end() is true: null is empty - m_it.generic_iterator = generic_iterator_value::end; + m_it.generic_iterator = 1; break; } default: { - m_it.generic_iterator = generic_iterator_value::begin; + m_it.generic_iterator = 0; break; } } @@ -2264,7 +2611,7 @@ class basic_json default: { - m_it.generic_iterator = generic_iterator_value::end; + m_it.generic_iterator = 1; break; } } @@ -2292,7 +2639,7 @@ class basic_json default: { - if (m_it.generic_iterator == generic_iterator_value::begin) + if (m_it.generic_iterator == 0) { return *m_object; } @@ -2321,7 +2668,7 @@ class basic_json default: { - if (m_it.generic_iterator == generic_iterator_value::begin) + if (m_it.generic_iterator == 0) { return m_object; } @@ -2336,7 +2683,7 @@ class basic_json /// post-increment (it++) inline const_iterator operator++(int) { - const_iterator result = *this; + auto result = *this; switch (m_object->m_type) { @@ -2354,14 +2701,7 @@ class basic_json default: { - if (m_it.generic_iterator == generic_iterator_value::begin) - { - m_it.generic_iterator = generic_iterator_value::end; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } + m_it.generic_iterator++; break; } } @@ -2388,14 +2728,7 @@ class basic_json default: { - if (m_it.generic_iterator == generic_iterator_value::begin) - { - m_it.generic_iterator = generic_iterator_value::end; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } + ++m_it.generic_iterator; break; } } @@ -2406,7 +2739,7 @@ class basic_json /// post-decrement (it--) inline const_iterator operator--(int) { - const_iterator result = *this; + auto result = *this; switch (m_object->m_type) { @@ -2422,22 +2755,9 @@ class basic_json break; } - case (basic_json::value_t::null): - { - m_it.generic_iterator = generic_iterator_value::invalid; - break; - } - default: { - if (m_it.generic_iterator == generic_iterator_value::end) - { - m_it.generic_iterator = generic_iterator_value::begin; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } + m_it.generic_iterator--; break; } } @@ -2462,22 +2782,9 @@ class basic_json break; } - case (basic_json::value_t::null): - { - m_it.generic_iterator = generic_iterator_value::invalid; - break; - } - default: { - if (m_it.generic_iterator == generic_iterator_value::end) - { - m_it.generic_iterator = generic_iterator_value::begin; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } + --m_it.generic_iterator; break; } } @@ -2488,9 +2795,10 @@ class basic_json /// comparison: equal inline bool operator==(const const_iterator& other) const { - if (m_object != other.m_object or m_object->m_type != other.m_object->m_type) + // if objects are not the same, the comparison is undefined + if (m_object != other.m_object) { - return false; + throw std::domain_error("cannot compare iterators of different containers"); } switch (m_object->m_type) @@ -2518,6 +2826,157 @@ class basic_json return not operator==(other); } + /// comparison: smaller + inline bool operator<(const const_iterator& other) const + { + // if objects are not the same, the comparison is undefined + if (m_object != other.m_object) + { + throw std::domain_error("cannot compare iterators of different containers"); + } + + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + throw std::domain_error("cannot use operator< for object iterators"); + } + + case (basic_json::value_t::array): + { + return (m_it.array_iterator < other.m_it.array_iterator); + } + + default: + { + return (m_it.generic_iterator < other.m_it.generic_iterator); + } + } + } + + /// comparison: less than or equal + inline bool operator<=(const const_iterator& other) const + { + return not other.operator < (*this); + } + + /// comparison: greater than + inline bool operator>(const const_iterator& other) const + { + return not operator<=(other); + } + + /// comparison: greater than or equal + inline bool operator>=(const const_iterator& other) const + { + return not operator<(other); + } + + /// add to iterator + inline const_iterator& operator+=(difference_type i) + { + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + throw std::domain_error("cannot use operator+= for object iterators"); + break; + } + + case (basic_json::value_t::array): + { + m_it.array_iterator += i; + break; + } + + default: + { + m_it.generic_iterator += i; + break; + } + } + + return *this; + } + + /// subtract from iterator + inline const_iterator& operator-=(difference_type i) + { + return operator+=(-i); + } + + /// add to iterator + inline const_iterator operator+(difference_type i) + { + auto result = *this; + result += i; + return result; + } + + /// subtract from iterator + inline const_iterator operator-(difference_type i) + { + auto result = *this; + result -= i; + return result; + } + + /// return difference + inline difference_type operator-(const const_iterator& other) const + { + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + throw std::domain_error("cannot use operator- for object iterators"); + } + + case (basic_json::value_t::array): + { + return m_it.array_iterator - other.m_it.array_iterator; + } + + default: + { + return m_it.generic_iterator - other.m_it.generic_iterator; + } + } + } + + /// access to successor + inline reference operator[](difference_type n) const + { + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + throw std::domain_error("cannot use operator[] for object iterators"); + } + + case (basic_json::value_t::array): + { + return *(m_it.array_iterator + n); + } + + case (basic_json::value_t::null): + { + throw std::out_of_range("cannot get value"); + } + + default: + { + if (m_it.generic_iterator == -n) + { + return *m_object; + } + else + { + throw std::out_of_range("cannot get value"); + } + } + } + } + private: /// associated JSON instance pointer m_object = nullptr; @@ -2577,7 +3036,8 @@ class basic_json /*! @brief create a string from a Unicode code point - @param codepoint the code point (must be in [0x0, 0x10ffff] + @param codepoint1 the code point (can be high surrogate) + @param codepoint2 the code point (can be low surrogate or 0) @return string representation of the code point @exception std::out_of_range if code point is >0x10ffff @exception std::invalid_argument if the low surrogate is invalid @@ -2930,6 +3390,9 @@ class basic_json const lexer_char_t* m_limit = nullptr; }; + /*! + @brief syntax analysis + */ class parser { public: @@ -3165,7 +3628,10 @@ using json = basic_json<>; // specialization of std::swap, and std::hash namespace std { -/// swaps the values of two JSON objects +/*! +@brief exchanges the values of two JSON objects +@ingroup container +*/ template <> inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( diff --git a/test/unit.cpp b/test/unit.cpp index 19d0f7d5..6d0904bf 100644 --- a/test/unit.cpp +++ b/test/unit.cpp @@ -1050,6 +1050,86 @@ TEST_CASE("other constructors and destructor") TEST_CASE("object inspection") { + SECTION("convenience type checker") + { + SECTION("object") + { + json j {{"foo", 1}, {"bar", false}}; + CHECK(not j.is_null()); + CHECK(not j.is_boolean()); + CHECK(not j.is_number()); + CHECK(j.is_object()); + CHECK(not j.is_array()); + CHECK(not j.is_string()); + } + + SECTION("array") + { + json j {"foo", 1, 42.23, false}; + CHECK(not j.is_null()); + CHECK(not j.is_boolean()); + CHECK(not j.is_number()); + CHECK(not j.is_object()); + CHECK(j.is_array()); + CHECK(not j.is_string()); + } + + SECTION("null") + { + json j(nullptr); + CHECK(j.is_null()); + CHECK(not j.is_boolean()); + CHECK(not j.is_number()); + CHECK(not j.is_object()); + CHECK(not j.is_array()); + CHECK(not j.is_string()); + } + + SECTION("boolean") + { + json j(true); + CHECK(not j.is_null()); + CHECK(j.is_boolean()); + CHECK(not j.is_number()); + CHECK(not j.is_object()); + CHECK(not j.is_array()); + CHECK(not j.is_string()); + } + + SECTION("string") + { + json j("Hello world"); + CHECK(not j.is_null()); + CHECK(not j.is_boolean()); + CHECK(not j.is_number()); + CHECK(not j.is_object()); + CHECK(not j.is_array()); + CHECK(j.is_string()); + } + + SECTION("number (integer)") + { + json j(42); + CHECK(not j.is_null()); + CHECK(not j.is_boolean()); + CHECK(j.is_number()); + CHECK(not j.is_object()); + CHECK(not j.is_array()); + CHECK(not j.is_string()); + } + + SECTION("number (floating-point)") + { + json j(42.23); + CHECK(not j.is_null()); + CHECK(not j.is_boolean()); + CHECK(j.is_number()); + CHECK(not j.is_object()); + CHECK(not j.is_array()); + CHECK(not j.is_string()); + } + } + SECTION("serialization") { json j {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} }; @@ -2366,1135 +2446,1501 @@ TEST_CASE("element access") TEST_CASE("iterators") { - SECTION("uninitialized") + SECTION("basic behavior") { - json::iterator it; - CHECK(it.m_object == nullptr); - - json::const_iterator cit; - CHECK(cit.m_object == nullptr); - } - - SECTION("boolean") - { - json j = true; - json j_const(j); - - SECTION("json + begin/end") + SECTION("uninitialized") { - json::iterator it = j.begin(); - CHECK(it != j.end()); - CHECK(*it == j); + json::iterator it; + CHECK(it.m_object == nullptr); - it++; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - it--; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - - ++it; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - --it; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); + json::const_iterator cit; + CHECK(cit.m_object == nullptr); } - SECTION("const json + begin/end") + SECTION("boolean") { - json::const_iterator it = j_const.begin(); - CHECK(it != j_const.end()); - CHECK(*it == j_const); + json j = true; + json j_const(j); - it++; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); + SECTION("json + begin/end") + { + json::iterator it = j.begin(); + CHECK(it != j.end()); + CHECK(*it == j); - it--; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); + it++; + CHECK(it != j.begin()); + CHECK(it == j.end()); - ++it; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); + it--; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); - --it; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); + ++it; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + --it; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + } + + SECTION("const json + begin/end") + { + json::const_iterator it = j_const.begin(); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + it--; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + --it; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it = j.cbegin(); + CHECK(it != j.cend()); + CHECK(*it == j); + + it++; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + it--; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + --it; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it = j_const.cbegin(); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + it--; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + --it; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it = j.rbegin(); + CHECK(it != j.rend()); + CHECK(*it == j); + + it++; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + it--; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + --it; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it = j.crbegin(); + CHECK(it != j.crend()); + CHECK(*it == j); + + it++; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + it--; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + --it; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it = j_const.crbegin(); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + it--; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + --it; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + } } - SECTION("json + cbegin/cend") + SECTION("string") { - json::const_iterator it = j.cbegin(); - CHECK(it != j.cend()); - CHECK(*it == j); + json j = "hello world"; + json j_const(j); - it++; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); + SECTION("json + begin/end") + { + json::iterator it = j.begin(); + CHECK(it != j.end()); + CHECK(*it == j); - it--; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); + it++; + CHECK(it != j.begin()); + CHECK(it == j.end()); - ++it; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); + it--; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); - --it; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); + ++it; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + --it; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + } + + SECTION("const json + begin/end") + { + json::const_iterator it = j_const.begin(); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + it--; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + --it; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it = j.cbegin(); + CHECK(it != j.cend()); + CHECK(*it == j); + + it++; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + it--; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + --it; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it = j_const.cbegin(); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + it--; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + --it; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it = j.rbegin(); + CHECK(it != j.rend()); + CHECK(*it == j); + + it++; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + it--; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + --it; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it = j.crbegin(); + CHECK(it != j.crend()); + CHECK(*it == j); + + it++; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + it--; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + --it; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it = j_const.crbegin(); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + it--; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + --it; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + } } - SECTION("const json + cbegin/cend") + SECTION("array") { - json::const_iterator it = j_const.cbegin(); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); + json j = {1, 2, 3}; + json j_const(j); - it++; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); + SECTION("json + begin/end") + { + json::iterator it_begin = j.begin(); + json::iterator it_end = j.end(); - it--; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j[0]); - ++it; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[1]); - --it; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[2]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("const json + begin/end") + { + json::const_iterator it_begin = j_const.begin(); + json::const_iterator it_end = j_const.end(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j_const[0]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j_const[1]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j_const[2]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it_begin = j.cbegin(); + json::const_iterator it_end = j.cend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j[0]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[1]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[2]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it_begin = j_const.cbegin(); + json::const_iterator it_end = j_const.cend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j[0]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[1]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[2]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it_begin = j.rbegin(); + json::reverse_iterator it_end = j.rend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j[2]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[1]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[0]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it_begin = j.crbegin(); + json::const_reverse_iterator it_end = j.crend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j[2]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[1]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[0]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it_begin = j_const.crbegin(); + json::const_reverse_iterator it_end = j_const.crend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j[2]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[1]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[0]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } } - SECTION("json + rbegin/rend") + SECTION("object") { - json::reverse_iterator it = j.rbegin(); - CHECK(it != j.rend()); - CHECK(*it == j); + json j = {{"A", 1}, {"B", 2}, {"C", 3}}; + json j_const(j); - it++; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); + SECTION("json + begin/end") + { + json::iterator it_begin = j.begin(); + json::iterator it_end = j.end(); - it--; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j["A"]); - ++it; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["B"]); - --it; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["C"]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("const json + begin/end") + { + json::const_iterator it_begin = j_const.begin(); + json::const_iterator it_end = j_const.end(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j_const["A"]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j_const["B"]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j_const["C"]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it_begin = j.cbegin(); + json::const_iterator it_end = j.cend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j["A"]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["B"]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["C"]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it_begin = j_const.cbegin(); + json::const_iterator it_end = j_const.cend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j_const["A"]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j_const["B"]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j_const["C"]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it_begin = j.rbegin(); + json::reverse_iterator it_end = j.rend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j["C"]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["B"]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["A"]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it_begin = j.crbegin(); + json::const_reverse_iterator it_end = j.crend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j["C"]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["B"]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["A"]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it_begin = j_const.crbegin(); + json::const_reverse_iterator it_end = j_const.crend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j["C"]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["B"]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["A"]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } } - SECTION("json + crbegin/crend") + SECTION("number (integer)") { - json::const_reverse_iterator it = j.crbegin(); - CHECK(it != j.crend()); - CHECK(*it == j); + json j = 23; + json j_const(j); - it++; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); + SECTION("json + begin/end") + { + json::iterator it = j.begin(); + CHECK(it != j.end()); + CHECK(*it == j); - it--; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); + it++; + CHECK(it != j.begin()); + CHECK(it == j.end()); - ++it; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); + it--; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); - --it; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); + ++it; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + --it; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + } + + SECTION("const json + begin/end") + { + json::const_iterator it = j_const.begin(); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + it--; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + --it; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it = j.cbegin(); + CHECK(it != j.cend()); + CHECK(*it == j); + + it++; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + it--; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + --it; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it = j_const.cbegin(); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + it--; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + --it; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it = j.rbegin(); + CHECK(it != j.rend()); + CHECK(*it == j); + + it++; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + it--; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + --it; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it = j.crbegin(); + CHECK(it != j.crend()); + CHECK(*it == j); + + it++; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + it--; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + --it; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it = j_const.crbegin(); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + it--; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + --it; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + } } - SECTION("const json + crbegin/crend") + SECTION("number (float)") { - json::const_reverse_iterator it = j_const.crbegin(); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); + json j = 23.42; + json j_const(j); - it++; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); + SECTION("json + begin/end") + { + json::iterator it = j.begin(); + CHECK(it != j.end()); + CHECK(*it == j); - it--; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); + it++; + CHECK(it != j.begin()); + CHECK(it == j.end()); - ++it; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); + it--; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); - --it; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); + ++it; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + --it; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + } + + SECTION("const json + begin/end") + { + json::const_iterator it = j_const.begin(); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + it--; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + --it; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it = j.cbegin(); + CHECK(it != j.cend()); + CHECK(*it == j); + + it++; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + it--; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + --it; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it = j_const.cbegin(); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + it--; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + --it; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it = j.rbegin(); + CHECK(it != j.rend()); + CHECK(*it == j); + + it++; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + it--; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + --it; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it = j.crbegin(); + CHECK(it != j.crend()); + CHECK(*it == j); + + it++; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + it--; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + --it; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it = j_const.crbegin(); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + it--; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + --it; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + } + } + + SECTION("null") + { + json j = nullptr; + json j_const(j); + + SECTION("json + begin/end") + { + json::iterator it = j.begin(); + CHECK(it == j.end()); + } + + SECTION("const json + begin/end") + { + json::const_iterator it_begin = j_const.begin(); + json::const_iterator it_end = j_const.end(); + CHECK(it_begin == it_end); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it_begin = j.cbegin(); + json::const_iterator it_end = j.cend(); + CHECK(it_begin == it_end); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it_begin = j_const.cbegin(); + json::const_iterator it_end = j_const.cend(); + CHECK(it_begin == it_end); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it = j.rbegin(); + CHECK(it == j.rend()); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it = j.crbegin(); + CHECK(it == j.crend()); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it = j_const.crbegin(); + CHECK(it == j_const.crend()); + } } } - SECTION("string") + SECTION("iterator comparisons") { - json j = "hello world"; - json j_const(j); + json j_values = {nullptr, true, 42, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"}; - SECTION("json + begin/end") + for (json& j : j_values) { - json::iterator it = j.begin(); - CHECK(it != j.end()); - CHECK(*it == j); + auto it1 = j.begin(); + auto it2 = j.begin(); + auto it3 = j.begin(); + ++it2; + ++it3; + ++it3; + auto it1_c = j.cbegin(); + auto it2_c = j.cbegin(); + auto it3_c = j.cbegin(); + ++it2_c; + ++it3_c; + ++it3_c; - it++; - CHECK(it != j.begin()); - CHECK(it == j.end()); + // comparison: equal + { + CHECK(it1 == it1); + CHECK(not (it1 == it2)); + CHECK(not (it1 == it3)); + CHECK(not (it2 == it3)); + CHECK(it1_c == it1_c); + CHECK(not (it1_c == it2_c)); + CHECK(not (it1_c == it3_c)); + CHECK(not (it2_c == it3_c)); + } - it--; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); + // comparison: not equal + { + // check definition + CHECK( (it1 != it1) == not(it1 == it1) ); + CHECK( (it1 != it2) == not(it1 == it2) ); + CHECK( (it1 != it3) == not(it1 == it3) ); + CHECK( (it2 != it3) == not(it2 == it3) ); + CHECK( (it1_c != it1_c) == not(it1_c == it1_c) ); + CHECK( (it1_c != it2_c) == not(it1_c == it2_c) ); + CHECK( (it1_c != it3_c) == not(it1_c == it3_c) ); + CHECK( (it2_c != it3_c) == not(it2_c == it3_c) ); + } - ++it; - CHECK(it != j.begin()); - CHECK(it == j.end()); + // comparison: smaller + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 < it1, std::domain_error); + CHECK_THROWS_AS(it1 < it2, std::domain_error); + CHECK_THROWS_AS(it2 < it3, std::domain_error); + CHECK_THROWS_AS(it1 < it3, std::domain_error); + CHECK_THROWS_AS(it1_c < it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c < it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c < it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c < it3_c, std::domain_error); + } + else + { + CHECK(not (it1 < it1)); + CHECK(it1 < it2); + CHECK(it1 < it3); + CHECK(it2 < it3); + CHECK(not (it1_c < it1_c)); + CHECK(it1_c < it2_c); + CHECK(it1_c < it3_c); + CHECK(it2_c < it3_c); + } + } - --it; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); + // comparison: less than or equal + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 <= it1, std::domain_error); + CHECK_THROWS_AS(it1 <= it2, std::domain_error); + CHECK_THROWS_AS(it2 <= it3, std::domain_error); + CHECK_THROWS_AS(it1 <= it3, std::domain_error); + CHECK_THROWS_AS(it1_c <= it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c <= it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c <= it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c <= it3_c, std::domain_error); + } + else + { + // check definition + CHECK( (it1 <= it1) == not(it1 < it1) ); + CHECK( (it1 <= it2) == not(it2 < it1) ); + CHECK( (it1 <= it3) == not(it3 < it1) ); + CHECK( (it2 <= it3) == not(it3 < it2) ); + CHECK( (it1_c <= it1_c) == not(it1_c < it1_c) ); + CHECK( (it1_c <= it2_c) == not(it2_c < it1_c) ); + CHECK( (it1_c <= it3_c) == not(it3_c < it1_c) ); + CHECK( (it2_c <= it3_c) == not(it3_c < it2_c) ); + } + } + + // comparison: greater than + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 > it1, std::domain_error); + CHECK_THROWS_AS(it1 > it2, std::domain_error); + CHECK_THROWS_AS(it2 > it3, std::domain_error); + CHECK_THROWS_AS(it1 > it3, std::domain_error); + CHECK_THROWS_AS(it1_c > it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c > it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c > it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c > it3_c, std::domain_error); + } + else + { + // check definition + CHECK( (it1 > it1) == (it1 < it1) ); + CHECK( (it1 > it2) == (it2 < it1) ); + CHECK( (it1 > it3) == (it3 < it1) ); + CHECK( (it2 > it3) == (it3 < it2) ); + CHECK( (it1_c > it1_c) == (it1_c < it1_c) ); + CHECK( (it1_c > it2_c) == (it2_c < it1_c) ); + CHECK( (it1_c > it3_c) == (it3_c < it1_c) ); + CHECK( (it2_c > it3_c) == (it3_c < it2_c) ); + } + } + + // comparison: greater than or equal + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 >= it1, std::domain_error); + CHECK_THROWS_AS(it1 >= it2, std::domain_error); + CHECK_THROWS_AS(it2 >= it3, std::domain_error); + CHECK_THROWS_AS(it1 >= it3, std::domain_error); + CHECK_THROWS_AS(it1_c >= it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c >= it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c >= it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c >= it3_c, std::domain_error); + } + else + { + // check definition + CHECK( (it1 >= it1) == not(it1 < it1) ); + CHECK( (it1 >= it2) == not(it1 < it2) ); + CHECK( (it1 >= it3) == not(it1 < it3) ); + CHECK( (it2 >= it3) == not(it2 < it3) ); + CHECK( (it1_c >= it1_c) == not(it1_c < it1_c) ); + CHECK( (it1_c >= it2_c) == not(it1_c < it2_c) ); + CHECK( (it1_c >= it3_c) == not(it1_c < it3_c) ); + CHECK( (it2_c >= it3_c) == not(it2_c < it3_c) ); + } + } } - SECTION("const json + begin/end") + // check exceptions if different objects are compared + for (auto j : j_values) { - json::const_iterator it = j_const.begin(); - CHECK(it != j_const.end()); - CHECK(*it == j_const); + for (auto k : j_values) + { + if (j != k) + { + CHECK_THROWS_AS(j.begin() == k.begin(), std::domain_error); + CHECK_THROWS_AS(j.cbegin() == k.cbegin(), std::domain_error); - it++; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - it--; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - --it; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - } - - SECTION("json + cbegin/cend") - { - json::const_iterator it = j.cbegin(); - CHECK(it != j.cend()); - CHECK(*it == j); - - it++; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - it--; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - --it; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - } - - SECTION("const json + cbegin/cend") - { - json::const_iterator it = j_const.cbegin(); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - it--; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - --it; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - } - - SECTION("json + rbegin/rend") - { - json::reverse_iterator it = j.rbegin(); - CHECK(it != j.rend()); - CHECK(*it == j); - - it++; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - it--; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - --it; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - } - - SECTION("json + crbegin/crend") - { - json::const_reverse_iterator it = j.crbegin(); - CHECK(it != j.crend()); - CHECK(*it == j); - - it++; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - it--; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - --it; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - } - - SECTION("const json + crbegin/crend") - { - json::const_reverse_iterator it = j_const.crbegin(); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - it--; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - --it; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); + CHECK_THROWS_AS(j.begin() < k.begin(), std::domain_error); + CHECK_THROWS_AS(j.cbegin() < k.cbegin(), std::domain_error); + } + } } } - SECTION("array") + SECTION("iterator arithmetic") { - json j = {1, 2, 3}; - json j_const(j); + json j_object = {{"one", 1}, {"two", 2}, {"three", 3}}; + json j_array = {1, 2, 3, 4, 5, 6}; + json j_null = nullptr; + json j_value = 42; - SECTION("json + begin/end") + SECTION("addition and subtraction") { - json::iterator it_begin = j.begin(); - json::iterator it_end = j.end(); + SECTION("object") + { + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it += 1, std::domain_error); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it += 1, std::domain_error); + } + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it + 1, std::domain_error); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it + 1, std::domain_error); + } + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it -= 1, std::domain_error); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it -= 1, std::domain_error); + } + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it - 1, std::domain_error); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it - 1, std::domain_error); + } + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it - it, std::domain_error); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it - it, std::domain_error); + } + } - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j[0]); + SECTION("array") + { + { + auto it = j_array.begin(); + it += 3; + CHECK((j_array.begin() + 3) == it); + CHECK((it - 3) == j_array.begin()); + CHECK((it - j_array.begin()) == 3); + CHECK(*it == json(4)); + it -= 2; + CHECK(*it == json(2)); + } + { + auto it = j_array.cbegin(); + it += 3; + CHECK((j_array.cbegin() + 3) == it); + CHECK((it - 3) == j_array.cbegin()); + CHECK((it - j_array.cbegin()) == 3); + CHECK(*it == json(4)); + it -= 2; + CHECK(*it == json(2)); + } + } - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[1]); + SECTION("null") + { + { + auto it = j_null.begin(); + it += 3; + CHECK((j_null.begin() + 3) == it); + CHECK((it - 3) == j_null.begin()); + CHECK((it - j_null.begin()) == 3); + CHECK(it != j_null.end()); + it -= 3; + CHECK(it == j_null.end()); + } + { + auto it = j_null.cbegin(); + it += 3; + CHECK((j_null.cbegin() + 3) == it); + CHECK((it - 3) == j_null.cbegin()); + CHECK((it - j_null.cbegin()) == 3); + CHECK(it != j_null.cend()); + it -= 3; + CHECK(it == j_null.cend()); + } + } - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[2]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); + SECTION("value") + { + { + auto it = j_value.begin(); + it += 3; + CHECK((j_value.begin() + 3) == it); + CHECK((it - 3) == j_value.begin()); + CHECK((it - j_value.begin()) == 3); + CHECK(it != j_value.end()); + it -= 3; + CHECK(*it == json(42)); + } + { + auto it = j_value.cbegin(); + it += 3; + CHECK((j_value.cbegin() + 3) == it); + CHECK((it - 3) == j_value.cbegin()); + CHECK((it - j_value.cbegin()) == 3); + CHECK(it != j_value.cend()); + it -= 3; + CHECK(*it == json(42)); + } + } } - SECTION("const json + begin/end") + SECTION("subscript operator") { - json::const_iterator it_begin = j_const.begin(); - json::const_iterator it_end = j_const.end(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j_const[0]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j_const[1]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j_const[2]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("json + cbegin/cend") - { - json::const_iterator it_begin = j.cbegin(); - json::const_iterator it_end = j.cend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j[0]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[1]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[2]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("const json + cbegin/cend") - { - json::const_iterator it_begin = j_const.cbegin(); - json::const_iterator it_end = j_const.cend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j[0]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[1]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[2]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("json + rbegin/rend") - { - json::reverse_iterator it_begin = j.rbegin(); - json::reverse_iterator it_end = j.rend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j[2]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[1]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[0]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("json + crbegin/crend") - { - json::const_reverse_iterator it_begin = j.crbegin(); - json::const_reverse_iterator it_end = j.crend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j[2]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[1]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[0]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("const json + crbegin/crend") - { - json::const_reverse_iterator it_begin = j_const.crbegin(); - json::const_reverse_iterator it_end = j_const.crend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j[2]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[1]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[0]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - } - - SECTION("object") - { - json j = {{"A", 1}, {"B", 2}, {"C", 3}}; - json j_const(j); - - SECTION("json + begin/end") - { - json::iterator it_begin = j.begin(); - json::iterator it_end = j.end(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j["A"]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["B"]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["C"]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("const json + begin/end") - { - json::const_iterator it_begin = j_const.begin(); - json::const_iterator it_end = j_const.end(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j_const["A"]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j_const["B"]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j_const["C"]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("json + cbegin/cend") - { - json::const_iterator it_begin = j.cbegin(); - json::const_iterator it_end = j.cend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j["A"]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["B"]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["C"]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("const json + cbegin/cend") - { - json::const_iterator it_begin = j_const.cbegin(); - json::const_iterator it_end = j_const.cend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j_const["A"]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j_const["B"]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j_const["C"]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("json + rbegin/rend") - { - json::reverse_iterator it_begin = j.rbegin(); - json::reverse_iterator it_end = j.rend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j["C"]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["B"]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["A"]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("json + crbegin/crend") - { - json::const_reverse_iterator it_begin = j.crbegin(); - json::const_reverse_iterator it_end = j.crend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j["C"]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["B"]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["A"]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("const json + crbegin/crend") - { - json::const_reverse_iterator it_begin = j_const.crbegin(); - json::const_reverse_iterator it_end = j_const.crend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j["C"]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["B"]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["A"]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - } - - SECTION("number (integer)") - { - json j = 23; - json j_const(j); - - SECTION("json + begin/end") - { - json::iterator it = j.begin(); - CHECK(it != j.end()); - CHECK(*it == j); - - it++; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - it--; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - - ++it; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - --it; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - } - - SECTION("const json + begin/end") - { - json::const_iterator it = j_const.begin(); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - it--; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - --it; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - } - - SECTION("json + cbegin/cend") - { - json::const_iterator it = j.cbegin(); - CHECK(it != j.cend()); - CHECK(*it == j); - - it++; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - it--; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - --it; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - } - - SECTION("const json + cbegin/cend") - { - json::const_iterator it = j_const.cbegin(); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - it--; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - --it; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - } - - SECTION("json + rbegin/rend") - { - json::reverse_iterator it = j.rbegin(); - CHECK(it != j.rend()); - CHECK(*it == j); - - it++; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - it--; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - --it; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - } - - SECTION("json + crbegin/crend") - { - json::const_reverse_iterator it = j.crbegin(); - CHECK(it != j.crend()); - CHECK(*it == j); - - it++; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - it--; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - --it; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - } - - SECTION("const json + crbegin/crend") - { - json::const_reverse_iterator it = j_const.crbegin(); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - it--; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - --it; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - } - } - - SECTION("number (float)") - { - json j = 23.42; - json j_const(j); - - SECTION("json + begin/end") - { - json::iterator it = j.begin(); - CHECK(it != j.end()); - CHECK(*it == j); - - it++; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - it--; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - - ++it; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - --it; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - } - - SECTION("const json + begin/end") - { - json::const_iterator it = j_const.begin(); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - it--; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - --it; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - } - - SECTION("json + cbegin/cend") - { - json::const_iterator it = j.cbegin(); - CHECK(it != j.cend()); - CHECK(*it == j); - - it++; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - it--; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - --it; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - } - - SECTION("const json + cbegin/cend") - { - json::const_iterator it = j_const.cbegin(); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - it--; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - --it; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - } - - SECTION("json + rbegin/rend") - { - json::reverse_iterator it = j.rbegin(); - CHECK(it != j.rend()); - CHECK(*it == j); - - it++; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - it--; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - --it; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - } - - SECTION("json + crbegin/crend") - { - json::const_reverse_iterator it = j.crbegin(); - CHECK(it != j.crend()); - CHECK(*it == j); - - it++; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - it--; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - --it; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - } - - SECTION("const json + crbegin/crend") - { - json::const_reverse_iterator it = j_const.crbegin(); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - it--; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - --it; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - } - } - - SECTION("null") - { - json j = nullptr; - json j_const(j); - - SECTION("json + begin/end") - { - json::iterator it = j.begin(); - CHECK(it == j.end()); - } - - SECTION("const json + begin/end") - { - json::const_iterator it_begin = j_const.begin(); - json::const_iterator it_end = j_const.end(); - CHECK(it_begin == it_end); - } - - SECTION("json + cbegin/cend") - { - json::const_iterator it_begin = j.cbegin(); - json::const_iterator it_end = j.cend(); - CHECK(it_begin == it_end); - } - - SECTION("const json + cbegin/cend") - { - json::const_iterator it_begin = j_const.cbegin(); - json::const_iterator it_end = j_const.cend(); - CHECK(it_begin == it_end); - } - - SECTION("json + rbegin/rend") - { - json::reverse_iterator it = j.rbegin(); - CHECK(it == j.rend()); - } - - SECTION("json + crbegin/crend") - { - json::const_reverse_iterator it = j.crbegin(); - CHECK(it == j.crend()); - } - - SECTION("const json + crbegin/crend") - { - json::const_reverse_iterator it = j_const.crbegin(); - CHECK(it == j_const.crend()); + SECTION("object") + { + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it[0], std::domain_error); + CHECK_THROWS_AS(it[1], std::domain_error); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it[0], std::domain_error); + CHECK_THROWS_AS(it[1], std::domain_error); + } + } + + SECTION("array") + { + { + auto it = j_array.begin(); + CHECK(it[0] == json(1)); + CHECK(it[1] == json(2)); + CHECK(it[2] == json(3)); + CHECK(it[3] == json(4)); + CHECK(it[4] == json(5)); + CHECK(it[5] == json(6)); + } + { + auto it = j_array.cbegin(); + CHECK(it[0] == json(1)); + CHECK(it[1] == json(2)); + CHECK(it[2] == json(3)); + CHECK(it[3] == json(4)); + CHECK(it[4] == json(5)); + CHECK(it[5] == json(6)); + } + } + + SECTION("null") + { + { + auto it = j_null.begin(); + CHECK_THROWS_AS(it[0], std::out_of_range); + CHECK_THROWS_AS(it[1], std::out_of_range); + } + { + auto it = j_null.cbegin(); + CHECK_THROWS_AS(it[0], std::out_of_range); + CHECK_THROWS_AS(it[1], std::out_of_range); + } + } + + SECTION("value") + { + { + auto it = j_value.begin(); + CHECK(it[0] == json(42)); + CHECK_THROWS_AS(it[1], std::out_of_range); + } + { + auto it = j_value.cbegin(); + CHECK(it[0] == json(42)); + CHECK_THROWS_AS(it[1], std::out_of_range); + } + } } } } @@ -4356,121 +4802,163 @@ TEST_CASE("modifiers") TEST_CASE("lexicographical comparison operators") { - json j_values = + SECTION("types") { - nullptr, nullptr, - 17, 42, - 3.14159, 23.42, - "foo", "bar", - true, false, - {1, 2, 3}, {"one", "two", "three"}, - {{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}} - }; - - SECTION("comparison: equal") - { - std::vector> expected = + std::vector j_types = { - {true, true, false, false, false, false, false, false, false, false, false, false, false, false}, - {true, true, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, true, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, true, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, true, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, true, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, true, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, true, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, true, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, true, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, true, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, true, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, true, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, true} + json::value_t::null, + json::value_t::boolean, + json::value_t::number_integer, + json::value_t::number_float, + json::value_t::object, + json::value_t::array, + json::value_t::string }; - for (size_t i = 0; i < j_values.size(); ++i) + SECTION("comparison: less") { - for (size_t j = 0; j < j_values.size(); ++j) + std::vector> expected = { - // check precomputed values - CHECK( (j_values[i] == j_values[j]) == expected[i][j] ); + {false, true, true, true, true, true, true}, + {false, false, true, true, true, true, true}, + {false, false, false, false, true, true, true}, + {false, false, false, false, true, true, true}, + {false, false, false, false, false, true, true}, + {false, false, false, false, false, false, true}, + {false, false, false, false, false, false, false} + }; + + for (size_t i = 0; i < j_types.size(); ++i) + { + for (size_t j = 0; j < j_types.size(); ++j) + { + CAPTURE(i); + CAPTURE(j); + // check precomputed values + CHECK( (j_types[i] < j_types[j]) == expected[i][j] ); + } } } } - SECTION("comparison: not equal") + SECTION("values") { - for (size_t i = 0; i < j_values.size(); ++i) + json j_values = { - for (size_t j = 0; j < j_values.size(); ++j) - { - // check definition - CHECK( (j_values[i] != j_values[j]) == not(j_values[i] == j_values[j]) ); - } - } - } - - SECTION("comparison: less") - { - std::vector> expected = - { - {false, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, true, false, true, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, true, true, false, true, false, false, false, false, false, false, false, false}, - {false, false, false, true, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, true, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, true, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, true, false} + nullptr, nullptr, + 17, 42, + 3.14159, 23.42, + "foo", "bar", + true, false, + {1, 2, 3}, {"one", "two", "three"}, + {{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}} }; - for (size_t i = 0; i < j_values.size(); ++i) + SECTION("comparison: equal") { - for (size_t j = 0; j < j_values.size(); ++j) + std::vector> expected = { - // check precomputed values - CHECK( (j_values[i] < j_values[j]) == expected[i][j] ); + {true, true, false, false, false, false, false, false, false, false, false, false, false, false}, + {true, true, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, true, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, true, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, true, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, true, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, true, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, true, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, true, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, true, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, true, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, true, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, true, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, true} + }; + + for (size_t i = 0; i < j_values.size(); ++i) + { + for (size_t j = 0; j < j_values.size(); ++j) + { + // check precomputed values + CHECK( (j_values[i] == j_values[j]) == expected[i][j] ); + } } } - } - SECTION("comparison: less than or equal equal") - { - for (size_t i = 0; i < j_values.size(); ++i) + SECTION("comparison: not equal") { - for (size_t j = 0; j < j_values.size(); ++j) + for (size_t i = 0; i < j_values.size(); ++i) { - // check definition - CHECK( (j_values[i] <= j_values[j]) == not(j_values[j] < j_values[i]) ); + for (size_t j = 0; j < j_values.size(); ++j) + { + // check definition + CHECK( (j_values[i] != j_values[j]) == not(j_values[i] == j_values[j]) ); + } } } - } - SECTION("comparison: greater than") - { - for (size_t i = 0; i < j_values.size(); ++i) + SECTION("comparison: less") { - for (size_t j = 0; j < j_values.size(); ++j) + std::vector> expected = { - // check definition - CHECK( (j_values[i] > j_values[j]) == (j_values[j] < j_values[i]) ); + {false, false, true, true, true, true, true, true, true, true, true, true, true, true}, + {false, false, true, true, true, true, true, true, true, true, true, true, true, true}, + {false, false, false, true, false, true, true, true, false, false, true, true, true, true}, + {false, false, false, false, false, false, true, true, false, false, true, true, true, true}, + {false, false, true, true, false, true, true, true, false, false, true, true, true, true}, + {false, false, false, true, false, false, true, true, false, false, true, true, true, true}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, true, false, false, false, false, false, false, false}, + {false, false, true, true, true, true, true, true, false, false, true, true, true, true}, + {false, false, true, true, true, true, true, true, true, false, true, true, true, true}, + {false, false, false, false, false, false, true, true, false, false, false, true, false, false}, + {false, false, false, false, false, false, true, true, false, false, false, false, false, false}, + {false, false, false, false, false, false, true, true, false, false, true, true, false, false}, + {false, false, false, false, false, false, true, true, false, false, true, true, true, false} + }; + + for (size_t i = 0; i < j_values.size(); ++i) + { + for (size_t j = 0; j < j_values.size(); ++j) + { + // check precomputed values + CHECK( (j_values[i] < j_values[j]) == expected[i][j] ); + } } } - } - SECTION("comparison: greater than or equal") - { - for (size_t i = 0; i < j_values.size(); ++i) + SECTION("comparison: less than or equal equal") { - for (size_t j = 0; j < j_values.size(); ++j) + for (size_t i = 0; i < j_values.size(); ++i) { - // check definition - CHECK( (j_values[i] >= j_values[j]) == not(j_values[i] < j_values[j]) ); + for (size_t j = 0; j < j_values.size(); ++j) + { + // check definition + CHECK( (j_values[i] <= j_values[j]) == not(j_values[j] < j_values[i]) ); + } + } + } + + SECTION("comparison: greater than") + { + for (size_t i = 0; i < j_values.size(); ++i) + { + for (size_t j = 0; j < j_values.size(); ++j) + { + // check definition + CHECK( (j_values[i] > j_values[j]) == (j_values[j] < j_values[i]) ); + } + } + } + + SECTION("comparison: greater than or equal") + { + for (size_t i = 0; i < j_values.size(); ++i) + { + for (size_t j = 0; j < j_values.size(); ++j) + { + // check definition + CHECK( (j_values[i] >= j_values[j]) == not(j_values[i] < j_values[j]) ); + } } } } @@ -4721,20 +5209,20 @@ TEST_CASE("iterator class") { json j(json::value_t::null); json::iterator it = j.begin(); - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); + CHECK(it.m_it.generic_iterator == 1); it++; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid); + CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1)); } SECTION("number") { json j(17); json::iterator it = j.begin(); - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin); + CHECK(it.m_it.generic_iterator == 0); it++; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); + CHECK(it.m_it.generic_iterator == 1); it++; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid); + CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1)); } SECTION("object") @@ -4772,20 +5260,20 @@ TEST_CASE("iterator class") { json j(json::value_t::null); json::iterator it = j.begin(); - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); + CHECK(it.m_it.generic_iterator == 1); ++it; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid); + CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1)); } SECTION("number") { json j(17); json::iterator it = j.begin(); - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin); + CHECK(it.m_it.generic_iterator == 0); ++it; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); + CHECK(it.m_it.generic_iterator == 1); ++it; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid); + CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1)); } SECTION("object") @@ -4823,20 +5311,18 @@ TEST_CASE("iterator class") { json j(json::value_t::null); json::iterator it = j.end(); - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); - it--; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid); + CHECK(it.m_it.generic_iterator == 1); } SECTION("number") { json j(17); json::iterator it = j.end(); - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); + CHECK(it.m_it.generic_iterator == 1); it--; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin); + CHECK(it.m_it.generic_iterator == 0); it--; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid); + CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1)); } SECTION("object") @@ -4874,20 +5360,18 @@ TEST_CASE("iterator class") { json j(json::value_t::null); json::iterator it = j.end(); - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); - --it; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid); + CHECK(it.m_it.generic_iterator == 1); } SECTION("number") { json j(17); json::iterator it = j.end(); - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); + CHECK(it.m_it.generic_iterator == 1); --it; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin); + CHECK(it.m_it.generic_iterator == 0); --it; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid); + CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1)); } SECTION("object") @@ -4919,63 +5403,6 @@ TEST_CASE("iterator class") } } } - - SECTION("comparison") - { - json j_values = - { - nullptr, nullptr, - 17, 42, - 3.14159, 23.42, - "foo", "bar", - true, false, - {1, 2, 3}, {"one", "two", "three"}, - {{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}} - }; - - SECTION("comparison: equal") - { - std::vector> expected = - { - {true, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, true, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, true, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, true, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, true, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, true, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, true, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, true, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, true, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, true, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, true, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, true, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, true, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, true} - }; - - for (size_t i = 0; i < j_values.size(); ++i) - { - for (size_t j = 0; j < j_values.size(); ++j) - { - // check precomputed values - CHECK( (j_values[i].begin() == j_values[j].begin()) == expected[i][j] ); - } - } - } - - SECTION("comparison: not equal") - { - for (size_t i = 0; i < j_values.size(); ++i) - { - for (size_t j = 0; j < j_values.size(); ++j) - { - // check definition - CHECK( (j_values[i].begin() != j_values[j].begin()) == not ((j_values[i].begin() == - j_values[j].begin())) ); - } - } - } - } } TEST_CASE("const_iterator class") @@ -5146,20 +5573,20 @@ TEST_CASE("const_iterator class") { json j(json::value_t::null); json::const_iterator it = j.cbegin(); - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); + CHECK(it.m_it.generic_iterator == 1); it++; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid); + CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1)); } SECTION("number") { json j(17); json::const_iterator it = j.cbegin(); - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin); + CHECK(it.m_it.generic_iterator == 0); it++; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); + CHECK(it.m_it.generic_iterator == 1); it++; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid); + CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1)); } SECTION("object") @@ -5197,20 +5624,20 @@ TEST_CASE("const_iterator class") { json j(json::value_t::null); json::const_iterator it = j.cbegin(); - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); + CHECK(it.m_it.generic_iterator == 1); ++it; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid); + CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1)); } SECTION("number") { json j(17); json::const_iterator it = j.cbegin(); - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin); + CHECK(it.m_it.generic_iterator == 0); ++it; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); + CHECK(it.m_it.generic_iterator == 1); ++it; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid); + CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1)); } SECTION("object") @@ -5248,20 +5675,18 @@ TEST_CASE("const_iterator class") { json j(json::value_t::null); json::const_iterator it = j.cend(); - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); - it--; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid); + CHECK(it.m_it.generic_iterator == 1); } SECTION("number") { json j(17); json::const_iterator it = j.cend(); - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); + CHECK(it.m_it.generic_iterator == 1); it--; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin); + CHECK(it.m_it.generic_iterator == 0); it--; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid); + CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1)); } SECTION("object") @@ -5299,20 +5724,18 @@ TEST_CASE("const_iterator class") { json j(json::value_t::null); json::const_iterator it = j.cend(); - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); - --it; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid); + CHECK(it.m_it.generic_iterator == 1); } SECTION("number") { json j(17); json::const_iterator it = j.cend(); - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); + CHECK(it.m_it.generic_iterator == 1); --it; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin); + CHECK(it.m_it.generic_iterator == 0); --it; - CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid); + CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1)); } SECTION("object") @@ -5344,63 +5767,6 @@ TEST_CASE("const_iterator class") } } } - - SECTION("comparison") - { - json j_values = - { - nullptr, nullptr, - 17, 42, - 3.14159, 23.42, - "foo", "bar", - true, false, - {1, 2, 3}, {"one", "two", "three"}, - {{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}} - }; - - SECTION("comparison: equal") - { - std::vector> expected = - { - {true, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, true, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, true, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, true, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, true, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, true, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, true, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, true, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, true, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, true, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, true, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, true, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, true, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, true} - }; - - for (size_t i = 0; i < j_values.size(); ++i) - { - for (size_t j = 0; j < j_values.size(); ++j) - { - // check precomputed values - CHECK( (j_values[i].cbegin() == j_values[j].cbegin()) == expected[i][j] ); - } - } - } - - SECTION("comparison: not equal") - { - for (size_t i = 0; i < j_values.size(); ++i) - { - for (size_t j = 0; j < j_values.size(); ++j) - { - // check definition - CHECK( (j_values[i].cbegin() != j_values[j].cbegin()) == not ((j_values[i].cbegin() == - j_values[j].cbegin())) ); - } - } - } - } } TEST_CASE("convenience functions") @@ -6143,3 +6509,326 @@ TEST_CASE("README", "[hide]") // etc. } } + +TEST_CASE("algorithms") +{ + json j_array = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz"}; + json j_object = {{"one", 1}, {"two", 2}}; + + SECTION("non-modifying sequence operations") + { + SECTION("std::all_of") + { + CHECK(std::all_of(j_array.begin(), j_array.end(), [](const json & value) + { + return value.size() > 0; + })); + CHECK(std::all_of(j_object.begin(), j_object.end(), [](const json & value) + { + return value.type() == json::value_t::number_integer; + })); + } + + SECTION("std::any_of") + { + CHECK(std::any_of(j_array.begin(), j_array.end(), [](const json & value) + { + return value.is_string() and value.get() == "foo"; + })); + CHECK(std::any_of(j_object.begin(), j_object.end(), [](const json & value) + { + return value.get() > 1; + })); + } + + SECTION("std::none_of") + { + CHECK(std::none_of(j_array.begin(), j_array.end(), [](const json & value) + { + return value.size() == 0; + })); + CHECK(std::none_of(j_object.begin(), j_object.end(), [](const json & value) + { + return value.get() <= 0; + })); + } + + SECTION("std::for_each") + { + SECTION("reading") + { + int sum = 0; + + std::for_each(j_array.cbegin(), j_array.cend(), [&sum](const json & value) + { + if (value.is_number()) + { + sum += static_cast(value); + } + }); + + CHECK(sum == 45); + } + + SECTION("writing") + { + auto add17 = [](json & value) + { + if (value.is_array()) + { + value.push_back(17); + } + }; + + std::for_each(j_array.begin(), j_array.end(), add17); + + CHECK(j_array[6] == json({1, 2, 3, 17})); + } + } + + SECTION("std::count") + { + CHECK(std::count(j_array.begin(), j_array.end(), json(true)) == 1); + } + + SECTION("std::count_if") + { + CHECK(std::count_if(j_array.begin(), j_array.end(), [](const json & value) + { + return (value.is_number()); + }) == 3); + CHECK(std::count_if(j_array.begin(), j_array.end(), [](const json&) + { + return true; + }) == 9); + } + + SECTION("std::mismatch") + { + json j_array2 = {13, 29, 3, {{"one", 1}, {"two", 2}, {"three", 3}}, true, false, {1, 2, 3}, "foo", "baz"}; + auto res = std::mismatch(j_array.begin(), j_array.end(), j_array2.begin()); + CHECK(*res.first == json({{"one", 1}, {"two", 2}})); + CHECK(*res.second == json({{"one", 1}, {"two", 2}, {"three", 3}})); + } + + SECTION("std::equal") + { + SECTION("using operator==") + { + CHECK(std::equal(j_array.begin(), j_array.end(), j_array.begin())); + CHECK(std::equal(j_object.begin(), j_object.end(), j_object.begin())); + CHECK(not std::equal(j_array.begin(), j_array.end(), j_object.begin())); + } + + SECTION("using user-defined comparison") + { + // compare objects only by size of its elements + json j_array2 = {13, 29, 3, {"Hello", "World"}, true, false, {{"one", 1}, {"two", 2}, {"three", 3}}, "foo", "baz"}; + CHECK(not std::equal(j_array.begin(), j_array.end(), j_array2.begin())); + CHECK(std::equal(j_array.begin(), j_array.end(), j_array2.begin(), + [](const json & a, const json & b) + { + return (a.size() == b.size()); + })); + } + } + + SECTION("std::find") + { + auto it = std::find(j_array.begin(), j_array.end(), json(false)); + CHECK(std::distance(j_array.begin(), it) == 5); + } + + SECTION("std::find_if") + { + auto it = std::find_if(j_array.begin(), j_array.end(), + [](const json & value) + { + return value.is_boolean(); + }); + CHECK(std::distance(j_array.begin(), it) == 4); + } + + SECTION("std::find_if_not") + { + auto it = std::find_if_not(j_array.begin(), j_array.end(), + [](const json & value) + { + return value.is_number(); + }); + CHECK(std::distance(j_array.begin(), it) == 3); + } + + SECTION("std::adjacent_find") + { + CHECK(std::adjacent_find(j_array.begin(), j_array.end()) == j_array.end()); + CHECK(std::adjacent_find(j_array.begin(), j_array.end(), + [](const json & v1, const json & v2) + { + return v1.type() == v2.type(); + }) == j_array.begin()); + } + } + + SECTION("modifying sequence operations") + { + SECTION("std::reverse") + { + std::reverse(j_array.begin(), j_array.end()); + CHECK(j_array == json({"baz", "foo", {1, 2, 3}, false, true, {{"one", 1}, {"two", 2}}, 3, 29, 13})); + } + + SECTION("std::rotate") + { + std::rotate(j_array.begin(), j_array.begin() + 1, j_array.end()); + CHECK(j_array == json({29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", 13})); + } + + SECTION("std::partition") + { + auto it = std::partition(j_array.begin(), j_array.end(), [](const json & v) + { + return v.is_string(); + }); + CHECK(std::distance(j_array.begin(), it) == 2); + CHECK(not it[2].is_string()); + } + } + + SECTION("sorting operations") + { + SECTION("std::sort") + { + SECTION("with standard comparison") + { + json j = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", nullptr}; + std::sort(j.begin(), j.end()); + CHECK(j == json({nullptr, false, true, 3, 13, 29, {{"one", 1}, {"two", 2}}, {1, 2, 3}, "baz", "foo"})); + } + + SECTION("with user-defined comparison") + { + json j = {3, {{"one", 1}, {"two", 2}}, {1, 2, 3}, nullptr}; + std::sort(j.begin(), j.end(), [](const json & a, const json & b) + { + return a.size() < b.size(); + }); + CHECK(j == json({nullptr, 3, {{"one", 1}, {"two", 2}}, {1, 2, 3}})); + } + + SECTION("sorting an object") + { + json j({{"one", 1}, {"two", 2}}); + CHECK_THROWS_AS(std::sort(j.begin(), j.end()), std::domain_error); + } + } + + SECTION("std::partial_sort") + { + json j = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", nullptr}; + std::partial_sort(j.begin(), j.begin() + 4, j.end()); + CHECK(j == json({nullptr, false, true, 3, {{"one", 1}, {"two", 2}}, 29, {1, 2, 3}, "foo", "baz", 13})); + } + } + + SECTION("set operations") + { + SECTION("std::merge") + { + { + json j1 = {2, 4, 6, 8}; + json j2 = {1, 2, 3, 5, 7}; + json j3; + + std::merge(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); + CHECK(j3 == json({1, 2, 2, 3, 4, 5, 6, 7, 8})); + } + } + + SECTION("std::set_difference") + { + json j1 = {1, 2, 3, 4, 5, 6, 7, 8}; + json j2 = {1, 2, 3, 5, 7}; + json j3; + + std::set_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); + CHECK(j3 == json({4, 6, 8})); + } + + SECTION("std::set_intersection") + { + json j1 = {1, 2, 3, 4, 5, 6, 7, 8}; + json j2 = {1, 2, 3, 5, 7}; + json j3; + + std::set_intersection(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); + CHECK(j3 == json({1, 2, 3, 5, 7})); + } + + SECTION("std::set_union") + { + json j1 = {2, 4, 6, 8}; + json j2 = {1, 2, 3, 5, 7}; + json j3; + + std::set_union(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); + CHECK(j3 == json({1, 2, 3, 4, 5, 6, 7, 8})); + } + + SECTION("std::set_symmetric_difference") + { + json j1 = {2, 4, 6, 8}; + json j2 = {1, 2, 3, 5, 7}; + json j3; + + std::set_symmetric_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); + CHECK(j3 == json({1, 3, 4, 5, 6, 7, 8})); + } + } + + SECTION("heap operations") + { + std::make_heap(j_array.begin(), j_array.end()); + CHECK(std::is_heap(j_array.begin(), j_array.end())); + std::sort_heap(j_array.begin(), j_array.end()); + CHECK(j_array == json({false, true, 3, 13, 29, {{"one", 1}, {"two", 2}}, {1, 2, 3}, "baz", "foo"})); + } +} + +TEST_CASE("concepts") +{ + SECTION("DefaultConstructible") + { + CHECK(std::is_nothrow_default_constructible::value); + } + + SECTION("MoveConstructible") + { + CHECK(std::is_nothrow_move_constructible::value); + } + + SECTION("CopyConstructible") + { + CHECK(std::is_copy_constructible::value); + } + + SECTION("MoveAssignable") + { + CHECK(std::is_nothrow_move_assignable::value); + } + + SECTION("CopyAssignable") + { + CHECK(std::is_copy_assignable::value); + } + + SECTION("Destructible") + { + CHECK(std::is_nothrow_destructible::value); + } + + SECTION("StandardLayoutType") + { + CHECK(std::is_standard_layout::value); + } +}