Commit d524e232 by Niels

overworked iterators

parent bc2e3a79
...@@ -214,6 +214,14 @@ j.empty(); // false ...@@ -214,6 +214,14 @@ j.empty(); // false
j.type(); // json::value_t::array j.type(); // json::value_t::array
j.clear(); // the array is empty again 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 // comparison
j == "[\"foo\", 1, true]"_json; // true j == "[\"foo\", 1, true]"_json; // true
......
json.gif

344 KB

...@@ -7,6 +7,26 @@ ...@@ -7,6 +7,26 @@
@see https://github.com/nlohmann/json @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 #ifndef _NLOHMANN_JSON
#define _NLOHMANN_JSON #define _NLOHMANN_JSON
...@@ -27,6 +47,7 @@ ...@@ -27,6 +47,7 @@
#include <vector> #include <vector>
/*! /*!
@brief namespace for Niels Lohmann
@see https://github.com/nlohmann @see https://github.com/nlohmann
*/ */
namespace nlohmann namespace nlohmann
...@@ -75,30 +96,76 @@ class basic_json ...@@ -75,30 +96,76 @@ class basic_json
class iterator; class iterator;
class const_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; using value_type = basic_json;
/// the type of an element reference
/*!
@brief the type of an element reference
@ingroup container
*/
using reference = basic_json&; 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&; 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<basic_json>;
/// the type of an element pointer /// the type of an element pointer
using pointer = basic_json*; using pointer = basic_json*;
/// the type of an element const pointer /// the type of an element const pointer
using const_pointer = const basic_json*; 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 @brief an iterator for a basic_json container
using size_type = std::size_t; @ingroup container
/// an iterator for a basic_json container */
using iterator = basic_json::iterator; 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; 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<iterator>; using reverse_iterator = std::reverse_iterator<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<const_iterator>; using const_reverse_iterator = std::reverse_iterator<const_iterator>;
/// returns the allocator associated with the container
inline allocator_type get_allocator() const
{
return allocator_type();
}
/////////////////////////// ///////////////////////////
// JSON value data types // // JSON value data types //
/////////////////////////// ///////////////////////////
...@@ -166,6 +233,85 @@ class basic_json ...@@ -166,6 +233,85 @@ class basic_json
number_float ///< number value (floating-point) 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 // // constructors //
...@@ -226,10 +372,11 @@ class basic_json ...@@ -226,10 +372,11 @@ class basic_json
} }
} }
/// create a null object (implicitly) /*!
inline basic_json() noexcept @brief create a null object (implicitly)
: m_type(value_t::null) @ingroup container
{} */
inline basic_json() noexcept = default;
/// create a null object (explicitly) /// create a null object (explicitly)
inline basic_json(std::nullptr_t) noexcept inline basic_json(std::nullptr_t) noexcept
...@@ -424,11 +571,15 @@ class basic_json ...@@ -424,11 +571,15 @@ class basic_json
return basic_json(l, false, value_t::object); return basic_json(l, false, value_t::object);
} }
/////////////////////////////////////// ///////////////////////////////////////
// other constructors and destructor // // other constructors and destructor //
/////////////////////////////////////// ///////////////////////////////////////
/// copy constructor /*!
@brief copy constructor
@ingroup container
*/
inline basic_json(const basic_json& other) inline basic_json(const basic_json& other)
: m_type(other.m_type) : m_type(other.m_type)
{ {
...@@ -487,7 +638,10 @@ class basic_json ...@@ -487,7 +638,10 @@ class basic_json
other.m_value = {}; other.m_value = {};
} }
/// copy assignment /*!
@brief copy assignment
@ingroup container
*/
inline reference& operator=(basic_json other) noexcept ( inline reference& operator=(basic_json other) noexcept (
std::is_nothrow_move_constructible<value_t>::value and std::is_nothrow_move_constructible<value_t>::value and
std::is_nothrow_move_assignable<value_t>::value and std::is_nothrow_move_assignable<value_t>::value and
...@@ -500,7 +654,10 @@ class basic_json ...@@ -500,7 +654,10 @@ class basic_json
return *this; return *this;
} }
/// destructor /*!
@brief destructor
@ingroup container
*/
inline ~basic_json() noexcept inline ~basic_json() noexcept
{ {
switch (m_type) switch (m_type)
...@@ -577,6 +734,42 @@ class basic_json ...@@ -577,6 +734,42 @@ class basic_json
return m_type; 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) /// return the type of the object (implicit)
operator value_t() const noexcept operator value_t() const noexcept
{ {
...@@ -871,7 +1064,10 @@ class basic_json ...@@ -871,7 +1064,10 @@ class basic_json
// iterators // // iterators //
/////////////// ///////////////
/// returns an iterator to the beginning of the container /*!
@brief returns an iterator to the first element
@ingroup container
*/
inline iterator begin() noexcept inline iterator begin() noexcept
{ {
iterator result(this); iterator result(this);
...@@ -879,7 +1075,10 @@ class basic_json ...@@ -879,7 +1075,10 @@ class basic_json
return result; 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 inline const_iterator begin() const noexcept
{ {
const_iterator result(this); const_iterator result(this);
...@@ -887,7 +1086,10 @@ class basic_json ...@@ -887,7 +1086,10 @@ class basic_json
return result; 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 inline const_iterator cbegin() const noexcept
{ {
const_iterator result(this); const_iterator result(this);
...@@ -895,7 +1097,10 @@ class basic_json ...@@ -895,7 +1097,10 @@ class basic_json
return result; 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 inline iterator end() noexcept
{ {
iterator result(this); iterator result(this);
...@@ -903,7 +1108,10 @@ class basic_json ...@@ -903,7 +1108,10 @@ class basic_json
return result; 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 inline const_iterator end() const noexcept
{ {
const_iterator result(this); const_iterator result(this);
...@@ -911,7 +1119,10 @@ class basic_json ...@@ -911,7 +1119,10 @@ class basic_json
return result; 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 inline const_iterator cend() const noexcept
{ {
const_iterator result(this); const_iterator result(this);
...@@ -919,37 +1130,55 @@ class basic_json ...@@ -919,37 +1130,55 @@ class basic_json
return result; 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 inline reverse_iterator rbegin() noexcept
{ {
return reverse_iterator(end()); 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 inline const_reverse_iterator rbegin() const noexcept
{ {
return const_reverse_iterator(end()); 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 inline reverse_iterator rend() noexcept
{ {
return reverse_iterator(begin()); 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 inline const_reverse_iterator rend() const noexcept
{ {
return const_reverse_iterator(begin()); 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 inline const_reverse_iterator crbegin() const noexcept
{ {
return const_reverse_iterator(cend()); 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 inline const_reverse_iterator crend() const noexcept
{ {
return const_reverse_iterator(cbegin()); return const_reverse_iterator(cbegin());
...@@ -960,7 +1189,10 @@ class basic_json ...@@ -960,7 +1189,10 @@ class basic_json
// capacity // // capacity //
////////////// //////////////
/// checks whether the container is empty /*!
@brief checks whether the container is empty
@ingroup container
*/
inline bool empty() const noexcept inline bool empty() const noexcept
{ {
switch (m_type) switch (m_type)
...@@ -988,7 +1220,10 @@ class basic_json ...@@ -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 inline size_type size() const noexcept
{ {
switch (m_type) switch (m_type)
...@@ -1016,7 +1251,10 @@ class basic_json ...@@ -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 inline size_type max_size() const noexcept
{ {
switch (m_type) switch (m_type)
...@@ -1186,7 +1424,10 @@ class basic_json ...@@ -1186,7 +1424,10 @@ class basic_json
return operator[](value.first); return operator[](value.first);
} }
/// swaps the contents /*!
@brief exchanges the values
@ingroup container
*/
inline void swap(reference other) noexcept ( inline void swap(reference other) noexcept (
std::is_nothrow_move_constructible<value_t>::value and std::is_nothrow_move_constructible<value_t>::value and
std::is_nothrow_move_assignable<value_t>::value and std::is_nothrow_move_assignable<value_t>::value and
...@@ -1242,7 +1483,10 @@ class basic_json ...@@ -1242,7 +1483,10 @@ class basic_json
// lexicographical comparison operators // // lexicographical comparison operators //
////////////////////////////////////////// //////////////////////////////////////////
/// comparison: equal /*!
@brief comparison: equal
@ingroup container
*/
friend bool operator==(const_reference lhs, const_reference rhs) friend bool operator==(const_reference lhs, const_reference rhs)
{ {
switch (lhs.type()) switch (lhs.type())
...@@ -1316,7 +1560,10 @@ class basic_json ...@@ -1316,7 +1560,10 @@ class basic_json
return false; return false;
} }
/// comparison: not equal /*!
@brief comparison: not equal
@ingroup container
*/
friend bool operator!=(const_reference lhs, const_reference rhs) friend bool operator!=(const_reference lhs, const_reference rhs)
{ {
return not (lhs == rhs); return not (lhs == rhs);
...@@ -1393,7 +1640,9 @@ class basic_json ...@@ -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 /// comparison: less than or equal
...@@ -1743,19 +1992,6 @@ class basic_json ...@@ -1743,19 +1992,6 @@ class basic_json
// iterators // // 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 /// an iterator value
template<typename array_iterator_t, typename object_iterator_t> template<typename array_iterator_t, typename object_iterator_t>
union internal_iterator union internal_iterator
...@@ -1765,21 +2001,15 @@ class basic_json ...@@ -1765,21 +2001,15 @@ class basic_json
/// iterator for JSON arrays /// iterator for JSON arrays
array_iterator_t array_iterator; array_iterator_t array_iterator;
/// generic iteraotr for all other value types /// generic iteraotr for all other value types
generic_iterator_value generic_iterator; difference_type generic_iterator;
/// default constructor /// default constructor
internal_iterator() : generic_iterator(generic_iterator_value::uninitialized) {} internal_iterator() : generic_iterator(-1) {}
/// 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) {}
}; };
public: public:
/// a bidirectional iterator for the basic_json class /// a random access iterator for the basic_json class
class iterator : public std::iterator<std::bidirectional_iterator_tag, basic_json> class iterator : public std::iterator<std::random_access_iterator_tag, basic_json>
{ {
public: public:
/// the type of the values when the iterator is dereferenced /// the type of the values when the iterator is dereferenced
...@@ -1813,7 +2043,7 @@ class basic_json ...@@ -1813,7 +2043,7 @@ class basic_json
} }
default: default:
{ {
m_it.generic_iterator = generic_iterator_value::uninitialized; m_it.generic_iterator = -1;
break; break;
} }
} }
...@@ -1847,13 +2077,13 @@ class basic_json ...@@ -1847,13 +2077,13 @@ class basic_json
case (basic_json::value_t::null): case (basic_json::value_t::null):
{ {
// set to end so begin()==end() is true: null is empty // 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; break;
} }
default: default:
{ {
m_it.generic_iterator = generic_iterator_value::begin; m_it.generic_iterator = 0;
break; break;
} }
} }
...@@ -1878,14 +2108,14 @@ class basic_json ...@@ -1878,14 +2108,14 @@ class basic_json
default: default:
{ {
m_it.generic_iterator = generic_iterator_value::end; m_it.generic_iterator = 1;
break; break;
} }
} }
} }
/// return a reference to the value pointed to by the iterator /// return a reference to the value pointed to by the iterator
inline reference operator*() const inline reference operator*()
{ {
switch (m_object->m_type) switch (m_object->m_type)
{ {
...@@ -1906,7 +2136,7 @@ class basic_json ...@@ -1906,7 +2136,7 @@ class basic_json
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::begin) if (m_it.generic_iterator == 0)
{ {
return *m_object; return *m_object;
} }
...@@ -1919,7 +2149,7 @@ class basic_json ...@@ -1919,7 +2149,7 @@ class basic_json
} }
/// dereference the iterator /// dereference the iterator
inline pointer operator->() const inline pointer operator->()
{ {
switch (m_object->m_type) switch (m_object->m_type)
{ {
...@@ -1933,9 +2163,14 @@ class basic_json ...@@ -1933,9 +2163,14 @@ class basic_json
return &*m_it.array_iterator; return &*m_it.array_iterator;
} }
case (basic_json::value_t::null):
{
throw std::out_of_range("cannot get value");
}
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::begin) if (m_it.generic_iterator == 0)
{ {
return m_object; return m_object;
} }
...@@ -1950,7 +2185,7 @@ class basic_json ...@@ -1950,7 +2185,7 @@ class basic_json
/// post-increment (it++) /// post-increment (it++)
inline iterator operator++(int) inline iterator operator++(int)
{ {
iterator result = *this; auto result = *this;
switch (m_object->m_type) switch (m_object->m_type)
{ {
...@@ -1968,14 +2203,7 @@ class basic_json ...@@ -1968,14 +2203,7 @@ class basic_json
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::begin) m_it.generic_iterator++;
{
m_it.generic_iterator = generic_iterator_value::end;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
break; break;
} }
} }
...@@ -2002,14 +2230,7 @@ class basic_json ...@@ -2002,14 +2230,7 @@ class basic_json
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::begin) ++m_it.generic_iterator;
{
m_it.generic_iterator = generic_iterator_value::end;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
break; break;
} }
} }
...@@ -2020,7 +2241,7 @@ class basic_json ...@@ -2020,7 +2241,7 @@ class basic_json
/// post-decrement (it--) /// post-decrement (it--)
inline iterator operator--(int) inline iterator operator--(int)
{ {
iterator result = *this; auto result = *this;
switch (m_object->m_type) switch (m_object->m_type)
{ {
...@@ -2036,22 +2257,9 @@ class basic_json ...@@ -2036,22 +2257,9 @@ class basic_json
break; break;
} }
case (basic_json::value_t::null):
{
m_it.generic_iterator = generic_iterator_value::invalid;
break;
}
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::end) m_it.generic_iterator--;
{
m_it.generic_iterator = generic_iterator_value::begin;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
break; break;
} }
} }
...@@ -2076,22 +2284,9 @@ class basic_json ...@@ -2076,22 +2284,9 @@ class basic_json
break; break;
} }
case (basic_json::value_t::null):
{
m_it.generic_iterator = generic_iterator_value::invalid;
break;
}
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::end) --m_it.generic_iterator;
{
m_it.generic_iterator = generic_iterator_value::begin;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
break; break;
} }
} }
...@@ -2102,9 +2297,10 @@ class basic_json ...@@ -2102,9 +2297,10 @@ class basic_json
/// comparison: equal /// comparison: equal
inline bool operator==(const iterator& other) const 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) switch (m_object->m_type)
...@@ -2132,6 +2328,157 @@ class basic_json ...@@ -2132,6 +2328,157 @@ class basic_json
return not operator==(other); 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: private:
/// associated JSON instance /// associated JSON instance
pointer m_object = nullptr; pointer m_object = nullptr;
...@@ -2139,8 +2486,8 @@ class basic_json ...@@ -2139,8 +2486,8 @@ class basic_json
internal_iterator<typename array_t::iterator, typename object_t::iterator> m_it; internal_iterator<typename array_t::iterator, typename object_t::iterator> m_it;
}; };
/// a const bidirectional iterator for the basic_json class /// a const random access iterator for the basic_json class
class const_iterator : public std::iterator<std::bidirectional_iterator_tag, const basic_json> class const_iterator : public std::iterator<std::random_access_iterator_tag, const basic_json>
{ {
public: public:
/// the type of the values when the iterator is dereferenced /// the type of the values when the iterator is dereferenced
...@@ -2174,7 +2521,7 @@ class basic_json ...@@ -2174,7 +2521,7 @@ class basic_json
} }
default: default:
{ {
m_it.generic_iterator = generic_iterator_value::uninitialized; m_it.generic_iterator = -1;
break; break;
} }
} }
...@@ -2233,13 +2580,13 @@ class basic_json ...@@ -2233,13 +2580,13 @@ class basic_json
case (basic_json::value_t::null): case (basic_json::value_t::null):
{ {
// set to end so begin()==end() is true: null is empty // 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; break;
} }
default: default:
{ {
m_it.generic_iterator = generic_iterator_value::begin; m_it.generic_iterator = 0;
break; break;
} }
} }
...@@ -2264,7 +2611,7 @@ class basic_json ...@@ -2264,7 +2611,7 @@ class basic_json
default: default:
{ {
m_it.generic_iterator = generic_iterator_value::end; m_it.generic_iterator = 1;
break; break;
} }
} }
...@@ -2292,7 +2639,7 @@ class basic_json ...@@ -2292,7 +2639,7 @@ class basic_json
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::begin) if (m_it.generic_iterator == 0)
{ {
return *m_object; return *m_object;
} }
...@@ -2321,7 +2668,7 @@ class basic_json ...@@ -2321,7 +2668,7 @@ class basic_json
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::begin) if (m_it.generic_iterator == 0)
{ {
return m_object; return m_object;
} }
...@@ -2336,7 +2683,7 @@ class basic_json ...@@ -2336,7 +2683,7 @@ class basic_json
/// post-increment (it++) /// post-increment (it++)
inline const_iterator operator++(int) inline const_iterator operator++(int)
{ {
const_iterator result = *this; auto result = *this;
switch (m_object->m_type) switch (m_object->m_type)
{ {
...@@ -2354,14 +2701,7 @@ class basic_json ...@@ -2354,14 +2701,7 @@ class basic_json
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::begin) m_it.generic_iterator++;
{
m_it.generic_iterator = generic_iterator_value::end;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
break; break;
} }
} }
...@@ -2388,14 +2728,7 @@ class basic_json ...@@ -2388,14 +2728,7 @@ class basic_json
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::begin) ++m_it.generic_iterator;
{
m_it.generic_iterator = generic_iterator_value::end;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
break; break;
} }
} }
...@@ -2406,7 +2739,7 @@ class basic_json ...@@ -2406,7 +2739,7 @@ class basic_json
/// post-decrement (it--) /// post-decrement (it--)
inline const_iterator operator--(int) inline const_iterator operator--(int)
{ {
const_iterator result = *this; auto result = *this;
switch (m_object->m_type) switch (m_object->m_type)
{ {
...@@ -2422,22 +2755,9 @@ class basic_json ...@@ -2422,22 +2755,9 @@ class basic_json
break; break;
} }
case (basic_json::value_t::null):
{
m_it.generic_iterator = generic_iterator_value::invalid;
break;
}
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::end) m_it.generic_iterator--;
{
m_it.generic_iterator = generic_iterator_value::begin;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
break; break;
} }
} }
...@@ -2462,22 +2782,9 @@ class basic_json ...@@ -2462,22 +2782,9 @@ class basic_json
break; break;
} }
case (basic_json::value_t::null):
{
m_it.generic_iterator = generic_iterator_value::invalid;
break;
}
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::end) --m_it.generic_iterator;
{
m_it.generic_iterator = generic_iterator_value::begin;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
break; break;
} }
} }
...@@ -2488,9 +2795,10 @@ class basic_json ...@@ -2488,9 +2795,10 @@ class basic_json
/// comparison: equal /// comparison: equal
inline bool operator==(const const_iterator& other) const 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) switch (m_object->m_type)
...@@ -2518,6 +2826,157 @@ class basic_json ...@@ -2518,6 +2826,157 @@ class basic_json
return not operator==(other); 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: private:
/// associated JSON instance /// associated JSON instance
pointer m_object = nullptr; pointer m_object = nullptr;
...@@ -2577,7 +3036,8 @@ class basic_json ...@@ -2577,7 +3036,8 @@ class basic_json
/*! /*!
@brief create a string from a Unicode code point @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 @return string representation of the code point
@exception std::out_of_range if code point is >0x10ffff @exception std::out_of_range if code point is >0x10ffff
@exception std::invalid_argument if the low surrogate is invalid @exception std::invalid_argument if the low surrogate is invalid
...@@ -3581,6 +4041,9 @@ basic_json_parser_59: ...@@ -3581,6 +4041,9 @@ basic_json_parser_59:
const lexer_char_t* m_limit = nullptr; const lexer_char_t* m_limit = nullptr;
}; };
/*!
@brief syntax analysis
*/
class parser class parser
{ {
public: public:
...@@ -3816,7 +4279,10 @@ using json = basic_json<>; ...@@ -3816,7 +4279,10 @@ using json = basic_json<>;
// specialization of std::swap, and std::hash // specialization of std::swap, and std::hash
namespace std namespace std
{ {
/// swaps the values of two JSON objects /*!
@brief exchanges the values of two JSON objects
@ingroup container
*/
template <> template <>
inline void swap(nlohmann::json& j1, inline void swap(nlohmann::json& j1,
nlohmann::json& j2) noexcept( nlohmann::json& j2) noexcept(
......
...@@ -7,6 +7,26 @@ ...@@ -7,6 +7,26 @@
@see https://github.com/nlohmann/json @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 #ifndef _NLOHMANN_JSON
#define _NLOHMANN_JSON #define _NLOHMANN_JSON
...@@ -27,6 +47,7 @@ ...@@ -27,6 +47,7 @@
#include <vector> #include <vector>
/*! /*!
@brief namespace for Niels Lohmann
@see https://github.com/nlohmann @see https://github.com/nlohmann
*/ */
namespace nlohmann namespace nlohmann
...@@ -75,30 +96,76 @@ class basic_json ...@@ -75,30 +96,76 @@ class basic_json
class iterator; class iterator;
class const_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; using value_type = basic_json;
/// the type of an element reference
/*!
@brief the type of an element reference
@ingroup container
*/
using reference = basic_json&; 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&; 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<basic_json>;
/// the type of an element pointer /// the type of an element pointer
using pointer = basic_json*; using pointer = basic_json*;
/// the type of an element const pointer /// the type of an element const pointer
using const_pointer = const basic_json*; 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 @brief an iterator for a basic_json container
using size_type = std::size_t; @ingroup container
/// an iterator for a basic_json container */
using iterator = basic_json::iterator; 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; 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<iterator>; using reverse_iterator = std::reverse_iterator<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<const_iterator>; using const_reverse_iterator = std::reverse_iterator<const_iterator>;
/// returns the allocator associated with the container
inline allocator_type get_allocator() const
{
return allocator_type();
}
/////////////////////////// ///////////////////////////
// JSON value data types // // JSON value data types //
/////////////////////////// ///////////////////////////
...@@ -166,6 +233,85 @@ class basic_json ...@@ -166,6 +233,85 @@ class basic_json
number_float ///< number value (floating-point) 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 // // constructors //
...@@ -226,10 +372,11 @@ class basic_json ...@@ -226,10 +372,11 @@ class basic_json
} }
} }
/// create a null object (implicitly) /*!
inline basic_json() noexcept @brief create a null object (implicitly)
: m_type(value_t::null) @ingroup container
{} */
inline basic_json() noexcept = default;
/// create a null object (explicitly) /// create a null object (explicitly)
inline basic_json(std::nullptr_t) noexcept inline basic_json(std::nullptr_t) noexcept
...@@ -424,11 +571,15 @@ class basic_json ...@@ -424,11 +571,15 @@ class basic_json
return basic_json(l, false, value_t::object); return basic_json(l, false, value_t::object);
} }
/////////////////////////////////////// ///////////////////////////////////////
// other constructors and destructor // // other constructors and destructor //
/////////////////////////////////////// ///////////////////////////////////////
/// copy constructor /*!
@brief copy constructor
@ingroup container
*/
inline basic_json(const basic_json& other) inline basic_json(const basic_json& other)
: m_type(other.m_type) : m_type(other.m_type)
{ {
...@@ -487,7 +638,10 @@ class basic_json ...@@ -487,7 +638,10 @@ class basic_json
other.m_value = {}; other.m_value = {};
} }
/// copy assignment /*!
@brief copy assignment
@ingroup container
*/
inline reference& operator=(basic_json other) noexcept ( inline reference& operator=(basic_json other) noexcept (
std::is_nothrow_move_constructible<value_t>::value and std::is_nothrow_move_constructible<value_t>::value and
std::is_nothrow_move_assignable<value_t>::value and std::is_nothrow_move_assignable<value_t>::value and
...@@ -500,7 +654,10 @@ class basic_json ...@@ -500,7 +654,10 @@ class basic_json
return *this; return *this;
} }
/// destructor /*!
@brief destructor
@ingroup container
*/
inline ~basic_json() noexcept inline ~basic_json() noexcept
{ {
switch (m_type) switch (m_type)
...@@ -577,6 +734,42 @@ class basic_json ...@@ -577,6 +734,42 @@ class basic_json
return m_type; 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) /// return the type of the object (implicit)
operator value_t() const noexcept operator value_t() const noexcept
{ {
...@@ -871,7 +1064,10 @@ class basic_json ...@@ -871,7 +1064,10 @@ class basic_json
// iterators // // iterators //
/////////////// ///////////////
/// returns an iterator to the beginning of the container /*!
@brief returns an iterator to the first element
@ingroup container
*/
inline iterator begin() noexcept inline iterator begin() noexcept
{ {
iterator result(this); iterator result(this);
...@@ -879,7 +1075,10 @@ class basic_json ...@@ -879,7 +1075,10 @@ class basic_json
return result; 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 inline const_iterator begin() const noexcept
{ {
const_iterator result(this); const_iterator result(this);
...@@ -887,7 +1086,10 @@ class basic_json ...@@ -887,7 +1086,10 @@ class basic_json
return result; 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 inline const_iterator cbegin() const noexcept
{ {
const_iterator result(this); const_iterator result(this);
...@@ -895,7 +1097,10 @@ class basic_json ...@@ -895,7 +1097,10 @@ class basic_json
return result; 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 inline iterator end() noexcept
{ {
iterator result(this); iterator result(this);
...@@ -903,7 +1108,10 @@ class basic_json ...@@ -903,7 +1108,10 @@ class basic_json
return result; 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 inline const_iterator end() const noexcept
{ {
const_iterator result(this); const_iterator result(this);
...@@ -911,7 +1119,10 @@ class basic_json ...@@ -911,7 +1119,10 @@ class basic_json
return result; 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 inline const_iterator cend() const noexcept
{ {
const_iterator result(this); const_iterator result(this);
...@@ -919,37 +1130,55 @@ class basic_json ...@@ -919,37 +1130,55 @@ class basic_json
return result; 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 inline reverse_iterator rbegin() noexcept
{ {
return reverse_iterator(end()); 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 inline const_reverse_iterator rbegin() const noexcept
{ {
return const_reverse_iterator(end()); 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 inline reverse_iterator rend() noexcept
{ {
return reverse_iterator(begin()); 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 inline const_reverse_iterator rend() const noexcept
{ {
return const_reverse_iterator(begin()); 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 inline const_reverse_iterator crbegin() const noexcept
{ {
return const_reverse_iterator(cend()); 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 inline const_reverse_iterator crend() const noexcept
{ {
return const_reverse_iterator(cbegin()); return const_reverse_iterator(cbegin());
...@@ -960,7 +1189,10 @@ class basic_json ...@@ -960,7 +1189,10 @@ class basic_json
// capacity // // capacity //
////////////// //////////////
/// checks whether the container is empty /*!
@brief checks whether the container is empty
@ingroup container
*/
inline bool empty() const noexcept inline bool empty() const noexcept
{ {
switch (m_type) switch (m_type)
...@@ -988,7 +1220,10 @@ class basic_json ...@@ -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 inline size_type size() const noexcept
{ {
switch (m_type) switch (m_type)
...@@ -1016,7 +1251,10 @@ class basic_json ...@@ -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 inline size_type max_size() const noexcept
{ {
switch (m_type) switch (m_type)
...@@ -1186,7 +1424,10 @@ class basic_json ...@@ -1186,7 +1424,10 @@ class basic_json
return operator[](value.first); return operator[](value.first);
} }
/// swaps the contents /*!
@brief exchanges the values
@ingroup container
*/
inline void swap(reference other) noexcept ( inline void swap(reference other) noexcept (
std::is_nothrow_move_constructible<value_t>::value and std::is_nothrow_move_constructible<value_t>::value and
std::is_nothrow_move_assignable<value_t>::value and std::is_nothrow_move_assignable<value_t>::value and
...@@ -1242,7 +1483,10 @@ class basic_json ...@@ -1242,7 +1483,10 @@ class basic_json
// lexicographical comparison operators // // lexicographical comparison operators //
////////////////////////////////////////// //////////////////////////////////////////
/// comparison: equal /*!
@brief comparison: equal
@ingroup container
*/
friend bool operator==(const_reference lhs, const_reference rhs) friend bool operator==(const_reference lhs, const_reference rhs)
{ {
switch (lhs.type()) switch (lhs.type())
...@@ -1316,7 +1560,10 @@ class basic_json ...@@ -1316,7 +1560,10 @@ class basic_json
return false; return false;
} }
/// comparison: not equal /*!
@brief comparison: not equal
@ingroup container
*/
friend bool operator!=(const_reference lhs, const_reference rhs) friend bool operator!=(const_reference lhs, const_reference rhs)
{ {
return not (lhs == rhs); return not (lhs == rhs);
...@@ -1393,7 +1640,9 @@ class basic_json ...@@ -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 /// comparison: less than or equal
...@@ -1743,19 +1992,6 @@ class basic_json ...@@ -1743,19 +1992,6 @@ class basic_json
// iterators // // 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 /// an iterator value
template<typename array_iterator_t, typename object_iterator_t> template<typename array_iterator_t, typename object_iterator_t>
union internal_iterator union internal_iterator
...@@ -1765,21 +2001,15 @@ class basic_json ...@@ -1765,21 +2001,15 @@ class basic_json
/// iterator for JSON arrays /// iterator for JSON arrays
array_iterator_t array_iterator; array_iterator_t array_iterator;
/// generic iteraotr for all other value types /// generic iteraotr for all other value types
generic_iterator_value generic_iterator; difference_type generic_iterator;
/// default constructor /// default constructor
internal_iterator() : generic_iterator(generic_iterator_value::uninitialized) {} internal_iterator() : generic_iterator(-1) {}
/// 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) {}
}; };
public: public:
/// a bidirectional iterator for the basic_json class /// a random access iterator for the basic_json class
class iterator : public std::iterator<std::bidirectional_iterator_tag, basic_json> class iterator : public std::iterator<std::random_access_iterator_tag, basic_json>
{ {
public: public:
/// the type of the values when the iterator is dereferenced /// the type of the values when the iterator is dereferenced
...@@ -1813,7 +2043,7 @@ class basic_json ...@@ -1813,7 +2043,7 @@ class basic_json
} }
default: default:
{ {
m_it.generic_iterator = generic_iterator_value::uninitialized; m_it.generic_iterator = -1;
break; break;
} }
} }
...@@ -1847,13 +2077,13 @@ class basic_json ...@@ -1847,13 +2077,13 @@ class basic_json
case (basic_json::value_t::null): case (basic_json::value_t::null):
{ {
// set to end so begin()==end() is true: null is empty // 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; break;
} }
default: default:
{ {
m_it.generic_iterator = generic_iterator_value::begin; m_it.generic_iterator = 0;
break; break;
} }
} }
...@@ -1878,14 +2108,14 @@ class basic_json ...@@ -1878,14 +2108,14 @@ class basic_json
default: default:
{ {
m_it.generic_iterator = generic_iterator_value::end; m_it.generic_iterator = 1;
break; break;
} }
} }
} }
/// return a reference to the value pointed to by the iterator /// return a reference to the value pointed to by the iterator
inline reference operator*() const inline reference operator*()
{ {
switch (m_object->m_type) switch (m_object->m_type)
{ {
...@@ -1906,7 +2136,7 @@ class basic_json ...@@ -1906,7 +2136,7 @@ class basic_json
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::begin) if (m_it.generic_iterator == 0)
{ {
return *m_object; return *m_object;
} }
...@@ -1919,7 +2149,7 @@ class basic_json ...@@ -1919,7 +2149,7 @@ class basic_json
} }
/// dereference the iterator /// dereference the iterator
inline pointer operator->() const inline pointer operator->()
{ {
switch (m_object->m_type) switch (m_object->m_type)
{ {
...@@ -1933,9 +2163,14 @@ class basic_json ...@@ -1933,9 +2163,14 @@ class basic_json
return &*m_it.array_iterator; return &*m_it.array_iterator;
} }
case (basic_json::value_t::null):
{
throw std::out_of_range("cannot get value");
}
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::begin) if (m_it.generic_iterator == 0)
{ {
return m_object; return m_object;
} }
...@@ -1950,7 +2185,7 @@ class basic_json ...@@ -1950,7 +2185,7 @@ class basic_json
/// post-increment (it++) /// post-increment (it++)
inline iterator operator++(int) inline iterator operator++(int)
{ {
iterator result = *this; auto result = *this;
switch (m_object->m_type) switch (m_object->m_type)
{ {
...@@ -1968,14 +2203,7 @@ class basic_json ...@@ -1968,14 +2203,7 @@ class basic_json
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::begin) m_it.generic_iterator++;
{
m_it.generic_iterator = generic_iterator_value::end;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
break; break;
} }
} }
...@@ -2002,14 +2230,7 @@ class basic_json ...@@ -2002,14 +2230,7 @@ class basic_json
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::begin) ++m_it.generic_iterator;
{
m_it.generic_iterator = generic_iterator_value::end;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
break; break;
} }
} }
...@@ -2020,7 +2241,7 @@ class basic_json ...@@ -2020,7 +2241,7 @@ class basic_json
/// post-decrement (it--) /// post-decrement (it--)
inline iterator operator--(int) inline iterator operator--(int)
{ {
iterator result = *this; auto result = *this;
switch (m_object->m_type) switch (m_object->m_type)
{ {
...@@ -2036,22 +2257,9 @@ class basic_json ...@@ -2036,22 +2257,9 @@ class basic_json
break; break;
} }
case (basic_json::value_t::null):
{
m_it.generic_iterator = generic_iterator_value::invalid;
break;
}
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::end) m_it.generic_iterator--;
{
m_it.generic_iterator = generic_iterator_value::begin;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
break; break;
} }
} }
...@@ -2076,22 +2284,9 @@ class basic_json ...@@ -2076,22 +2284,9 @@ class basic_json
break; break;
} }
case (basic_json::value_t::null):
{
m_it.generic_iterator = generic_iterator_value::invalid;
break;
}
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::end) --m_it.generic_iterator;
{
m_it.generic_iterator = generic_iterator_value::begin;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
break; break;
} }
} }
...@@ -2102,9 +2297,10 @@ class basic_json ...@@ -2102,9 +2297,10 @@ class basic_json
/// comparison: equal /// comparison: equal
inline bool operator==(const iterator& other) const 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) switch (m_object->m_type)
...@@ -2132,6 +2328,157 @@ class basic_json ...@@ -2132,6 +2328,157 @@ class basic_json
return not operator==(other); 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: private:
/// associated JSON instance /// associated JSON instance
pointer m_object = nullptr; pointer m_object = nullptr;
...@@ -2139,8 +2486,8 @@ class basic_json ...@@ -2139,8 +2486,8 @@ class basic_json
internal_iterator<typename array_t::iterator, typename object_t::iterator> m_it; internal_iterator<typename array_t::iterator, typename object_t::iterator> m_it;
}; };
/// a const bidirectional iterator for the basic_json class /// a const random access iterator for the basic_json class
class const_iterator : public std::iterator<std::bidirectional_iterator_tag, const basic_json> class const_iterator : public std::iterator<std::random_access_iterator_tag, const basic_json>
{ {
public: public:
/// the type of the values when the iterator is dereferenced /// the type of the values when the iterator is dereferenced
...@@ -2174,7 +2521,7 @@ class basic_json ...@@ -2174,7 +2521,7 @@ class basic_json
} }
default: default:
{ {
m_it.generic_iterator = generic_iterator_value::uninitialized; m_it.generic_iterator = -1;
break; break;
} }
} }
...@@ -2233,13 +2580,13 @@ class basic_json ...@@ -2233,13 +2580,13 @@ class basic_json
case (basic_json::value_t::null): case (basic_json::value_t::null):
{ {
// set to end so begin()==end() is true: null is empty // 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; break;
} }
default: default:
{ {
m_it.generic_iterator = generic_iterator_value::begin; m_it.generic_iterator = 0;
break; break;
} }
} }
...@@ -2264,7 +2611,7 @@ class basic_json ...@@ -2264,7 +2611,7 @@ class basic_json
default: default:
{ {
m_it.generic_iterator = generic_iterator_value::end; m_it.generic_iterator = 1;
break; break;
} }
} }
...@@ -2292,7 +2639,7 @@ class basic_json ...@@ -2292,7 +2639,7 @@ class basic_json
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::begin) if (m_it.generic_iterator == 0)
{ {
return *m_object; return *m_object;
} }
...@@ -2321,7 +2668,7 @@ class basic_json ...@@ -2321,7 +2668,7 @@ class basic_json
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::begin) if (m_it.generic_iterator == 0)
{ {
return m_object; return m_object;
} }
...@@ -2336,7 +2683,7 @@ class basic_json ...@@ -2336,7 +2683,7 @@ class basic_json
/// post-increment (it++) /// post-increment (it++)
inline const_iterator operator++(int) inline const_iterator operator++(int)
{ {
const_iterator result = *this; auto result = *this;
switch (m_object->m_type) switch (m_object->m_type)
{ {
...@@ -2354,14 +2701,7 @@ class basic_json ...@@ -2354,14 +2701,7 @@ class basic_json
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::begin) m_it.generic_iterator++;
{
m_it.generic_iterator = generic_iterator_value::end;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
break; break;
} }
} }
...@@ -2388,14 +2728,7 @@ class basic_json ...@@ -2388,14 +2728,7 @@ class basic_json
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::begin) ++m_it.generic_iterator;
{
m_it.generic_iterator = generic_iterator_value::end;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
break; break;
} }
} }
...@@ -2406,7 +2739,7 @@ class basic_json ...@@ -2406,7 +2739,7 @@ class basic_json
/// post-decrement (it--) /// post-decrement (it--)
inline const_iterator operator--(int) inline const_iterator operator--(int)
{ {
const_iterator result = *this; auto result = *this;
switch (m_object->m_type) switch (m_object->m_type)
{ {
...@@ -2422,22 +2755,9 @@ class basic_json ...@@ -2422,22 +2755,9 @@ class basic_json
break; break;
} }
case (basic_json::value_t::null):
{
m_it.generic_iterator = generic_iterator_value::invalid;
break;
}
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::end) m_it.generic_iterator--;
{
m_it.generic_iterator = generic_iterator_value::begin;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
break; break;
} }
} }
...@@ -2462,22 +2782,9 @@ class basic_json ...@@ -2462,22 +2782,9 @@ class basic_json
break; break;
} }
case (basic_json::value_t::null):
{
m_it.generic_iterator = generic_iterator_value::invalid;
break;
}
default: default:
{ {
if (m_it.generic_iterator == generic_iterator_value::end) --m_it.generic_iterator;
{
m_it.generic_iterator = generic_iterator_value::begin;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
break; break;
} }
} }
...@@ -2488,9 +2795,10 @@ class basic_json ...@@ -2488,9 +2795,10 @@ class basic_json
/// comparison: equal /// comparison: equal
inline bool operator==(const const_iterator& other) const 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) switch (m_object->m_type)
...@@ -2518,6 +2826,157 @@ class basic_json ...@@ -2518,6 +2826,157 @@ class basic_json
return not operator==(other); 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: private:
/// associated JSON instance /// associated JSON instance
pointer m_object = nullptr; pointer m_object = nullptr;
...@@ -2577,7 +3036,8 @@ class basic_json ...@@ -2577,7 +3036,8 @@ class basic_json
/*! /*!
@brief create a string from a Unicode code point @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 @return string representation of the code point
@exception std::out_of_range if code point is >0x10ffff @exception std::out_of_range if code point is >0x10ffff
@exception std::invalid_argument if the low surrogate is invalid @exception std::invalid_argument if the low surrogate is invalid
...@@ -2930,6 +3390,9 @@ class basic_json ...@@ -2930,6 +3390,9 @@ class basic_json
const lexer_char_t* m_limit = nullptr; const lexer_char_t* m_limit = nullptr;
}; };
/*!
@brief syntax analysis
*/
class parser class parser
{ {
public: public:
...@@ -3165,7 +3628,10 @@ using json = basic_json<>; ...@@ -3165,7 +3628,10 @@ using json = basic_json<>;
// specialization of std::swap, and std::hash // specialization of std::swap, and std::hash
namespace std namespace std
{ {
/// swaps the values of two JSON objects /*!
@brief exchanges the values of two JSON objects
@ingroup container
*/
template <> template <>
inline void swap(nlohmann::json& j1, inline void swap(nlohmann::json& j1,
nlohmann::json& j2) noexcept( nlohmann::json& j2) noexcept(
......
...@@ -1050,6 +1050,86 @@ TEST_CASE("other constructors and destructor") ...@@ -1050,6 +1050,86 @@ TEST_CASE("other constructors and destructor")
TEST_CASE("object inspection") 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") SECTION("serialization")
{ {
json j {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} }; json j {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} };
...@@ -2366,6 +2446,8 @@ TEST_CASE("element access") ...@@ -2366,6 +2446,8 @@ TEST_CASE("element access")
TEST_CASE("iterators") TEST_CASE("iterators")
{ {
SECTION("basic behavior")
{
SECTION("uninitialized") SECTION("uninitialized")
{ {
json::iterator it; json::iterator it;
...@@ -3497,6 +3579,370 @@ TEST_CASE("iterators") ...@@ -3497,6 +3579,370 @@ TEST_CASE("iterators")
CHECK(it == j_const.crend()); CHECK(it == j_const.crend());
} }
} }
}
SECTION("iterator comparisons")
{
json j_values = {nullptr, true, 42, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"};
for (json& j : j_values)
{
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;
// 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));
}
// 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) );
}
// 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);
}
}
// 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) );
}
}
}
// check exceptions if different objects are compared
for (auto j : j_values)
{
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);
CHECK_THROWS_AS(j.begin() < k.begin(), std::domain_error);
CHECK_THROWS_AS(j.cbegin() < k.cbegin(), std::domain_error);
}
}
}
}
SECTION("iterator arithmetic")
{
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("addition and subtraction")
{
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);
}
}
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));
}
}
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());
}
}
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("subscript operator")
{
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);
}
}
}
}
} }
TEST_CASE("capacity") TEST_CASE("capacity")
...@@ -4356,6 +4802,47 @@ TEST_CASE("modifiers") ...@@ -4356,6 +4802,47 @@ TEST_CASE("modifiers")
TEST_CASE("lexicographical comparison operators") TEST_CASE("lexicographical comparison operators")
{ {
SECTION("types")
{
std::vector<json::value_t> j_types =
{
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
};
SECTION("comparison: less")
{
std::vector<std::vector<bool>> expected =
{
{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("values")
{
json j_values = json j_values =
{ {
nullptr, nullptr, nullptr, nullptr,
...@@ -4413,20 +4900,20 @@ TEST_CASE("lexicographical comparison operators") ...@@ -4413,20 +4900,20 @@ TEST_CASE("lexicographical comparison operators")
{ {
std::vector<std::vector<bool>> expected = std::vector<std::vector<bool>> expected =
{ {
{false, false, false, false, false, false, false, false, false, false, false, false, false, false}, {false, false, true, true, true, true, true, true, true, true, true, true, true, true},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false}, {false, false, true, true, true, true, true, true, true, true, true, true, true, true},
{false, false, false, true, false, true, false, false, false, false, false, false, false, false}, {false, false, false, true, false, true, 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, true, false, false, true, true, true, true},
{false, false, true, true, false, true, false, false, false, false, false, false, false, false}, {false, false, true, true, false, true, true, true, false, false, true, true, true, true},
{false, false, false, true, false, false, false, false, false, false, false, false, false, false}, {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, 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, true, 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, true, true, true, true, false, false, true, true, true, true},
{false, false, false, false, false, false, false, false, true, false, false, false, false, false}, {false, false, true, true, true, true, true, true, true, 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, true, 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, true, 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, true, true, 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, true, true, false, false, true, true, true, false}
}; };
for (size_t i = 0; i < j_values.size(); ++i) for (size_t i = 0; i < j_values.size(); ++i)
...@@ -4474,6 +4961,7 @@ TEST_CASE("lexicographical comparison operators") ...@@ -4474,6 +4961,7 @@ TEST_CASE("lexicographical comparison operators")
} }
} }
} }
}
} }
TEST_CASE("serialization") TEST_CASE("serialization")
...@@ -4721,20 +5209,20 @@ TEST_CASE("iterator class") ...@@ -4721,20 +5209,20 @@ TEST_CASE("iterator class")
{ {
json j(json::value_t::null); json j(json::value_t::null);
json::iterator it = j.begin(); json::iterator it = j.begin();
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); CHECK(it.m_it.generic_iterator == 1);
it++; 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") SECTION("number")
{ {
json j(17); json j(17);
json::iterator it = j.begin(); json::iterator it = j.begin();
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin); CHECK(it.m_it.generic_iterator == 0);
it++; it++;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); CHECK(it.m_it.generic_iterator == 1);
it++; 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") SECTION("object")
...@@ -4772,20 +5260,20 @@ TEST_CASE("iterator class") ...@@ -4772,20 +5260,20 @@ TEST_CASE("iterator class")
{ {
json j(json::value_t::null); json j(json::value_t::null);
json::iterator it = j.begin(); json::iterator it = j.begin();
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); CHECK(it.m_it.generic_iterator == 1);
++it; ++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") SECTION("number")
{ {
json j(17); json j(17);
json::iterator it = j.begin(); json::iterator it = j.begin();
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin); CHECK(it.m_it.generic_iterator == 0);
++it; ++it;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); CHECK(it.m_it.generic_iterator == 1);
++it; ++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") SECTION("object")
...@@ -4823,20 +5311,18 @@ TEST_CASE("iterator class") ...@@ -4823,20 +5311,18 @@ TEST_CASE("iterator class")
{ {
json j(json::value_t::null); json j(json::value_t::null);
json::iterator it = j.end(); 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::invalid);
} }
SECTION("number") SECTION("number")
{ {
json j(17); json j(17);
json::iterator it = j.end(); json::iterator it = j.end();
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); CHECK(it.m_it.generic_iterator == 1);
it--; it--;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin); CHECK(it.m_it.generic_iterator == 0);
it--; 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") SECTION("object")
...@@ -4874,20 +5360,18 @@ TEST_CASE("iterator class") ...@@ -4874,20 +5360,18 @@ TEST_CASE("iterator class")
{ {
json j(json::value_t::null); json j(json::value_t::null);
json::iterator it = j.end(); 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::invalid);
} }
SECTION("number") SECTION("number")
{ {
json j(17); json j(17);
json::iterator it = j.end(); json::iterator it = j.end();
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); CHECK(it.m_it.generic_iterator == 1);
--it; --it;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin); CHECK(it.m_it.generic_iterator == 0);
--it; --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") SECTION("object")
...@@ -4919,63 +5403,6 @@ TEST_CASE("iterator class") ...@@ -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<std::vector<bool>> 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") TEST_CASE("const_iterator class")
...@@ -5146,20 +5573,20 @@ TEST_CASE("const_iterator class") ...@@ -5146,20 +5573,20 @@ TEST_CASE("const_iterator class")
{ {
json j(json::value_t::null); json j(json::value_t::null);
json::const_iterator it = j.cbegin(); 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++; 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") SECTION("number")
{ {
json j(17); json j(17);
json::const_iterator it = j.cbegin(); 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++; it++;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); CHECK(it.m_it.generic_iterator == 1);
it++; 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") SECTION("object")
...@@ -5197,20 +5624,20 @@ TEST_CASE("const_iterator class") ...@@ -5197,20 +5624,20 @@ TEST_CASE("const_iterator class")
{ {
json j(json::value_t::null); json j(json::value_t::null);
json::const_iterator it = j.cbegin(); 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; ++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") SECTION("number")
{ {
json j(17); json j(17);
json::const_iterator it = j.cbegin(); 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; ++it;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end); CHECK(it.m_it.generic_iterator == 1);
++it; ++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") SECTION("object")
...@@ -5248,20 +5675,18 @@ TEST_CASE("const_iterator class") ...@@ -5248,20 +5675,18 @@ TEST_CASE("const_iterator class")
{ {
json j(json::value_t::null); json j(json::value_t::null);
json::const_iterator it = j.cend(); 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::invalid);
} }
SECTION("number") SECTION("number")
{ {
json j(17); json j(17);
json::const_iterator it = j.cend(); 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--; it--;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin); CHECK(it.m_it.generic_iterator == 0);
it--; 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") SECTION("object")
...@@ -5299,20 +5724,18 @@ TEST_CASE("const_iterator class") ...@@ -5299,20 +5724,18 @@ TEST_CASE("const_iterator class")
{ {
json j(json::value_t::null); json j(json::value_t::null);
json::const_iterator it = j.cend(); 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::invalid);
} }
SECTION("number") SECTION("number")
{ {
json j(17); json j(17);
json::const_iterator it = j.cend(); 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; --it;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin); CHECK(it.m_it.generic_iterator == 0);
--it; --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") SECTION("object")
...@@ -5344,63 +5767,6 @@ TEST_CASE("const_iterator class") ...@@ -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<std::vector<bool>> 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") TEST_CASE("convenience functions")
...@@ -6143,3 +6509,326 @@ TEST_CASE("README", "[hide]") ...@@ -6143,3 +6509,326 @@ TEST_CASE("README", "[hide]")
// etc. // 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<std::string>() == "foo";
}));
CHECK(std::any_of(j_object.begin(), j_object.end(), [](const json & value)
{
return value.get<int>() > 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<int>() <= 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<int>(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<json>::value);
}
SECTION("MoveConstructible")
{
CHECK(std::is_nothrow_move_constructible<json>::value);
}
SECTION("CopyConstructible")
{
CHECK(std::is_copy_constructible<json>::value);
}
SECTION("MoveAssignable")
{
CHECK(std::is_nothrow_move_assignable<json>::value);
}
SECTION("CopyAssignable")
{
CHECK(std::is_copy_assignable<json>::value);
}
SECTION("Destructible")
{
CHECK(std::is_nothrow_destructible<json>::value);
}
SECTION("StandardLayoutType")
{
CHECK(std::is_standard_layout<json>::value);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment