Skip to content

Commit

Permalink
Fix iterators to meet (more) std::ranges requirements
Browse files Browse the repository at this point in the history
Fixes nlohmann#3130.
Related discussion: nlohmann#3408
  • Loading branch information
falbrechtskirchinger committed May 2, 2022
1 parent 4120c2e commit da5e454
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 38 deletions.
5 changes: 4 additions & 1 deletion include/nlohmann/detail/iterators/iter_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,12 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
// make sure BasicJsonType is basic_json or const basic_json
static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
"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:

/// 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.
/// A user-defined iterator should provide publicly accessible typedefs named
Expand Down
65 changes: 53 additions & 12 deletions include/nlohmann/detail/iterators/iteration_proxy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
#include <tuple> // tuple_size, get, tuple_element
#include <utility> // move

#if JSON_HAS_RANGES
#include <ranges> // enable_borrowed_range
#endif

#include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/value_t.hpp>

Expand All @@ -25,30 +29,45 @@ template<typename IteratorType> class iteration_proxy_value
public:
using difference_type = std::ptrdiff_t;
using value_type = iteration_proxy_value;
using pointer = value_type * ;
using reference = value_type & ;
using pointer = value_type *;
using reference = value_type &;
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;

private:
/// the iterator
IteratorType anchor;
IteratorType anchor{};
/// an index for arrays (used to create key names)
std::size_t array_index = 0;
/// last stringified array index
mutable std::size_t array_index_last = 0;
/// a string representation of the array index
mutable string_type array_index_str = "0";
/// an empty string (to return a reference for primitive values)
const string_type empty_str{};
string_type empty_str{};

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))
, 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)
iteration_proxy_value& operator*()
const iteration_proxy_value& operator*() const
{
return *this;
}
Expand All @@ -62,6 +81,14 @@ template<typename IteratorType> class iteration_proxy_value
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)
bool operator==(const iteration_proxy_value& o) const
{
Expand Down Expand Up @@ -122,25 +149,34 @@ template<typename IteratorType> class iteration_proxy
{
private:
/// the container to iterate
typename IteratorType::reference container;
typename IteratorType::pointer container = nullptr;

public:
explicit iteration_proxy() = default;

/// construct iteration proxy from a container
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)
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)
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
// For further reference see https://blog.tartanllama.xyz/structured-bindings/
// And see https://github.com/nlohmann/json/pull/1391
Expand Down Expand Up @@ -187,3 +223,8 @@ class tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >>
#pragma clang diagnostic pop
#endif
} // namespace std

#if JSON_HAS_RANGES
template <typename IteratorType>
inline constexpr bool ::std::ranges::enable_borrowed_range<::nlohmann::detail::iteration_proxy<IteratorType>> = true;
#endif
14 changes: 14 additions & 0 deletions include/nlohmann/detail/macro_scope.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,26 @@
#define JSON_HAS_THREE_WAY_COMPARISON 0
#endif

#ifndef JSON_HAS_RANGES
// ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error
#if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427
#define JSON_HAS_RANGES 0
#elif defined(__cpp_lib_ranges)
#define JSON_HAS_RANGES 1
#endif
#endif

#ifndef JSON_HAS_RANGES
#define JSON_HAS_RANGES 0
#endif

#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address)
#define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]]
#else
#define JSON_NO_UNIQUE_ADDRESS
#endif


// disable documentation warnings on clang
#if defined(__clang__)
#pragma clang diagnostic push
Expand Down
84 changes: 71 additions & 13 deletions single_include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2341,12 +2341,26 @@ using is_detected_convertible =
#define JSON_HAS_THREE_WAY_COMPARISON 0
#endif

#ifndef JSON_HAS_RANGES
// ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error
#if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427
#define JSON_HAS_RANGES 0
#elif defined(__cpp_lib_ranges)
#define JSON_HAS_RANGES 1
#endif
#endif

#ifndef JSON_HAS_RANGES
#define JSON_HAS_RANGES 0
#endif

#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address)
#define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]]
#else
#define JSON_NO_UNIQUE_ADDRESS
#endif


// disable documentation warnings on clang
#if defined(__clang__)
#pragma clang diagnostic push
Expand Down Expand Up @@ -4648,6 +4662,10 @@ constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::va
#include <tuple> // tuple_size, get, tuple_element
#include <utility> // move

#if JSON_HAS_RANGES
#include <ranges> // enable_borrowed_range
#endif

// #include <nlohmann/detail/meta/type_traits.hpp>

// #include <nlohmann/detail/value_t.hpp>
Expand All @@ -4669,30 +4687,45 @@ template<typename IteratorType> class iteration_proxy_value
public:
using difference_type = std::ptrdiff_t;
using value_type = iteration_proxy_value;
using pointer = value_type * ;
using reference = value_type & ;
using pointer = value_type *;
using reference = value_type &;
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;

private:
/// the iterator
IteratorType anchor;
IteratorType anchor{};
/// an index for arrays (used to create key names)
std::size_t array_index = 0;
/// last stringified array index
mutable std::size_t array_index_last = 0;
/// a string representation of the array index
mutable string_type array_index_str = "0";
/// an empty string (to return a reference for primitive values)
const string_type empty_str{};
string_type empty_str{};

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))
, 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)
iteration_proxy_value& operator*()
const iteration_proxy_value& operator*() const
{
return *this;
}
Expand All @@ -4706,6 +4739,14 @@ template<typename IteratorType> class iteration_proxy_value
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)
bool operator==(const iteration_proxy_value& o) const
{
Expand Down Expand Up @@ -4766,25 +4807,34 @@ template<typename IteratorType> class iteration_proxy
{
private:
/// the container to iterate
typename IteratorType::reference container;
typename IteratorType::pointer container = nullptr;

public:
explicit iteration_proxy() = default;

/// construct iteration proxy from a container
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)
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)
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
// For further reference see https://blog.tartanllama.xyz/structured-bindings/
// And see https://github.com/nlohmann/json/pull/1391
Expand Down Expand Up @@ -4832,6 +4882,11 @@ class tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >>
#endif
} // 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/type_traits.hpp>
Expand Down Expand Up @@ -12119,9 +12174,12 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
// make sure BasicJsonType is basic_json or const basic_json
static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
"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:

/// 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.
/// A user-defined iterator should provide publicly accessible typedefs named
Expand Down
Loading

0 comments on commit da5e454

Please sign in to comment.