Commit d524e232 by Niels

overworked iterators

parent bc2e3a79
......@@ -214,6 +214,14 @@ j.empty(); // false
j.type(); // json::value_t::array
j.clear(); // the array is empty again
// convenience type checkers
j.is_null();
j.is_boolean();
j.is_number();
j.is_object();
j.is_array();
j.is_string();
// comparison
j == "[\"foo\", 1, true]"_json; // true
......
json.gif

344 KB

......@@ -7,6 +7,26 @@
@see https://github.com/nlohmann/json
*/
/*!
@defgroup container Container
@brief C++ Container concept
A Container is an object used to store other objects and taking care of the
management of the memory used by the objects it contains.
@see http://en.cppreference.com/w/cpp/concept/Container
@defgroup reversiblecontainer Reversible Container
@brief C++ Reversible Container concept
@ingroup container
A ReversibleContainer is a Container that has iterators that meet the
requirements of either BidirectionalIterator or RandomAccessIterator. Such
iterators allow a ReversibleContainer to be iterated over in reverse.
@see http://en.cppreference.com/w/cpp/concept/ReversibleContainer
*/
#ifndef _NLOHMANN_JSON
#define _NLOHMANN_JSON
......@@ -27,6 +47,7 @@
#include <vector>
/*!
@brief namespace for Niels Lohmann
@see https://github.com/nlohmann
*/
namespace nlohmann
......@@ -75,30 +96,76 @@ class basic_json
class iterator;
class const_iterator;
/// the type of elements in a basic_json container
/*!
@brief the type of elements in a basic_json container
@ingroup container
*/
using value_type = basic_json;
/// the type of an element reference
/*!
@brief the type of an element reference
@ingroup container
*/
using reference = basic_json&;
/// the type of an element const reference
/*!
@brief the type of an element const reference
@ingroup container
*/
using const_reference = const basic_json&;
/*!
@brief a type to represent differences between iterators
@ingroup container
*/
using difference_type = std::ptrdiff_t;
/*!
@brief a type to represent container sizes
@ingroup container
*/
using size_type = std::size_t;
/// the allocator type
using allocator_type = Allocator<basic_json>;
/// the type of an element pointer
using pointer = basic_json*;
/// the type of an element const pointer
using const_pointer = const basic_json*;
/// a type to represent differences between iterators
using difference_type = std::ptrdiff_t;
/// a type to represent container sizes
using size_type = std::size_t;
/// an iterator for a basic_json container
/*!
@brief an iterator for a basic_json container
@ingroup container
*/
using iterator = basic_json::iterator;
/// a const iterator for a basic_json container
/*!
@brief a const iterator for a basic_json container
@ingroup container
*/
using const_iterator = basic_json::const_iterator;
/// a reverse iterator for a basic_json container
/*!
@brief a reverse iterator for a basic_json container
@ingroup reversiblecontainer
*/
using reverse_iterator = std::reverse_iterator<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>;
/// returns the allocator associated with the container
inline allocator_type get_allocator() const
{
return allocator_type();
}
///////////////////////////
// JSON value data types //
///////////////////////////
......@@ -166,6 +233,85 @@ class basic_json
number_float ///< number value (floating-point)
};
/*!
@brief comparison operator for JSON value types
Returns an ordering that is similar to Python:
- order: null < boolean < number < object < array < string
- furthermore, each type is not smaller than itself
*/
friend bool operator<(const value_t lhs, const value_t rhs)
{
// no type is smaller than itself
if (lhs == rhs)
{
return false;
}
switch (lhs)
{
case (value_t::null):
{
// nulls are smaller than all other types
return true;
}
case (value_t::boolean):
{
// only nulls are smaller than booleans
return (rhs != value_t::null);
}
case (value_t::number_float):
case (value_t::number_integer):
{
switch (rhs)
{
// numbers are smaller than objects, arrays, and string
case (value_t::object):
case (value_t::array):
case (value_t::string):
{
return true;
}
default:
{
return false;
}
}
}
case (value_t::object):
{
switch (rhs)
{
// objects are smaller than arrays and string
case (value_t::array):
case (value_t::string):
{
return true;
}
default:
{
return false;
}
}
}
case (value_t::array):
{
// arrays are smaller than strings
return (rhs == value_t::string);
}
default:
{
// a string is not smaller than any other types
return false;
}
}
}
//////////////////
// constructors //
......@@ -226,10 +372,11 @@ class basic_json
}
}
/// create a null object (implicitly)
inline basic_json() noexcept
: m_type(value_t::null)
{}
/*!
@brief create a null object (implicitly)
@ingroup container
*/
inline basic_json() noexcept = default;
/// create a null object (explicitly)
inline basic_json(std::nullptr_t) noexcept
......@@ -424,11 +571,15 @@ class basic_json
return basic_json(l, false, value_t::object);
}
///////////////////////////////////////
// other constructors and destructor //
///////////////////////////////////////
/// copy constructor
/*!
@brief copy constructor
@ingroup container
*/
inline basic_json(const basic_json& other)
: m_type(other.m_type)
{
......@@ -487,7 +638,10 @@ class basic_json
other.m_value = {};
}
/// copy assignment
/*!
@brief copy assignment
@ingroup container
*/
inline reference& operator=(basic_json other) noexcept (
std::is_nothrow_move_constructible<value_t>::value and
std::is_nothrow_move_assignable<value_t>::value and
......@@ -500,7 +654,10 @@ class basic_json
return *this;
}
/// destructor
/*!
@brief destructor
@ingroup container
*/
inline ~basic_json() noexcept
{
switch (m_type)
......@@ -577,6 +734,42 @@ class basic_json
return m_type;
}
// return whether value is null
inline bool is_null() const noexcept
{
return m_type == value_t::null;
}
// return whether value is boolean
inline bool is_boolean() const noexcept
{
return m_type == value_t::boolean;
}
// return whether value is number
inline bool is_number() const noexcept
{
return (m_type == value_t::number_integer) or (m_type == value_t::number_float);
}
// return whether value is object
inline bool is_object() const noexcept
{
return m_type == value_t::object;
}
// return whether value is array
inline bool is_array() const noexcept
{
return m_type == value_t::array;
}
// return whether value is string
inline bool is_string() const noexcept
{
return m_type == value_t::string;
}
/// return the type of the object (implicit)
operator value_t() const noexcept
{
......@@ -871,7 +1064,10 @@ class basic_json
// iterators //
///////////////
/// returns an iterator to the beginning of the container
/*!
@brief returns an iterator to the first element
@ingroup container
*/
inline iterator begin() noexcept
{
iterator result(this);
......@@ -879,7 +1075,10 @@ class basic_json
return result;
}
/// returns a const iterator to the beginning of the container
/*!
@brief returns a const iterator to the first element
@ingroup container
*/
inline const_iterator begin() const noexcept
{
const_iterator result(this);
......@@ -887,7 +1086,10 @@ class basic_json
return result;
}
/// returns a const iterator to the beginning of the container
/*!
@brief returns a const iterator to the first element
@ingroup container
*/
inline const_iterator cbegin() const noexcept
{
const_iterator result(this);
......@@ -895,7 +1097,10 @@ class basic_json
return result;
}
/// returns an iterator to the end of the container
/*!
@brief returns an iterator to one past the last element
@ingroup container
*/
inline iterator end() noexcept
{
iterator result(this);
......@@ -903,7 +1108,10 @@ class basic_json
return result;
}
/// returns a const iterator to the end of the container
/*!
@brief returns a const iterator to one past the last element
@ingroup container
*/
inline const_iterator end() const noexcept
{
const_iterator result(this);
......@@ -911,7 +1119,10 @@ class basic_json
return result;
}
/// returns a const iterator to the end of the container
/*!
@brief returns a const iterator to one past the last element
@ingroup container
*/
inline const_iterator cend() const noexcept
{
const_iterator result(this);
......@@ -919,37 +1130,55 @@ class basic_json
return result;
}
/// returns a reverse iterator to the beginning
/*!
@brief returns a reverse iterator to the first element
@ingroup reversiblecontainer
*/
inline reverse_iterator rbegin() noexcept
{
return reverse_iterator(end());
}
/// returns a reverse iterator to the beginning
/*!
@brief returns a const reverse iterator to the first element
@ingroup reversiblecontainer
*/
inline const_reverse_iterator rbegin() const noexcept
{
return const_reverse_iterator(end());
}
/// returns a reverse iterator to the end
/*!
@brief returns a reverse iterator to one past the last element
@ingroup reversiblecontainer
*/
inline reverse_iterator rend() noexcept
{
return reverse_iterator(begin());
}
/// returns a reverse iterator to the end
/*!
@brief returns a const reverse iterator to one past the last element
@ingroup reversiblecontainer
*/
inline const_reverse_iterator rend() const noexcept
{
return const_reverse_iterator(begin());
}
/// returns a reverse iterator to the beginning
/*!
@brief returns a const reverse iterator to the first element
@ingroup reversiblecontainer
*/
inline const_reverse_iterator crbegin() const noexcept
{
return const_reverse_iterator(cend());
}
/// returns a reverse iterator to the end
/*!
@brief returns a const reverse iterator to one past the last element
@ingroup reversiblecontainer
*/
inline const_reverse_iterator crend() const noexcept
{
return const_reverse_iterator(cbegin());
......@@ -960,7 +1189,10 @@ class basic_json
// capacity //
//////////////
/// checks whether the container is empty
/*!
@brief checks whether the container is empty
@ingroup container
*/
inline bool empty() const noexcept
{
switch (m_type)
......@@ -988,7 +1220,10 @@ class basic_json
}
}
/// returns the number of elements
/*!
@brief returns the number of elements
@ingroup container
*/
inline size_type size() const noexcept
{
switch (m_type)
......@@ -1016,7 +1251,10 @@ class basic_json
}
}
/// returns the maximum possible number of elements
/*!
@brief returns the maximum possible number of elements
@ingroup container
*/
inline size_type max_size() const noexcept
{
switch (m_type)
......@@ -1186,7 +1424,10 @@ class basic_json
return operator[](value.first);
}
/// swaps the contents
/*!
@brief exchanges the values
@ingroup container
*/
inline void swap(reference other) noexcept (
std::is_nothrow_move_constructible<value_t>::value and
std::is_nothrow_move_assignable<value_t>::value and
......@@ -1242,7 +1483,10 @@ class basic_json
// lexicographical comparison operators //
//////////////////////////////////////////
/// comparison: equal
/*!
@brief comparison: equal
@ingroup container
*/
friend bool operator==(const_reference lhs, const_reference rhs)
{
switch (lhs.type())
......@@ -1316,7 +1560,10 @@ class basic_json
return false;
}
/// comparison: not equal
/*!
@brief comparison: not equal
@ingroup container
*/
friend bool operator!=(const_reference lhs, const_reference rhs)
{
return not (lhs == rhs);
......@@ -1393,7 +1640,9 @@ class basic_json
}
}
return false;
// We only reach this line if we cannot compare values. In that case,
// we compare types.
return lhs.type() < rhs.type();
}
/// comparison: less than or equal
......@@ -1743,19 +1992,6 @@ class basic_json
// iterators //
///////////////
/// values of a generic iterator type of non-container JSON values
enum class generic_iterator_value
{
/// the iterator was not initialized
uninitialized,
/// the iterator points to the only value
begin,
/// the iterator points past the only value
end,
/// the iterator points to an invalid value
invalid
};
/// an iterator value
template<typename array_iterator_t, typename object_iterator_t>
union internal_iterator
......@@ -1765,21 +2001,15 @@ class basic_json
/// iterator for JSON arrays
array_iterator_t array_iterator;
/// generic iteraotr for all other value types
generic_iterator_value generic_iterator;
difference_type generic_iterator;
/// default constructor
internal_iterator() : generic_iterator(generic_iterator_value::uninitialized) {}
/// constructor for object iterators
internal_iterator(object_iterator_t v) : object_iterator(v) {}
/// constructor for array iterators
internal_iterator(array_iterator_t v) : array_iterator(v) {}
/// constructor for generic iterators
internal_iterator(generic_iterator_value v) : generic_iterator(v) {}
internal_iterator() : generic_iterator(-1) {}
};
public:
/// a bidirectional iterator for the basic_json class
class iterator : public std::iterator<std::bidirectional_iterator_tag, basic_json>
/// a random access iterator for the basic_json class
class iterator : public std::iterator<std::random_access_iterator_tag, basic_json>
{
public:
/// the type of the values when the iterator is dereferenced
......@@ -1813,7 +2043,7 @@ class basic_json
}
default:
{
m_it.generic_iterator = generic_iterator_value::uninitialized;
m_it.generic_iterator = -1;
break;
}
}
......@@ -1847,13 +2077,13 @@ class basic_json
case (basic_json::value_t::null):
{
// set to end so begin()==end() is true: null is empty
m_it.generic_iterator = generic_iterator_value::end;
m_it.generic_iterator = 1;
break;
}
default:
{
m_it.generic_iterator = generic_iterator_value::begin;
m_it.generic_iterator = 0;
break;
}
}
......@@ -1878,14 +2108,14 @@ class basic_json
default:
{
m_it.generic_iterator = generic_iterator_value::end;
m_it.generic_iterator = 1;
break;
}
}
}
/// return a reference to the value pointed to by the iterator
inline reference operator*() const
inline reference operator*()
{
switch (m_object->m_type)
{
......@@ -1906,7 +2136,7 @@ class basic_json
default:
{
if (m_it.generic_iterator == generic_iterator_value::begin)
if (m_it.generic_iterator == 0)
{
return *m_object;
}
......@@ -1919,7 +2149,7 @@ class basic_json
}
/// dereference the iterator
inline pointer operator->() const
inline pointer operator->()
{
switch (m_object->m_type)
{
......@@ -1933,9 +2163,14 @@ class basic_json
return &*m_it.array_iterator;
}
case (basic_json::value_t::null):
{
throw std::out_of_range("cannot get value");
}
default:
{
if (m_it.generic_iterator == generic_iterator_value::begin)
if (m_it.generic_iterator == 0)
{
return m_object;
}
......@@ -1950,7 +2185,7 @@ class basic_json
/// post-increment (it++)
inline iterator operator++(int)
{
iterator result = *this;
auto result = *this;
switch (m_object->m_type)
{
......@@ -1968,14 +2203,7 @@ class basic_json
default:
{
if (m_it.generic_iterator == generic_iterator_value::begin)
{
m_it.generic_iterator = generic_iterator_value::end;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
m_it.generic_iterator++;
break;
}
}
......@@ -2002,14 +2230,7 @@ class basic_json
default:
{
if (m_it.generic_iterator == generic_iterator_value::begin)
{
m_it.generic_iterator = generic_iterator_value::end;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
++m_it.generic_iterator;
break;
}
}
......@@ -2020,7 +2241,7 @@ class basic_json
/// post-decrement (it--)
inline iterator operator--(int)
{
iterator result = *this;
auto result = *this;
switch (m_object->m_type)
{
......@@ -2036,22 +2257,9 @@ class basic_json
break;
}
case (basic_json::value_t::null):
{
m_it.generic_iterator = generic_iterator_value::invalid;
break;
}
default:
{
if (m_it.generic_iterator == generic_iterator_value::end)
{
m_it.generic_iterator = generic_iterator_value::begin;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
m_it.generic_iterator--;
break;
}
}
......@@ -2076,22 +2284,9 @@ class basic_json
break;
}
case (basic_json::value_t::null):
{
m_it.generic_iterator = generic_iterator_value::invalid;
break;
}
default:
{
if (m_it.generic_iterator == generic_iterator_value::end)
{
m_it.generic_iterator = generic_iterator_value::begin;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
--m_it.generic_iterator;
break;
}
}
......@@ -2102,9 +2297,10 @@ class basic_json
/// comparison: equal
inline bool operator==(const iterator& other) const
{
if (m_object != other.m_object or m_object->m_type != other.m_object->m_type)
// if objects are not the same, the comparison is undefined
if (m_object != other.m_object)
{
return false;
throw std::domain_error("cannot compare iterators of different containers");
}
switch (m_object->m_type)
......@@ -2132,6 +2328,157 @@ class basic_json
return not operator==(other);
}
/// comparison: smaller
inline bool operator<(const iterator& other) const
{
// if objects are not the same, the comparison is undefined
if (m_object != other.m_object)
{
throw std::domain_error("cannot compare iterators of different containers");
}
switch (m_object->m_type)
{
case (basic_json::value_t::object):
{
throw std::domain_error("cannot use operator< for object iterators");
}
case (basic_json::value_t::array):
{
return (m_it.array_iterator < other.m_it.array_iterator);
}
default:
{
return (m_it.generic_iterator < other.m_it.generic_iterator);
}
}
}
/// comparison: less than or equal
inline bool operator<=(const iterator& other) const
{
return not other.operator < (*this);
}
/// comparison: greater than
inline bool operator>(const iterator& other) const
{
return not operator<=(other);
}
/// comparison: greater than or equal
inline bool operator>=(const iterator& other) const
{
return not operator<(other);
}
/// add to iterator
inline iterator& operator+=(difference_type i)
{
switch (m_object->m_type)
{
case (basic_json::value_t::object):
{
throw std::domain_error("cannot use operator+= for object iterators");
}
case (basic_json::value_t::array):
{
m_it.array_iterator += i;
break;
}
default:
{
m_it.generic_iterator += i;
break;
}
}
return *this;
}
/// subtract from iterator
inline iterator& operator-=(difference_type i)
{
return operator+=(-i);
}
/// add to iterator
inline iterator operator+(difference_type i)
{
auto result = *this;
result += i;
return result;
}
/// subtract from iterator
inline iterator operator-(difference_type i)
{
auto result = *this;
result -= i;
return result;
}
/// return difference
inline difference_type operator-(const iterator& other) const
{
switch (m_object->m_type)
{
case (basic_json::value_t::object):
{
throw std::domain_error("cannot use operator- for object iterators");
return 0;
}
case (basic_json::value_t::array):
{
return m_it.array_iterator - other.m_it.array_iterator;
}
default:
{
return m_it.generic_iterator - other.m_it.generic_iterator;
}
}
}
/// access to successor
inline reference operator[](difference_type n)
{
switch (m_object->m_type)
{
case (basic_json::value_t::object):
{
throw std::domain_error("cannot use operator[] for object iterators");
}
case (basic_json::value_t::array):
{
return *(m_it.array_iterator + n);
}
case (basic_json::value_t::null):
{
throw std::out_of_range("cannot get value");
}
default:
{
if (m_it.generic_iterator == -n)
{
return *m_object;
}
else
{
throw std::out_of_range("cannot get value");
}
}
}
}
private:
/// associated JSON instance
pointer m_object = nullptr;
......@@ -2139,8 +2486,8 @@ class basic_json
internal_iterator<typename array_t::iterator, typename object_t::iterator> m_it;
};
/// a const bidirectional iterator for the basic_json class
class const_iterator : public std::iterator<std::bidirectional_iterator_tag, const basic_json>
/// a const random access iterator for the basic_json class
class const_iterator : public std::iterator<std::random_access_iterator_tag, const basic_json>
{
public:
/// the type of the values when the iterator is dereferenced
......@@ -2174,7 +2521,7 @@ class basic_json
}
default:
{
m_it.generic_iterator = generic_iterator_value::uninitialized;
m_it.generic_iterator = -1;
break;
}
}
......@@ -2233,13 +2580,13 @@ class basic_json
case (basic_json::value_t::null):
{
// set to end so begin()==end() is true: null is empty
m_it.generic_iterator = generic_iterator_value::end;
m_it.generic_iterator = 1;
break;
}
default:
{
m_it.generic_iterator = generic_iterator_value::begin;
m_it.generic_iterator = 0;
break;
}
}
......@@ -2264,7 +2611,7 @@ class basic_json
default:
{
m_it.generic_iterator = generic_iterator_value::end;
m_it.generic_iterator = 1;
break;
}
}
......@@ -2292,7 +2639,7 @@ class basic_json
default:
{
if (m_it.generic_iterator == generic_iterator_value::begin)
if (m_it.generic_iterator == 0)
{
return *m_object;
}
......@@ -2321,7 +2668,7 @@ class basic_json
default:
{
if (m_it.generic_iterator == generic_iterator_value::begin)
if (m_it.generic_iterator == 0)
{
return m_object;
}
......@@ -2336,7 +2683,7 @@ class basic_json
/// post-increment (it++)
inline const_iterator operator++(int)
{
const_iterator result = *this;
auto result = *this;
switch (m_object->m_type)
{
......@@ -2354,14 +2701,7 @@ class basic_json
default:
{
if (m_it.generic_iterator == generic_iterator_value::begin)
{
m_it.generic_iterator = generic_iterator_value::end;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
m_it.generic_iterator++;
break;
}
}
......@@ -2388,14 +2728,7 @@ class basic_json
default:
{
if (m_it.generic_iterator == generic_iterator_value::begin)
{
m_it.generic_iterator = generic_iterator_value::end;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
++m_it.generic_iterator;
break;
}
}
......@@ -2406,7 +2739,7 @@ class basic_json
/// post-decrement (it--)
inline const_iterator operator--(int)
{
const_iterator result = *this;
auto result = *this;
switch (m_object->m_type)
{
......@@ -2422,22 +2755,9 @@ class basic_json
break;
}
case (basic_json::value_t::null):
{
m_it.generic_iterator = generic_iterator_value::invalid;
break;
}
default:
{
if (m_it.generic_iterator == generic_iterator_value::end)
{
m_it.generic_iterator = generic_iterator_value::begin;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
m_it.generic_iterator--;
break;
}
}
......@@ -2462,22 +2782,9 @@ class basic_json
break;
}
case (basic_json::value_t::null):
{
m_it.generic_iterator = generic_iterator_value::invalid;
break;
}
default:
{
if (m_it.generic_iterator == generic_iterator_value::end)
{
m_it.generic_iterator = generic_iterator_value::begin;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
--m_it.generic_iterator;
break;
}
}
......@@ -2488,9 +2795,10 @@ class basic_json
/// comparison: equal
inline bool operator==(const const_iterator& other) const
{
if (m_object != other.m_object or m_object->m_type != other.m_object->m_type)
// if objects are not the same, the comparison is undefined
if (m_object != other.m_object)
{
return false;
throw std::domain_error("cannot compare iterators of different containers");
}
switch (m_object->m_type)
......@@ -2518,6 +2826,157 @@ class basic_json
return not operator==(other);
}
/// comparison: smaller
inline bool operator<(const const_iterator& other) const
{
// if objects are not the same, the comparison is undefined
if (m_object != other.m_object)
{
throw std::domain_error("cannot compare iterators of different containers");
}
switch (m_object->m_type)
{
case (basic_json::value_t::object):
{
throw std::domain_error("cannot use operator< for object iterators");
}
case (basic_json::value_t::array):
{
return (m_it.array_iterator < other.m_it.array_iterator);
}
default:
{
return (m_it.generic_iterator < other.m_it.generic_iterator);
}
}
}
/// comparison: less than or equal
inline bool operator<=(const const_iterator& other) const
{
return not other.operator < (*this);
}
/// comparison: greater than
inline bool operator>(const const_iterator& other) const
{
return not operator<=(other);
}
/// comparison: greater than or equal
inline bool operator>=(const const_iterator& other) const
{
return not operator<(other);
}
/// add to iterator
inline const_iterator& operator+=(difference_type i)
{
switch (m_object->m_type)
{
case (basic_json::value_t::object):
{
throw std::domain_error("cannot use operator+= for object iterators");
break;
}
case (basic_json::value_t::array):
{
m_it.array_iterator += i;
break;
}
default:
{
m_it.generic_iterator += i;
break;
}
}
return *this;
}
/// subtract from iterator
inline const_iterator& operator-=(difference_type i)
{
return operator+=(-i);
}
/// add to iterator
inline const_iterator operator+(difference_type i)
{
auto result = *this;
result += i;
return result;
}
/// subtract from iterator
inline const_iterator operator-(difference_type i)
{
auto result = *this;
result -= i;
return result;
}
/// return difference
inline difference_type operator-(const const_iterator& other) const
{
switch (m_object->m_type)
{
case (basic_json::value_t::object):
{
throw std::domain_error("cannot use operator- for object iterators");
}
case (basic_json::value_t::array):
{
return m_it.array_iterator - other.m_it.array_iterator;
}
default:
{
return m_it.generic_iterator - other.m_it.generic_iterator;
}
}
}
/// access to successor
inline reference operator[](difference_type n) const
{
switch (m_object->m_type)
{
case (basic_json::value_t::object):
{
throw std::domain_error("cannot use operator[] for object iterators");
}
case (basic_json::value_t::array):
{
return *(m_it.array_iterator + n);
}
case (basic_json::value_t::null):
{
throw std::out_of_range("cannot get value");
}
default:
{
if (m_it.generic_iterator == -n)
{
return *m_object;
}
else
{
throw std::out_of_range("cannot get value");
}
}
}
}
private:
/// associated JSON instance
pointer m_object = nullptr;
......@@ -2577,7 +3036,8 @@ class basic_json
/*!
@brief create a string from a Unicode code point
@param codepoint the code point (must be in [0x0, 0x10ffff]
@param codepoint1 the code point (can be high surrogate)
@param codepoint2 the code point (can be low surrogate or 0)
@return string representation of the code point
@exception std::out_of_range if code point is >0x10ffff
@exception std::invalid_argument if the low surrogate is invalid
......@@ -3581,6 +4041,9 @@ basic_json_parser_59:
const lexer_char_t* m_limit = nullptr;
};
/*!
@brief syntax analysis
*/
class parser
{
public:
......@@ -3816,7 +4279,10 @@ using json = basic_json<>;
// specialization of std::swap, and std::hash
namespace std
{
/// swaps the values of two JSON objects
/*!
@brief exchanges the values of two JSON objects
@ingroup container
*/
template <>
inline void swap(nlohmann::json& j1,
nlohmann::json& j2) noexcept(
......
......@@ -7,6 +7,26 @@
@see https://github.com/nlohmann/json
*/
/*!
@defgroup container Container
@brief C++ Container concept
A Container is an object used to store other objects and taking care of the
management of the memory used by the objects it contains.
@see http://en.cppreference.com/w/cpp/concept/Container
@defgroup reversiblecontainer Reversible Container
@brief C++ Reversible Container concept
@ingroup container
A ReversibleContainer is a Container that has iterators that meet the
requirements of either BidirectionalIterator or RandomAccessIterator. Such
iterators allow a ReversibleContainer to be iterated over in reverse.
@see http://en.cppreference.com/w/cpp/concept/ReversibleContainer
*/
#ifndef _NLOHMANN_JSON
#define _NLOHMANN_JSON
......@@ -27,6 +47,7 @@
#include <vector>
/*!
@brief namespace for Niels Lohmann
@see https://github.com/nlohmann
*/
namespace nlohmann
......@@ -75,30 +96,76 @@ class basic_json
class iterator;
class const_iterator;
/// the type of elements in a basic_json container
/*!
@brief the type of elements in a basic_json container
@ingroup container
*/
using value_type = basic_json;
/// the type of an element reference
/*!
@brief the type of an element reference
@ingroup container
*/
using reference = basic_json&;
/// the type of an element const reference
/*!
@brief the type of an element const reference
@ingroup container
*/
using const_reference = const basic_json&;
/*!
@brief a type to represent differences between iterators
@ingroup container
*/
using difference_type = std::ptrdiff_t;
/*!
@brief a type to represent container sizes
@ingroup container
*/
using size_type = std::size_t;
/// the allocator type
using allocator_type = Allocator<basic_json>;
/// the type of an element pointer
using pointer = basic_json*;
/// the type of an element const pointer
using const_pointer = const basic_json*;
/// a type to represent differences between iterators
using difference_type = std::ptrdiff_t;
/// a type to represent container sizes
using size_type = std::size_t;
/// an iterator for a basic_json container
/*!
@brief an iterator for a basic_json container
@ingroup container
*/
using iterator = basic_json::iterator;
/// a const iterator for a basic_json container
/*!
@brief a const iterator for a basic_json container
@ingroup container
*/
using const_iterator = basic_json::const_iterator;
/// a reverse iterator for a basic_json container
/*!
@brief a reverse iterator for a basic_json container
@ingroup reversiblecontainer
*/
using reverse_iterator = std::reverse_iterator<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>;
/// returns the allocator associated with the container
inline allocator_type get_allocator() const
{
return allocator_type();
}
///////////////////////////
// JSON value data types //
///////////////////////////
......@@ -166,6 +233,85 @@ class basic_json
number_float ///< number value (floating-point)
};
/*!
@brief comparison operator for JSON value types
Returns an ordering that is similar to Python:
- order: null < boolean < number < object < array < string
- furthermore, each type is not smaller than itself
*/
friend bool operator<(const value_t lhs, const value_t rhs)
{
// no type is smaller than itself
if (lhs == rhs)
{
return false;
}
switch (lhs)
{
case (value_t::null):
{
// nulls are smaller than all other types
return true;
}
case (value_t::boolean):
{
// only nulls are smaller than booleans
return (rhs != value_t::null);
}
case (value_t::number_float):
case (value_t::number_integer):
{
switch (rhs)
{
// numbers are smaller than objects, arrays, and string
case (value_t::object):
case (value_t::array):
case (value_t::string):
{
return true;
}
default:
{
return false;
}
}
}
case (value_t::object):
{
switch (rhs)
{
// objects are smaller than arrays and string
case (value_t::array):
case (value_t::string):
{
return true;
}
default:
{
return false;
}
}
}
case (value_t::array):
{
// arrays are smaller than strings
return (rhs == value_t::string);
}
default:
{
// a string is not smaller than any other types
return false;
}
}
}
//////////////////
// constructors //
......@@ -226,10 +372,11 @@ class basic_json
}
}
/// create a null object (implicitly)
inline basic_json() noexcept
: m_type(value_t::null)
{}
/*!
@brief create a null object (implicitly)
@ingroup container
*/
inline basic_json() noexcept = default;
/// create a null object (explicitly)
inline basic_json(std::nullptr_t) noexcept
......@@ -424,11 +571,15 @@ class basic_json
return basic_json(l, false, value_t::object);
}
///////////////////////////////////////
// other constructors and destructor //
///////////////////////////////////////
/// copy constructor
/*!
@brief copy constructor
@ingroup container
*/
inline basic_json(const basic_json& other)
: m_type(other.m_type)
{
......@@ -487,7 +638,10 @@ class basic_json
other.m_value = {};
}
/// copy assignment
/*!
@brief copy assignment
@ingroup container
*/
inline reference& operator=(basic_json other) noexcept (
std::is_nothrow_move_constructible<value_t>::value and
std::is_nothrow_move_assignable<value_t>::value and
......@@ -500,7 +654,10 @@ class basic_json
return *this;
}
/// destructor
/*!
@brief destructor
@ingroup container
*/
inline ~basic_json() noexcept
{
switch (m_type)
......@@ -577,6 +734,42 @@ class basic_json
return m_type;
}
// return whether value is null
inline bool is_null() const noexcept
{
return m_type == value_t::null;
}
// return whether value is boolean
inline bool is_boolean() const noexcept
{
return m_type == value_t::boolean;
}
// return whether value is number
inline bool is_number() const noexcept
{
return (m_type == value_t::number_integer) or (m_type == value_t::number_float);
}
// return whether value is object
inline bool is_object() const noexcept
{
return m_type == value_t::object;
}
// return whether value is array
inline bool is_array() const noexcept
{
return m_type == value_t::array;
}
// return whether value is string
inline bool is_string() const noexcept
{
return m_type == value_t::string;
}
/// return the type of the object (implicit)
operator value_t() const noexcept
{
......@@ -871,7 +1064,10 @@ class basic_json
// iterators //
///////////////
/// returns an iterator to the beginning of the container
/*!
@brief returns an iterator to the first element
@ingroup container
*/
inline iterator begin() noexcept
{
iterator result(this);
......@@ -879,7 +1075,10 @@ class basic_json
return result;
}
/// returns a const iterator to the beginning of the container
/*!
@brief returns a const iterator to the first element
@ingroup container
*/
inline const_iterator begin() const noexcept
{
const_iterator result(this);
......@@ -887,7 +1086,10 @@ class basic_json
return result;
}
/// returns a const iterator to the beginning of the container
/*!
@brief returns a const iterator to the first element
@ingroup container
*/
inline const_iterator cbegin() const noexcept
{
const_iterator result(this);
......@@ -895,7 +1097,10 @@ class basic_json
return result;
}
/// returns an iterator to the end of the container
/*!
@brief returns an iterator to one past the last element
@ingroup container
*/
inline iterator end() noexcept
{
iterator result(this);
......@@ -903,7 +1108,10 @@ class basic_json
return result;
}
/// returns a const iterator to the end of the container
/*!
@brief returns a const iterator to one past the last element
@ingroup container
*/
inline const_iterator end() const noexcept
{
const_iterator result(this);
......@@ -911,7 +1119,10 @@ class basic_json
return result;
}
/// returns a const iterator to the end of the container
/*!
@brief returns a const iterator to one past the last element
@ingroup container
*/
inline const_iterator cend() const noexcept
{
const_iterator result(this);
......@@ -919,37 +1130,55 @@ class basic_json
return result;
}
/// returns a reverse iterator to the beginning
/*!
@brief returns a reverse iterator to the first element
@ingroup reversiblecontainer
*/
inline reverse_iterator rbegin() noexcept
{
return reverse_iterator(end());
}
/// returns a reverse iterator to the beginning
/*!
@brief returns a const reverse iterator to the first element
@ingroup reversiblecontainer
*/
inline const_reverse_iterator rbegin() const noexcept
{
return const_reverse_iterator(end());
}
/// returns a reverse iterator to the end
/*!
@brief returns a reverse iterator to one past the last element
@ingroup reversiblecontainer
*/
inline reverse_iterator rend() noexcept
{
return reverse_iterator(begin());
}
/// returns a reverse iterator to the end
/*!
@brief returns a const reverse iterator to one past the last element
@ingroup reversiblecontainer
*/
inline const_reverse_iterator rend() const noexcept
{
return const_reverse_iterator(begin());
}
/// returns a reverse iterator to the beginning
/*!
@brief returns a const reverse iterator to the first element
@ingroup reversiblecontainer
*/
inline const_reverse_iterator crbegin() const noexcept
{
return const_reverse_iterator(cend());
}
/// returns a reverse iterator to the end
/*!
@brief returns a const reverse iterator to one past the last element
@ingroup reversiblecontainer
*/
inline const_reverse_iterator crend() const noexcept
{
return const_reverse_iterator(cbegin());
......@@ -960,7 +1189,10 @@ class basic_json
// capacity //
//////////////
/// checks whether the container is empty
/*!
@brief checks whether the container is empty
@ingroup container
*/
inline bool empty() const noexcept
{
switch (m_type)
......@@ -988,7 +1220,10 @@ class basic_json
}
}
/// returns the number of elements
/*!
@brief returns the number of elements
@ingroup container
*/
inline size_type size() const noexcept
{
switch (m_type)
......@@ -1016,7 +1251,10 @@ class basic_json
}
}
/// returns the maximum possible number of elements
/*!
@brief returns the maximum possible number of elements
@ingroup container
*/
inline size_type max_size() const noexcept
{
switch (m_type)
......@@ -1186,7 +1424,10 @@ class basic_json
return operator[](value.first);
}
/// swaps the contents
/*!
@brief exchanges the values
@ingroup container
*/
inline void swap(reference other) noexcept (
std::is_nothrow_move_constructible<value_t>::value and
std::is_nothrow_move_assignable<value_t>::value and
......@@ -1242,7 +1483,10 @@ class basic_json
// lexicographical comparison operators //
//////////////////////////////////////////
/// comparison: equal
/*!
@brief comparison: equal
@ingroup container
*/
friend bool operator==(const_reference lhs, const_reference rhs)
{
switch (lhs.type())
......@@ -1316,7 +1560,10 @@ class basic_json
return false;
}
/// comparison: not equal
/*!
@brief comparison: not equal
@ingroup container
*/
friend bool operator!=(const_reference lhs, const_reference rhs)
{
return not (lhs == rhs);
......@@ -1393,7 +1640,9 @@ class basic_json
}
}
return false;
// We only reach this line if we cannot compare values. In that case,
// we compare types.
return lhs.type() < rhs.type();
}
/// comparison: less than or equal
......@@ -1743,19 +1992,6 @@ class basic_json
// iterators //
///////////////
/// values of a generic iterator type of non-container JSON values
enum class generic_iterator_value
{
/// the iterator was not initialized
uninitialized,
/// the iterator points to the only value
begin,
/// the iterator points past the only value
end,
/// the iterator points to an invalid value
invalid
};
/// an iterator value
template<typename array_iterator_t, typename object_iterator_t>
union internal_iterator
......@@ -1765,21 +2001,15 @@ class basic_json
/// iterator for JSON arrays
array_iterator_t array_iterator;
/// generic iteraotr for all other value types
generic_iterator_value generic_iterator;
difference_type generic_iterator;
/// default constructor
internal_iterator() : generic_iterator(generic_iterator_value::uninitialized) {}
/// constructor for object iterators
internal_iterator(object_iterator_t v) : object_iterator(v) {}
/// constructor for array iterators
internal_iterator(array_iterator_t v) : array_iterator(v) {}
/// constructor for generic iterators
internal_iterator(generic_iterator_value v) : generic_iterator(v) {}
internal_iterator() : generic_iterator(-1) {}
};
public:
/// a bidirectional iterator for the basic_json class
class iterator : public std::iterator<std::bidirectional_iterator_tag, basic_json>
/// a random access iterator for the basic_json class
class iterator : public std::iterator<std::random_access_iterator_tag, basic_json>
{
public:
/// the type of the values when the iterator is dereferenced
......@@ -1813,7 +2043,7 @@ class basic_json
}
default:
{
m_it.generic_iterator = generic_iterator_value::uninitialized;
m_it.generic_iterator = -1;
break;
}
}
......@@ -1847,13 +2077,13 @@ class basic_json
case (basic_json::value_t::null):
{
// set to end so begin()==end() is true: null is empty
m_it.generic_iterator = generic_iterator_value::end;
m_it.generic_iterator = 1;
break;
}
default:
{
m_it.generic_iterator = generic_iterator_value::begin;
m_it.generic_iterator = 0;
break;
}
}
......@@ -1878,14 +2108,14 @@ class basic_json
default:
{
m_it.generic_iterator = generic_iterator_value::end;
m_it.generic_iterator = 1;
break;
}
}
}
/// return a reference to the value pointed to by the iterator
inline reference operator*() const
inline reference operator*()
{
switch (m_object->m_type)
{
......@@ -1906,7 +2136,7 @@ class basic_json
default:
{
if (m_it.generic_iterator == generic_iterator_value::begin)
if (m_it.generic_iterator == 0)
{
return *m_object;
}
......@@ -1919,7 +2149,7 @@ class basic_json
}
/// dereference the iterator
inline pointer operator->() const
inline pointer operator->()
{
switch (m_object->m_type)
{
......@@ -1933,9 +2163,14 @@ class basic_json
return &*m_it.array_iterator;
}
case (basic_json::value_t::null):
{
throw std::out_of_range("cannot get value");
}
default:
{
if (m_it.generic_iterator == generic_iterator_value::begin)
if (m_it.generic_iterator == 0)
{
return m_object;
}
......@@ -1950,7 +2185,7 @@ class basic_json
/// post-increment (it++)
inline iterator operator++(int)
{
iterator result = *this;
auto result = *this;
switch (m_object->m_type)
{
......@@ -1968,14 +2203,7 @@ class basic_json
default:
{
if (m_it.generic_iterator == generic_iterator_value::begin)
{
m_it.generic_iterator = generic_iterator_value::end;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
m_it.generic_iterator++;
break;
}
}
......@@ -2002,14 +2230,7 @@ class basic_json
default:
{
if (m_it.generic_iterator == generic_iterator_value::begin)
{
m_it.generic_iterator = generic_iterator_value::end;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
++m_it.generic_iterator;
break;
}
}
......@@ -2020,7 +2241,7 @@ class basic_json
/// post-decrement (it--)
inline iterator operator--(int)
{
iterator result = *this;
auto result = *this;
switch (m_object->m_type)
{
......@@ -2036,22 +2257,9 @@ class basic_json
break;
}
case (basic_json::value_t::null):
{
m_it.generic_iterator = generic_iterator_value::invalid;
break;
}
default:
{
if (m_it.generic_iterator == generic_iterator_value::end)
{
m_it.generic_iterator = generic_iterator_value::begin;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
m_it.generic_iterator--;
break;
}
}
......@@ -2076,22 +2284,9 @@ class basic_json
break;
}
case (basic_json::value_t::null):
{
m_it.generic_iterator = generic_iterator_value::invalid;
break;
}
default:
{
if (m_it.generic_iterator == generic_iterator_value::end)
{
m_it.generic_iterator = generic_iterator_value::begin;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
--m_it.generic_iterator;
break;
}
}
......@@ -2102,9 +2297,10 @@ class basic_json
/// comparison: equal
inline bool operator==(const iterator& other) const
{
if (m_object != other.m_object or m_object->m_type != other.m_object->m_type)
// if objects are not the same, the comparison is undefined
if (m_object != other.m_object)
{
return false;
throw std::domain_error("cannot compare iterators of different containers");
}
switch (m_object->m_type)
......@@ -2132,6 +2328,157 @@ class basic_json
return not operator==(other);
}
/// comparison: smaller
inline bool operator<(const iterator& other) const
{
// if objects are not the same, the comparison is undefined
if (m_object != other.m_object)
{
throw std::domain_error("cannot compare iterators of different containers");
}
switch (m_object->m_type)
{
case (basic_json::value_t::object):
{
throw std::domain_error("cannot use operator< for object iterators");
}
case (basic_json::value_t::array):
{
return (m_it.array_iterator < other.m_it.array_iterator);
}
default:
{
return (m_it.generic_iterator < other.m_it.generic_iterator);
}
}
}
/// comparison: less than or equal
inline bool operator<=(const iterator& other) const
{
return not other.operator < (*this);
}
/// comparison: greater than
inline bool operator>(const iterator& other) const
{
return not operator<=(other);
}
/// comparison: greater than or equal
inline bool operator>=(const iterator& other) const
{
return not operator<(other);
}
/// add to iterator
inline iterator& operator+=(difference_type i)
{
switch (m_object->m_type)
{
case (basic_json::value_t::object):
{
throw std::domain_error("cannot use operator+= for object iterators");
}
case (basic_json::value_t::array):
{
m_it.array_iterator += i;
break;
}
default:
{
m_it.generic_iterator += i;
break;
}
}
return *this;
}
/// subtract from iterator
inline iterator& operator-=(difference_type i)
{
return operator+=(-i);
}
/// add to iterator
inline iterator operator+(difference_type i)
{
auto result = *this;
result += i;
return result;
}
/// subtract from iterator
inline iterator operator-(difference_type i)
{
auto result = *this;
result -= i;
return result;
}
/// return difference
inline difference_type operator-(const iterator& other) const
{
switch (m_object->m_type)
{
case (basic_json::value_t::object):
{
throw std::domain_error("cannot use operator- for object iterators");
return 0;
}
case (basic_json::value_t::array):
{
return m_it.array_iterator - other.m_it.array_iterator;
}
default:
{
return m_it.generic_iterator - other.m_it.generic_iterator;
}
}
}
/// access to successor
inline reference operator[](difference_type n)
{
switch (m_object->m_type)
{
case (basic_json::value_t::object):
{
throw std::domain_error("cannot use operator[] for object iterators");
}
case (basic_json::value_t::array):
{
return *(m_it.array_iterator + n);
}
case (basic_json::value_t::null):
{
throw std::out_of_range("cannot get value");
}
default:
{
if (m_it.generic_iterator == -n)
{
return *m_object;
}
else
{
throw std::out_of_range("cannot get value");
}
}
}
}
private:
/// associated JSON instance
pointer m_object = nullptr;
......@@ -2139,8 +2486,8 @@ class basic_json
internal_iterator<typename array_t::iterator, typename object_t::iterator> m_it;
};
/// a const bidirectional iterator for the basic_json class
class const_iterator : public std::iterator<std::bidirectional_iterator_tag, const basic_json>
/// a const random access iterator for the basic_json class
class const_iterator : public std::iterator<std::random_access_iterator_tag, const basic_json>
{
public:
/// the type of the values when the iterator is dereferenced
......@@ -2174,7 +2521,7 @@ class basic_json
}
default:
{
m_it.generic_iterator = generic_iterator_value::uninitialized;
m_it.generic_iterator = -1;
break;
}
}
......@@ -2233,13 +2580,13 @@ class basic_json
case (basic_json::value_t::null):
{
// set to end so begin()==end() is true: null is empty
m_it.generic_iterator = generic_iterator_value::end;
m_it.generic_iterator = 1;
break;
}
default:
{
m_it.generic_iterator = generic_iterator_value::begin;
m_it.generic_iterator = 0;
break;
}
}
......@@ -2264,7 +2611,7 @@ class basic_json
default:
{
m_it.generic_iterator = generic_iterator_value::end;
m_it.generic_iterator = 1;
break;
}
}
......@@ -2292,7 +2639,7 @@ class basic_json
default:
{
if (m_it.generic_iterator == generic_iterator_value::begin)
if (m_it.generic_iterator == 0)
{
return *m_object;
}
......@@ -2321,7 +2668,7 @@ class basic_json
default:
{
if (m_it.generic_iterator == generic_iterator_value::begin)
if (m_it.generic_iterator == 0)
{
return m_object;
}
......@@ -2336,7 +2683,7 @@ class basic_json
/// post-increment (it++)
inline const_iterator operator++(int)
{
const_iterator result = *this;
auto result = *this;
switch (m_object->m_type)
{
......@@ -2354,14 +2701,7 @@ class basic_json
default:
{
if (m_it.generic_iterator == generic_iterator_value::begin)
{
m_it.generic_iterator = generic_iterator_value::end;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
m_it.generic_iterator++;
break;
}
}
......@@ -2388,14 +2728,7 @@ class basic_json
default:
{
if (m_it.generic_iterator == generic_iterator_value::begin)
{
m_it.generic_iterator = generic_iterator_value::end;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
++m_it.generic_iterator;
break;
}
}
......@@ -2406,7 +2739,7 @@ class basic_json
/// post-decrement (it--)
inline const_iterator operator--(int)
{
const_iterator result = *this;
auto result = *this;
switch (m_object->m_type)
{
......@@ -2422,22 +2755,9 @@ class basic_json
break;
}
case (basic_json::value_t::null):
{
m_it.generic_iterator = generic_iterator_value::invalid;
break;
}
default:
{
if (m_it.generic_iterator == generic_iterator_value::end)
{
m_it.generic_iterator = generic_iterator_value::begin;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
m_it.generic_iterator--;
break;
}
}
......@@ -2462,22 +2782,9 @@ class basic_json
break;
}
case (basic_json::value_t::null):
{
m_it.generic_iterator = generic_iterator_value::invalid;
break;
}
default:
{
if (m_it.generic_iterator == generic_iterator_value::end)
{
m_it.generic_iterator = generic_iterator_value::begin;
}
else
{
m_it.generic_iterator = generic_iterator_value::invalid;
}
--m_it.generic_iterator;
break;
}
}
......@@ -2488,9 +2795,10 @@ class basic_json
/// comparison: equal
inline bool operator==(const const_iterator& other) const
{
if (m_object != other.m_object or m_object->m_type != other.m_object->m_type)
// if objects are not the same, the comparison is undefined
if (m_object != other.m_object)
{
return false;
throw std::domain_error("cannot compare iterators of different containers");
}
switch (m_object->m_type)
......@@ -2518,6 +2826,157 @@ class basic_json
return not operator==(other);
}
/// comparison: smaller
inline bool operator<(const const_iterator& other) const
{
// if objects are not the same, the comparison is undefined
if (m_object != other.m_object)
{
throw std::domain_error("cannot compare iterators of different containers");
}
switch (m_object->m_type)
{
case (basic_json::value_t::object):
{
throw std::domain_error("cannot use operator< for object iterators");
}
case (basic_json::value_t::array):
{
return (m_it.array_iterator < other.m_it.array_iterator);
}
default:
{
return (m_it.generic_iterator < other.m_it.generic_iterator);
}
}
}
/// comparison: less than or equal
inline bool operator<=(const const_iterator& other) const
{
return not other.operator < (*this);
}
/// comparison: greater than
inline bool operator>(const const_iterator& other) const
{
return not operator<=(other);
}
/// comparison: greater than or equal
inline bool operator>=(const const_iterator& other) const
{
return not operator<(other);
}
/// add to iterator
inline const_iterator& operator+=(difference_type i)
{
switch (m_object->m_type)
{
case (basic_json::value_t::object):
{
throw std::domain_error("cannot use operator+= for object iterators");
break;
}
case (basic_json::value_t::array):
{
m_it.array_iterator += i;
break;
}
default:
{
m_it.generic_iterator += i;
break;
}
}
return *this;
}
/// subtract from iterator
inline const_iterator& operator-=(difference_type i)
{
return operator+=(-i);
}
/// add to iterator
inline const_iterator operator+(difference_type i)
{
auto result = *this;
result += i;
return result;
}
/// subtract from iterator
inline const_iterator operator-(difference_type i)
{
auto result = *this;
result -= i;
return result;
}
/// return difference
inline difference_type operator-(const const_iterator& other) const
{
switch (m_object->m_type)
{
case (basic_json::value_t::object):
{
throw std::domain_error("cannot use operator- for object iterators");
}
case (basic_json::value_t::array):
{
return m_it.array_iterator - other.m_it.array_iterator;
}
default:
{
return m_it.generic_iterator - other.m_it.generic_iterator;
}
}
}
/// access to successor
inline reference operator[](difference_type n) const
{
switch (m_object->m_type)
{
case (basic_json::value_t::object):
{
throw std::domain_error("cannot use operator[] for object iterators");
}
case (basic_json::value_t::array):
{
return *(m_it.array_iterator + n);
}
case (basic_json::value_t::null):
{
throw std::out_of_range("cannot get value");
}
default:
{
if (m_it.generic_iterator == -n)
{
return *m_object;
}
else
{
throw std::out_of_range("cannot get value");
}
}
}
}
private:
/// associated JSON instance
pointer m_object = nullptr;
......@@ -2577,7 +3036,8 @@ class basic_json
/*!
@brief create a string from a Unicode code point
@param codepoint the code point (must be in [0x0, 0x10ffff]
@param codepoint1 the code point (can be high surrogate)
@param codepoint2 the code point (can be low surrogate or 0)
@return string representation of the code point
@exception std::out_of_range if code point is >0x10ffff
@exception std::invalid_argument if the low surrogate is invalid
......@@ -2930,6 +3390,9 @@ class basic_json
const lexer_char_t* m_limit = nullptr;
};
/*!
@brief syntax analysis
*/
class parser
{
public:
......@@ -3165,7 +3628,10 @@ using json = basic_json<>;
// specialization of std::swap, and std::hash
namespace std
{
/// swaps the values of two JSON objects
/*!
@brief exchanges the values of two JSON objects
@ingroup container
*/
template <>
inline void swap(nlohmann::json& j1,
nlohmann::json& j2) noexcept(
......
......@@ -1050,6 +1050,86 @@ TEST_CASE("other constructors and destructor")
TEST_CASE("object inspection")
{
SECTION("convenience type checker")
{
SECTION("object")
{
json j {{"foo", 1}, {"bar", false}};
CHECK(not j.is_null());
CHECK(not j.is_boolean());
CHECK(not j.is_number());
CHECK(j.is_object());
CHECK(not j.is_array());
CHECK(not j.is_string());
}
SECTION("array")
{
json j {"foo", 1, 42.23, false};
CHECK(not j.is_null());
CHECK(not j.is_boolean());
CHECK(not j.is_number());
CHECK(not j.is_object());
CHECK(j.is_array());
CHECK(not j.is_string());
}
SECTION("null")
{
json j(nullptr);
CHECK(j.is_null());
CHECK(not j.is_boolean());
CHECK(not j.is_number());
CHECK(not j.is_object());
CHECK(not j.is_array());
CHECK(not j.is_string());
}
SECTION("boolean")
{
json j(true);
CHECK(not j.is_null());
CHECK(j.is_boolean());
CHECK(not j.is_number());
CHECK(not j.is_object());
CHECK(not j.is_array());
CHECK(not j.is_string());
}
SECTION("string")
{
json j("Hello world");
CHECK(not j.is_null());
CHECK(not j.is_boolean());
CHECK(not j.is_number());
CHECK(not j.is_object());
CHECK(not j.is_array());
CHECK(j.is_string());
}
SECTION("number (integer)")
{
json j(42);
CHECK(not j.is_null());
CHECK(not j.is_boolean());
CHECK(j.is_number());
CHECK(not j.is_object());
CHECK(not j.is_array());
CHECK(not j.is_string());
}
SECTION("number (floating-point)")
{
json j(42.23);
CHECK(not j.is_null());
CHECK(not j.is_boolean());
CHECK(j.is_number());
CHECK(not j.is_object());
CHECK(not j.is_array());
CHECK(not j.is_string());
}
}
SECTION("serialization")
{
json j {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} };
......@@ -2366,6 +2446,8 @@ TEST_CASE("element access")
TEST_CASE("iterators")
{
SECTION("basic behavior")
{
SECTION("uninitialized")
{
json::iterator it;
......@@ -3497,6 +3579,370 @@ TEST_CASE("iterators")
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")
......@@ -4356,6 +4802,47 @@ TEST_CASE("modifiers")
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 =
{
nullptr, nullptr,
......@@ -4413,20 +4900,20 @@ TEST_CASE("lexicographical comparison operators")
{
std::vector<std::vector<bool>> expected =
{
{false, false, false, false, false, false, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false},
{false, false, false, true, false, true, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false},
{false, false, true, true, false, true, false, false, false, false, false, false, false, false},
{false, false, false, true, false, false, false, false, false, false, false, false, false, false},
{false, false, true, true, true, true, true, true, true, true, true, true, true, true},
{false, false, true, true, true, true, true, true, true, true, true, true, true, true},
{false, false, false, true, false, true, true, true, false, false, true, true, true, true},
{false, false, false, false, false, false, true, true, false, false, true, true, true, true},
{false, false, true, true, false, true, true, true, false, false, true, true, true, true},
{false, false, false, true, false, false, true, true, false, false, true, true, true, true},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, true, false, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, true, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false, false, false, true, false}
{false, false, true, true, true, true, true, true, false, false, true, true, true, true},
{false, false, true, true, true, true, true, true, true, false, true, true, true, true},
{false, false, false, false, false, false, true, true, false, false, false, true, false, false},
{false, false, false, false, false, false, true, true, false, false, false, false, false, false},
{false, false, false, false, false, false, true, true, false, false, true, true, false, false},
{false, false, false, false, false, false, true, true, false, false, true, true, true, false}
};
for (size_t i = 0; i < j_values.size(); ++i)
......@@ -4474,6 +4961,7 @@ TEST_CASE("lexicographical comparison operators")
}
}
}
}
}
TEST_CASE("serialization")
......@@ -4721,20 +5209,20 @@ TEST_CASE("iterator class")
{
json j(json::value_t::null);
json::iterator it = j.begin();
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
CHECK(it.m_it.generic_iterator == 1);
it++;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
}
SECTION("number")
{
json j(17);
json::iterator it = j.begin();
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin);
CHECK(it.m_it.generic_iterator == 0);
it++;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
CHECK(it.m_it.generic_iterator == 1);
it++;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
}
SECTION("object")
......@@ -4772,20 +5260,20 @@ TEST_CASE("iterator class")
{
json j(json::value_t::null);
json::iterator it = j.begin();
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
CHECK(it.m_it.generic_iterator == 1);
++it;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
}
SECTION("number")
{
json j(17);
json::iterator it = j.begin();
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin);
CHECK(it.m_it.generic_iterator == 0);
++it;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
CHECK(it.m_it.generic_iterator == 1);
++it;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
}
SECTION("object")
......@@ -4823,20 +5311,18 @@ TEST_CASE("iterator class")
{
json j(json::value_t::null);
json::iterator it = j.end();
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
it--;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
CHECK(it.m_it.generic_iterator == 1);
}
SECTION("number")
{
json j(17);
json::iterator it = j.end();
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
CHECK(it.m_it.generic_iterator == 1);
it--;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin);
CHECK(it.m_it.generic_iterator == 0);
it--;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
}
SECTION("object")
......@@ -4874,20 +5360,18 @@ TEST_CASE("iterator class")
{
json j(json::value_t::null);
json::iterator it = j.end();
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
--it;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
CHECK(it.m_it.generic_iterator == 1);
}
SECTION("number")
{
json j(17);
json::iterator it = j.end();
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
CHECK(it.m_it.generic_iterator == 1);
--it;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin);
CHECK(it.m_it.generic_iterator == 0);
--it;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
}
SECTION("object")
......@@ -4919,63 +5403,6 @@ TEST_CASE("iterator class")
}
}
}
SECTION("comparison")
{
json j_values =
{
nullptr, nullptr,
17, 42,
3.14159, 23.42,
"foo", "bar",
true, false,
{1, 2, 3}, {"one", "two", "three"},
{{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}}
};
SECTION("comparison: equal")
{
std::vector<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")
......@@ -5146,20 +5573,20 @@ TEST_CASE("const_iterator class")
{
json j(json::value_t::null);
json::const_iterator it = j.cbegin();
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
CHECK(it.m_it.generic_iterator == 1);
it++;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
}
SECTION("number")
{
json j(17);
json::const_iterator it = j.cbegin();
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin);
CHECK(it.m_it.generic_iterator == 0);
it++;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
CHECK(it.m_it.generic_iterator == 1);
it++;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
}
SECTION("object")
......@@ -5197,20 +5624,20 @@ TEST_CASE("const_iterator class")
{
json j(json::value_t::null);
json::const_iterator it = j.cbegin();
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
CHECK(it.m_it.generic_iterator == 1);
++it;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
}
SECTION("number")
{
json j(17);
json::const_iterator it = j.cbegin();
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin);
CHECK(it.m_it.generic_iterator == 0);
++it;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
CHECK(it.m_it.generic_iterator == 1);
++it;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
}
SECTION("object")
......@@ -5248,20 +5675,18 @@ TEST_CASE("const_iterator class")
{
json j(json::value_t::null);
json::const_iterator it = j.cend();
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
it--;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
CHECK(it.m_it.generic_iterator == 1);
}
SECTION("number")
{
json j(17);
json::const_iterator it = j.cend();
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
CHECK(it.m_it.generic_iterator == 1);
it--;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin);
CHECK(it.m_it.generic_iterator == 0);
it--;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
}
SECTION("object")
......@@ -5299,20 +5724,18 @@ TEST_CASE("const_iterator class")
{
json j(json::value_t::null);
json::const_iterator it = j.cend();
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
--it;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
CHECK(it.m_it.generic_iterator == 1);
}
SECTION("number")
{
json j(17);
json::const_iterator it = j.cend();
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::end);
CHECK(it.m_it.generic_iterator == 1);
--it;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::begin);
CHECK(it.m_it.generic_iterator == 0);
--it;
CHECK(it.m_it.generic_iterator == json::generic_iterator_value::invalid);
CHECK((it.m_it.generic_iterator != 0 and it.m_it.generic_iterator != 1));
}
SECTION("object")
......@@ -5344,63 +5767,6 @@ TEST_CASE("const_iterator class")
}
}
}
SECTION("comparison")
{
json j_values =
{
nullptr, nullptr,
17, 42,
3.14159, 23.42,
"foo", "bar",
true, false,
{1, 2, 3}, {"one", "two", "three"},
{{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}}
};
SECTION("comparison: equal")
{
std::vector<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")
......@@ -6143,3 +6509,326 @@ TEST_CASE("README", "[hide]")
// etc.
}
}
TEST_CASE("algorithms")
{
json j_array = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz"};
json j_object = {{"one", 1}, {"two", 2}};
SECTION("non-modifying sequence operations")
{
SECTION("std::all_of")
{
CHECK(std::all_of(j_array.begin(), j_array.end(), [](const json & value)
{
return value.size() > 0;
}));
CHECK(std::all_of(j_object.begin(), j_object.end(), [](const json & value)
{
return value.type() == json::value_t::number_integer;
}));
}
SECTION("std::any_of")
{
CHECK(std::any_of(j_array.begin(), j_array.end(), [](const json & value)
{
return value.is_string() and value.get<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