Fix iterators to meet (more) std::ranges requirements
Fixes #3130. Related discussion: #3408
This commit is contained in:
parent
529e4100c5
commit
fe2c4d7777
@ -51,9 +51,12 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
// make sure BasicJsonType is basic_json or const basic_json
|
// make sure BasicJsonType is basic_json or const basic_json
|
||||||
static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
|
static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
|
||||||
"iter_impl only accepts (const) basic_json");
|
"iter_impl only accepts (const) basic_json");
|
||||||
|
// superficial check for the LegacyBidirectionalIterator named requirement
|
||||||
|
static_assert(std::is_base_of<std::bidirectional_iterator_tag, std::bidirectional_iterator_tag>::value
|
||||||
|
&& std::is_base_of<std::bidirectional_iterator_tag, typename array_t::iterator::iterator_category>::value,
|
||||||
|
"basic_json iterator assumes array and object type iterators satisfy the LegacyBidirectionalIterator named requirement.");
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.
|
/// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.
|
||||||
/// The C++ Standard has never required user-defined iterators to derive from std::iterator.
|
/// The C++ Standard has never required user-defined iterators to derive from std::iterator.
|
||||||
/// A user-defined iterator should provide publicly accessible typedefs named
|
/// A user-defined iterator should provide publicly accessible typedefs named
|
||||||
|
|||||||
@ -6,6 +6,10 @@
|
|||||||
#include <tuple> // tuple_size, get, tuple_element
|
#include <tuple> // tuple_size, get, tuple_element
|
||||||
#include <utility> // move
|
#include <utility> // move
|
||||||
|
|
||||||
|
#if JSON_HAS_RANGES
|
||||||
|
#include <ranges> // enable_borrowed_range
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||||
#include <nlohmann/detail/value_t.hpp>
|
#include <nlohmann/detail/value_t.hpp>
|
||||||
|
|
||||||
@ -25,14 +29,14 @@ template<typename IteratorType> class iteration_proxy_value
|
|||||||
public:
|
public:
|
||||||
using difference_type = std::ptrdiff_t;
|
using difference_type = std::ptrdiff_t;
|
||||||
using value_type = iteration_proxy_value;
|
using value_type = iteration_proxy_value;
|
||||||
using pointer = value_type * ;
|
using pointer = value_type *;
|
||||||
using reference = value_type & ;
|
using reference = value_type &;
|
||||||
using iterator_category = std::input_iterator_tag;
|
using iterator_category = std::input_iterator_tag;
|
||||||
using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;
|
using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// the iterator
|
/// the iterator
|
||||||
IteratorType anchor;
|
IteratorType anchor{};
|
||||||
/// an index for arrays (used to create key names)
|
/// an index for arrays (used to create key names)
|
||||||
std::size_t array_index = 0;
|
std::size_t array_index = 0;
|
||||||
/// last stringified array index
|
/// last stringified array index
|
||||||
@ -40,15 +44,30 @@ template<typename IteratorType> class iteration_proxy_value
|
|||||||
/// a string representation of the array index
|
/// a string representation of the array index
|
||||||
mutable string_type array_index_str = "0";
|
mutable string_type array_index_str = "0";
|
||||||
/// an empty string (to return a reference for primitive values)
|
/// an empty string (to return a reference for primitive values)
|
||||||
const string_type empty_str{};
|
string_type empty_str{};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit iteration_proxy_value(IteratorType it) noexcept
|
explicit iteration_proxy_value() = default;
|
||||||
|
explicit iteration_proxy_value(IteratorType it, std::size_t array_index_ = 0)
|
||||||
|
noexcept(std::is_nothrow_move_constructible<IteratorType>::value
|
||||||
|
&& std::is_nothrow_default_constructible<string_type>::value)
|
||||||
: anchor(std::move(it))
|
: anchor(std::move(it))
|
||||||
|
, array_index(array_index_)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
iteration_proxy_value(iteration_proxy_value const&) = default;
|
||||||
|
iteration_proxy_value& operator=(iteration_proxy_value const&) = default;
|
||||||
|
// older GCCs are a bit fussy and require explicit noexcept specifiers on defaulted functions
|
||||||
|
iteration_proxy_value(iteration_proxy_value&&)
|
||||||
|
noexcept(std::is_nothrow_move_constructible<IteratorType>::value
|
||||||
|
&& std::is_nothrow_move_constructible<string_type>::value) = default;
|
||||||
|
iteration_proxy_value& operator=(iteration_proxy_value&&)
|
||||||
|
noexcept(std::is_nothrow_move_assignable<IteratorType>::value
|
||||||
|
&& std::is_nothrow_move_assignable<string_type>::value) = default;
|
||||||
|
~iteration_proxy_value() = default;
|
||||||
|
|
||||||
/// dereference operator (needed for range-based for)
|
/// dereference operator (needed for range-based for)
|
||||||
iteration_proxy_value& operator*()
|
const iteration_proxy_value& operator*() const
|
||||||
{
|
{
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@ -62,6 +81,14 @@ template<typename IteratorType> class iteration_proxy_value
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iteration_proxy_value operator++(int)& // NOLINT(cert-dcl21-cpp)
|
||||||
|
{
|
||||||
|
auto tmp = iteration_proxy_value(anchor, array_index);
|
||||||
|
++anchor;
|
||||||
|
++array_index;
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
/// equality operator (needed for InputIterator)
|
/// equality operator (needed for InputIterator)
|
||||||
bool operator==(const iteration_proxy_value& o) const
|
bool operator==(const iteration_proxy_value& o) const
|
||||||
{
|
{
|
||||||
@ -122,25 +149,34 @@ template<typename IteratorType> class iteration_proxy
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
/// the container to iterate
|
/// the container to iterate
|
||||||
typename IteratorType::reference container;
|
typename IteratorType::pointer container = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
explicit iteration_proxy() = default;
|
||||||
|
|
||||||
/// construct iteration proxy from a container
|
/// construct iteration proxy from a container
|
||||||
explicit iteration_proxy(typename IteratorType::reference cont) noexcept
|
explicit iteration_proxy(typename IteratorType::reference cont) noexcept
|
||||||
: container(cont) {}
|
: container(&cont) {}
|
||||||
|
|
||||||
|
iteration_proxy(iteration_proxy const&) = default;
|
||||||
|
iteration_proxy& operator=(iteration_proxy const&) = default;
|
||||||
|
iteration_proxy(iteration_proxy&&) noexcept = default;
|
||||||
|
iteration_proxy& operator=(iteration_proxy&&) noexcept = default;
|
||||||
|
~iteration_proxy() = default;
|
||||||
|
|
||||||
/// return iterator begin (needed for range-based for)
|
/// return iterator begin (needed for range-based for)
|
||||||
iteration_proxy_value<IteratorType> begin() noexcept
|
iteration_proxy_value<IteratorType> begin() const noexcept
|
||||||
{
|
{
|
||||||
return iteration_proxy_value<IteratorType>(container.begin());
|
return iteration_proxy_value<IteratorType>(container->begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// return iterator end (needed for range-based for)
|
/// return iterator end (needed for range-based for)
|
||||||
iteration_proxy_value<IteratorType> end() noexcept
|
iteration_proxy_value<IteratorType> end() const noexcept
|
||||||
{
|
{
|
||||||
return iteration_proxy_value<IteratorType>(container.end());
|
return iteration_proxy_value<IteratorType>(container->end());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Structured Bindings Support
|
// Structured Bindings Support
|
||||||
// For further reference see https://blog.tartanllama.xyz/structured-bindings/
|
// For further reference see https://blog.tartanllama.xyz/structured-bindings/
|
||||||
// And see https://github.com/nlohmann/json/pull/1391
|
// And see https://github.com/nlohmann/json/pull/1391
|
||||||
@ -187,3 +223,8 @@ class tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >>
|
|||||||
#pragma clang diagnostic pop
|
#pragma clang diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
} // namespace std
|
} // namespace std
|
||||||
|
|
||||||
|
#if JSON_HAS_RANGES
|
||||||
|
template <typename IteratorType>
|
||||||
|
inline constexpr bool ::std::ranges::enable_borrowed_range<::nlohmann::detail::iteration_proxy<IteratorType>> = true;
|
||||||
|
#endif
|
||||||
|
|||||||
@ -118,13 +118,11 @@
|
|||||||
#define JSON_HAS_RANGES 0
|
#define JSON_HAS_RANGES 0
|
||||||
#elif defined(__cpp_lib_ranges)
|
#elif defined(__cpp_lib_ranges)
|
||||||
#define JSON_HAS_RANGES 1
|
#define JSON_HAS_RANGES 1
|
||||||
|
#else
|
||||||
|
#define JSON_HAS_RANGES 0
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef JSON_HAS_RANGES
|
|
||||||
#define JSON_HAS_RANGES 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address)
|
#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address)
|
||||||
#define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]]
|
#define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]]
|
||||||
#else
|
#else
|
||||||
|
|||||||
@ -2345,13 +2345,11 @@ using is_detected_convertible =
|
|||||||
#define JSON_HAS_RANGES 0
|
#define JSON_HAS_RANGES 0
|
||||||
#elif defined(__cpp_lib_ranges)
|
#elif defined(__cpp_lib_ranges)
|
||||||
#define JSON_HAS_RANGES 1
|
#define JSON_HAS_RANGES 1
|
||||||
|
#else
|
||||||
|
#define JSON_HAS_RANGES 0
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef JSON_HAS_RANGES
|
|
||||||
#define JSON_HAS_RANGES 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address)
|
#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address)
|
||||||
#define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]]
|
#define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]]
|
||||||
#else
|
#else
|
||||||
@ -4660,6 +4658,10 @@ constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::va
|
|||||||
#include <tuple> // tuple_size, get, tuple_element
|
#include <tuple> // tuple_size, get, tuple_element
|
||||||
#include <utility> // move
|
#include <utility> // move
|
||||||
|
|
||||||
|
#if JSON_HAS_RANGES
|
||||||
|
#include <ranges> // enable_borrowed_range
|
||||||
|
#endif
|
||||||
|
|
||||||
// #include <nlohmann/detail/meta/type_traits.hpp>
|
// #include <nlohmann/detail/meta/type_traits.hpp>
|
||||||
|
|
||||||
// #include <nlohmann/detail/value_t.hpp>
|
// #include <nlohmann/detail/value_t.hpp>
|
||||||
@ -4681,14 +4683,14 @@ template<typename IteratorType> class iteration_proxy_value
|
|||||||
public:
|
public:
|
||||||
using difference_type = std::ptrdiff_t;
|
using difference_type = std::ptrdiff_t;
|
||||||
using value_type = iteration_proxy_value;
|
using value_type = iteration_proxy_value;
|
||||||
using pointer = value_type * ;
|
using pointer = value_type *;
|
||||||
using reference = value_type & ;
|
using reference = value_type &;
|
||||||
using iterator_category = std::input_iterator_tag;
|
using iterator_category = std::input_iterator_tag;
|
||||||
using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;
|
using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// the iterator
|
/// the iterator
|
||||||
IteratorType anchor;
|
IteratorType anchor{};
|
||||||
/// an index for arrays (used to create key names)
|
/// an index for arrays (used to create key names)
|
||||||
std::size_t array_index = 0;
|
std::size_t array_index = 0;
|
||||||
/// last stringified array index
|
/// last stringified array index
|
||||||
@ -4696,15 +4698,30 @@ template<typename IteratorType> class iteration_proxy_value
|
|||||||
/// a string representation of the array index
|
/// a string representation of the array index
|
||||||
mutable string_type array_index_str = "0";
|
mutable string_type array_index_str = "0";
|
||||||
/// an empty string (to return a reference for primitive values)
|
/// an empty string (to return a reference for primitive values)
|
||||||
const string_type empty_str{};
|
string_type empty_str{};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit iteration_proxy_value(IteratorType it) noexcept
|
explicit iteration_proxy_value() = default;
|
||||||
|
explicit iteration_proxy_value(IteratorType it, std::size_t array_index_ = 0)
|
||||||
|
noexcept(std::is_nothrow_move_constructible<IteratorType>::value
|
||||||
|
&& std::is_nothrow_default_constructible<string_type>::value)
|
||||||
: anchor(std::move(it))
|
: anchor(std::move(it))
|
||||||
|
, array_index(array_index_)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
iteration_proxy_value(iteration_proxy_value const&) = default;
|
||||||
|
iteration_proxy_value& operator=(iteration_proxy_value const&) = default;
|
||||||
|
// older GCCs are a bit fussy and require explicit noexcept specifiers on defaulted functions
|
||||||
|
iteration_proxy_value(iteration_proxy_value&&)
|
||||||
|
noexcept(std::is_nothrow_move_constructible<IteratorType>::value
|
||||||
|
&& std::is_nothrow_move_constructible<string_type>::value) = default;
|
||||||
|
iteration_proxy_value& operator=(iteration_proxy_value&&)
|
||||||
|
noexcept(std::is_nothrow_move_assignable<IteratorType>::value
|
||||||
|
&& std::is_nothrow_move_assignable<string_type>::value) = default;
|
||||||
|
~iteration_proxy_value() = default;
|
||||||
|
|
||||||
/// dereference operator (needed for range-based for)
|
/// dereference operator (needed for range-based for)
|
||||||
iteration_proxy_value& operator*()
|
const iteration_proxy_value& operator*() const
|
||||||
{
|
{
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@ -4718,6 +4735,14 @@ template<typename IteratorType> class iteration_proxy_value
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iteration_proxy_value operator++(int)& // NOLINT(cert-dcl21-cpp)
|
||||||
|
{
|
||||||
|
auto tmp = iteration_proxy_value(anchor, array_index);
|
||||||
|
++anchor;
|
||||||
|
++array_index;
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
/// equality operator (needed for InputIterator)
|
/// equality operator (needed for InputIterator)
|
||||||
bool operator==(const iteration_proxy_value& o) const
|
bool operator==(const iteration_proxy_value& o) const
|
||||||
{
|
{
|
||||||
@ -4778,25 +4803,34 @@ template<typename IteratorType> class iteration_proxy
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
/// the container to iterate
|
/// the container to iterate
|
||||||
typename IteratorType::reference container;
|
typename IteratorType::pointer container = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
explicit iteration_proxy() = default;
|
||||||
|
|
||||||
/// construct iteration proxy from a container
|
/// construct iteration proxy from a container
|
||||||
explicit iteration_proxy(typename IteratorType::reference cont) noexcept
|
explicit iteration_proxy(typename IteratorType::reference cont) noexcept
|
||||||
: container(cont) {}
|
: container(&cont) {}
|
||||||
|
|
||||||
|
iteration_proxy(iteration_proxy const&) = default;
|
||||||
|
iteration_proxy& operator=(iteration_proxy const&) = default;
|
||||||
|
iteration_proxy(iteration_proxy&&) noexcept = default;
|
||||||
|
iteration_proxy& operator=(iteration_proxy&&) noexcept = default;
|
||||||
|
~iteration_proxy() = default;
|
||||||
|
|
||||||
/// return iterator begin (needed for range-based for)
|
/// return iterator begin (needed for range-based for)
|
||||||
iteration_proxy_value<IteratorType> begin() noexcept
|
iteration_proxy_value<IteratorType> begin() const noexcept
|
||||||
{
|
{
|
||||||
return iteration_proxy_value<IteratorType>(container.begin());
|
return iteration_proxy_value<IteratorType>(container->begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// return iterator end (needed for range-based for)
|
/// return iterator end (needed for range-based for)
|
||||||
iteration_proxy_value<IteratorType> end() noexcept
|
iteration_proxy_value<IteratorType> end() const noexcept
|
||||||
{
|
{
|
||||||
return iteration_proxy_value<IteratorType>(container.end());
|
return iteration_proxy_value<IteratorType>(container->end());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Structured Bindings Support
|
// Structured Bindings Support
|
||||||
// For further reference see https://blog.tartanllama.xyz/structured-bindings/
|
// For further reference see https://blog.tartanllama.xyz/structured-bindings/
|
||||||
// And see https://github.com/nlohmann/json/pull/1391
|
// And see https://github.com/nlohmann/json/pull/1391
|
||||||
@ -4844,6 +4878,11 @@ class tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >>
|
|||||||
#endif
|
#endif
|
||||||
} // namespace std
|
} // namespace std
|
||||||
|
|
||||||
|
#if JSON_HAS_RANGES
|
||||||
|
template <typename IteratorType>
|
||||||
|
inline constexpr bool ::std::ranges::enable_borrowed_range<::nlohmann::detail::iteration_proxy<IteratorType>> = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
// #include <nlohmann/detail/meta/cpp_future.hpp>
|
// #include <nlohmann/detail/meta/cpp_future.hpp>
|
||||||
|
|
||||||
// #include <nlohmann/detail/meta/type_traits.hpp>
|
// #include <nlohmann/detail/meta/type_traits.hpp>
|
||||||
@ -12197,9 +12236,12 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
// make sure BasicJsonType is basic_json or const basic_json
|
// make sure BasicJsonType is basic_json or const basic_json
|
||||||
static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
|
static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
|
||||||
"iter_impl only accepts (const) basic_json");
|
"iter_impl only accepts (const) basic_json");
|
||||||
|
// superficial check for the LegacyBidirectionalIterator named requirement
|
||||||
|
static_assert(std::is_base_of<std::bidirectional_iterator_tag, std::bidirectional_iterator_tag>::value
|
||||||
|
&& std::is_base_of<std::bidirectional_iterator_tag, typename array_t::iterator::iterator_category>::value,
|
||||||
|
"basic_json iterator assumes array and object type iterators satisfy the LegacyBidirectionalIterator named requirement.");
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.
|
/// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.
|
||||||
/// The C++ Standard has never required user-defined iterators to derive from std::iterator.
|
/// The C++ Standard has never required user-defined iterators to derive from std::iterator.
|
||||||
/// A user-defined iterator should provide publicly accessible typedefs named
|
/// A user-defined iterator should provide publicly accessible typedefs named
|
||||||
|
|||||||
@ -80,7 +80,7 @@ TEST_CASE("iterator_wrapper")
|
|||||||
json j = { {"A", 1}, {"B", 2} };
|
json j = { {"A", 1}, {"B", 2} };
|
||||||
int counter = 1;
|
int counter = 1;
|
||||||
|
|
||||||
for (auto& i : json::iterator_wrapper(j))
|
for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
|
||||||
{
|
{
|
||||||
switch (counter++)
|
switch (counter++)
|
||||||
{
|
{
|
||||||
@ -226,7 +226,7 @@ TEST_CASE("iterator_wrapper")
|
|||||||
const json j = { {"A", 1}, {"B", 2} };
|
const json j = { {"A", 1}, {"B", 2} };
|
||||||
int counter = 1;
|
int counter = 1;
|
||||||
|
|
||||||
for (auto& i : json::iterator_wrapper(j))
|
for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
|
||||||
{
|
{
|
||||||
switch (counter++)
|
switch (counter++)
|
||||||
{
|
{
|
||||||
@ -361,7 +361,7 @@ TEST_CASE("iterator_wrapper")
|
|||||||
json j = { "A", "B" };
|
json j = { "A", "B" };
|
||||||
int counter = 1;
|
int counter = 1;
|
||||||
|
|
||||||
for (auto& i : json::iterator_wrapper(j))
|
for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
|
||||||
{
|
{
|
||||||
switch (counter++)
|
switch (counter++)
|
||||||
{
|
{
|
||||||
@ -507,7 +507,7 @@ TEST_CASE("iterator_wrapper")
|
|||||||
const json j = { "A", "B" };
|
const json j = { "A", "B" };
|
||||||
int counter = 1;
|
int counter = 1;
|
||||||
|
|
||||||
for (auto& i : json::iterator_wrapper(j))
|
for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
|
||||||
{
|
{
|
||||||
switch (counter++)
|
switch (counter++)
|
||||||
{
|
{
|
||||||
@ -624,7 +624,7 @@ TEST_CASE("iterator_wrapper")
|
|||||||
json j = 1;
|
json j = 1;
|
||||||
int counter = 1;
|
int counter = 1;
|
||||||
|
|
||||||
for (auto& i : json::iterator_wrapper(j))
|
for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
|
||||||
{
|
{
|
||||||
++counter;
|
++counter;
|
||||||
CHECK(i.key() == "");
|
CHECK(i.key() == "");
|
||||||
@ -693,7 +693,7 @@ TEST_CASE("iterator_wrapper")
|
|||||||
const json j = 1;
|
const json j = 1;
|
||||||
int counter = 1;
|
int counter = 1;
|
||||||
|
|
||||||
for (auto& i : json::iterator_wrapper(j))
|
for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
|
||||||
{
|
{
|
||||||
++counter;
|
++counter;
|
||||||
CHECK(i.key() == "");
|
CHECK(i.key() == "");
|
||||||
@ -777,7 +777,7 @@ TEST_CASE("items()")
|
|||||||
json j = { {"A", 1}, {"B", 2} };
|
json j = { {"A", 1}, {"B", 2} };
|
||||||
int counter = 1;
|
int counter = 1;
|
||||||
|
|
||||||
for (auto& i : j.items())
|
for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
|
||||||
{
|
{
|
||||||
switch (counter++)
|
switch (counter++)
|
||||||
{
|
{
|
||||||
@ -939,7 +939,7 @@ TEST_CASE("items()")
|
|||||||
const json j = { {"A", 1}, {"B", 2} };
|
const json j = { {"A", 1}, {"B", 2} };
|
||||||
int counter = 1;
|
int counter = 1;
|
||||||
|
|
||||||
for (auto& i : j.items())
|
for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
|
||||||
{
|
{
|
||||||
switch (counter++)
|
switch (counter++)
|
||||||
{
|
{
|
||||||
@ -1074,7 +1074,7 @@ TEST_CASE("items()")
|
|||||||
json j = { "A", "B" };
|
json j = { "A", "B" };
|
||||||
int counter = 1;
|
int counter = 1;
|
||||||
|
|
||||||
for (auto& i : j.items())
|
for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
|
||||||
{
|
{
|
||||||
switch (counter++)
|
switch (counter++)
|
||||||
{
|
{
|
||||||
@ -1220,7 +1220,7 @@ TEST_CASE("items()")
|
|||||||
const json j = { "A", "B" };
|
const json j = { "A", "B" };
|
||||||
int counter = 1;
|
int counter = 1;
|
||||||
|
|
||||||
for (auto& i : j.items())
|
for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
|
||||||
{
|
{
|
||||||
switch (counter++)
|
switch (counter++)
|
||||||
{
|
{
|
||||||
@ -1337,7 +1337,7 @@ TEST_CASE("items()")
|
|||||||
json j = 1;
|
json j = 1;
|
||||||
int counter = 1;
|
int counter = 1;
|
||||||
|
|
||||||
for (auto& i : j.items())
|
for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
|
||||||
{
|
{
|
||||||
++counter;
|
++counter;
|
||||||
CHECK(i.key() == "");
|
CHECK(i.key() == "");
|
||||||
@ -1406,7 +1406,7 @@ TEST_CASE("items()")
|
|||||||
const json j = 1;
|
const json j = 1;
|
||||||
int counter = 1;
|
int counter = 1;
|
||||||
|
|
||||||
for (auto& i : j.items())
|
for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
|
||||||
{
|
{
|
||||||
++counter;
|
++counter;
|
||||||
CHECK(i.key() == "");
|
CHECK(i.key() == "");
|
||||||
|
|||||||
@ -32,6 +32,12 @@ SOFTWARE.
|
|||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
|
#if JSON_HAS_RANGES
|
||||||
|
// JSON_HAS_CPP_20 (magic keyword; do not remove)
|
||||||
|
#include <algorithm>
|
||||||
|
#include <ranges>
|
||||||
|
#endif
|
||||||
|
|
||||||
TEST_CASE("iterators 2")
|
TEST_CASE("iterators 2")
|
||||||
{
|
{
|
||||||
SECTION("iterator comparisons")
|
SECTION("iterator comparisons")
|
||||||
@ -881,4 +887,100 @@ TEST_CASE("iterators 2")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if JSON_HAS_RANGES
|
||||||
|
SECTION("ranges")
|
||||||
|
{
|
||||||
|
SECTION("concepts")
|
||||||
|
{
|
||||||
|
using nlohmann::detail::iteration_proxy_value;
|
||||||
|
CHECK(std::bidirectional_iterator<json::iterator>);
|
||||||
|
CHECK(std::input_iterator<iteration_proxy_value<json::iterator>>);
|
||||||
|
|
||||||
|
CHECK(std::is_same<json::iterator, std::ranges::iterator_t<json>>::value);
|
||||||
|
CHECK(std::ranges::bidirectional_range<json>);
|
||||||
|
|
||||||
|
using nlohmann::detail::iteration_proxy;
|
||||||
|
using items_type = decltype(std::declval<json&>().items());
|
||||||
|
CHECK(std::is_same<items_type, iteration_proxy<json::iterator>>::value);
|
||||||
|
CHECK(std::is_same<iteration_proxy_value<json::iterator>, std::ranges::iterator_t<items_type>>::value);
|
||||||
|
CHECK(std::ranges::input_range<items_type>);
|
||||||
|
}
|
||||||
|
|
||||||
|
// libstdc++ algorithms don't work with Clang 15 (04/2022)
|
||||||
|
#if !defined(__clang__) || (defined(__clang__) && defined(__GLIBCXX__))
|
||||||
|
SECTION("algorithms")
|
||||||
|
{
|
||||||
|
SECTION("copy")
|
||||||
|
{
|
||||||
|
json j{"foo", "bar"};
|
||||||
|
auto j_copied = json::array();
|
||||||
|
|
||||||
|
std::ranges::copy(j, std::back_inserter(j_copied));
|
||||||
|
|
||||||
|
CHECK(j == j_copied);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("find_if")
|
||||||
|
{
|
||||||
|
json j{1, 3, 2, 4};
|
||||||
|
auto j_even = json::array();
|
||||||
|
|
||||||
|
#if JSON_USE_IMPLICIT_CONVERSIONS
|
||||||
|
auto it = std::ranges::find_if(j, [](int v) noexcept
|
||||||
|
{
|
||||||
|
return (v % 2) == 0;
|
||||||
|
});
|
||||||
|
#else
|
||||||
|
auto it = std::ranges::find_if(j, [](const json & j) noexcept
|
||||||
|
{
|
||||||
|
int v;
|
||||||
|
j.get_to(v);
|
||||||
|
return (v % 2) == 0;
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CHECK(*it == 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// libstdc++ views don't work with Clang 15 (04/2022)
|
||||||
|
// libc++ hides limited ranges implementation behind guard macro
|
||||||
|
#if !(defined(__clang__) && (defined(__GLIBCXX__) || defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)))
|
||||||
|
SECTION("views")
|
||||||
|
{
|
||||||
|
SECTION("reverse")
|
||||||
|
{
|
||||||
|
json j{1, 2, 3, 4, 5};
|
||||||
|
json j_expected{5, 4, 3, 2, 1};
|
||||||
|
|
||||||
|
auto reversed = j | std::views::reverse;
|
||||||
|
CHECK(reversed == j_expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("transform")
|
||||||
|
{
|
||||||
|
json j
|
||||||
|
{
|
||||||
|
{ "a_key", "a_value"},
|
||||||
|
{ "b_key", "b_value"},
|
||||||
|
{ "c_key", "c_value"},
|
||||||
|
};
|
||||||
|
json j_expected{"a_key", "b_key", "c_key"};
|
||||||
|
|
||||||
|
auto transformed = j.items() | std::views::transform([](const auto & item)
|
||||||
|
{
|
||||||
|
return item.key();
|
||||||
|
});
|
||||||
|
auto j_transformed = json::array();
|
||||||
|
std::ranges::copy(transformed, std::back_inserter(j_transformed));
|
||||||
|
|
||||||
|
CHECK(j_transformed == j_expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user