Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iteration_proxy has limited usefulness in C++20 range views #4371

Open
2 tasks done
captaincrutches opened this issue May 10, 2024 · 0 comments
Open
2 tasks done

iteration_proxy has limited usefulness in C++20 range views #4371

captaincrutches opened this issue May 10, 2024 · 0 comments

Comments

@captaincrutches
Copy link

captaincrutches commented May 10, 2024

Description

This is a sort-of continuation, sort-of reopening of #3130 - it's not exactly the same use case, but certain applications of items() on a json object still don't play nicely with C++20 std::ranges.

In particular, while items() can be used as an input for views like std::views::transform, that result isn't usable in situations that require a forward_iterator, such as constructing a container from the output range's iterators. See the code below for what I mean.

The compilation error suggests the view iterator's iterator_category is not defined, which is the case if the base range doesn't model forward_range.

I did some debugging using static_asserts, and found that while iteration_proxy_value does meet all the requirements for a forward_iterator, its iterator_category is explicitly exposed as only std::input_iterator_tag. Simply changing that to std::forward_iterator_tag (or removing it and letting iterator_traits deduce it) in my testing makes it fully model forward_iterator, so that items() models forward_range and the view is usable in this case.

Reproduction steps

  • Crate a json object
  • Run its items() through an std::views::transform
  • Try to construct a container via that result's iterators, e.g. std::vector<std::string> s{view.begin(), view.end()};

Expected vs. actual results

iteration_proxy is just a wrapper around the actual iterator of json itself, which does satisfy forward_iterator, so I expect iteration_proxy_value to also satisfy forward_iterator. Instead, it only satisfies input_iterator.

Minimal code example

#include <nlohmann/json.hpp>

#include <ranges>
#include <string>
#include <vector>

int main()
{
    // This works
    nlohmann::json arr { 1, 2, 3 };
    auto arrTransform = std::views::transform([](auto&& element){ return element.template get<int>() * 2; });
    auto arrView = arr | arrTransform;
    std::vector<int> arrVec{arrView.begin(), arrView.end()};

    // This doesn't work
    nlohmann::json obj {
        { "one", 1 },
        { "two", 2 },
        { "three", 3 }
    };
    auto objItems = obj.items();
    auto objTransform = std::views::transform([](auto&& element){ return element.key(); });
    auto objView = objItems | objTransform;
    std::vector<std::string> keys{objView.begin(), objView.end()};  // Fails to compile
}

Error messages

json-range.cpp: In function ‘int main()’:
json-range.cpp:24:65: error: no matching function for call to ‘std::vector<std::__cxx11::basic_string<char>, std::allocator<std::__cxx11::basic_string<char> > >::vector(<brace-enclosed initializer list>)’
   23 |     std::vector<std::string> keys{objView.begin(), objView.end()};  // Fails to compile
      |                                                                 ^
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/vector:66,
                 from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/functional:64,
                 from /usr/include/nlohmann/json.hpp:23,
                 from json-range.cpp:1:
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/stl_vector.h:707:9: note: candidate: ‘template<class _InputIterator, class> constexpr std::vector<_Tp, _Alloc>::vector(_InputIterator, _InputIterator, const allocator_type&) [with <template-parameter-2-2> = _InputIterator; _Tp = std::__cxx11::basic_string<char>; _Alloc = std::allocator<std::__cxx11::basic_string<char> >]’
  707 |         vector(_InputIterator __first, _InputIterator __last,
      |         ^~~~~~
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/stl_vector.h:707:9: note:   template argument deduction/substitution failed:
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/stl_algobase.h:65,
                 from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/algorithm:60,
                 from /usr/include/nlohmann/json.hpp:21:
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/stl_iterator_base_types.h: In substitution of ‘template<class _InIter> using std::_RequireInputIter = std::__enable_if_t<std::is_convertible<typename std::iterator_traits< <template-parameter-1-1> >::iterator_category, std::input_iterator_tag>::value> [with _InIter = std::ranges::transform_view<std::ranges::ref_view<nlohmann::json_abi_v3_11_3::detail::iteration_proxy<nlohmann::json_abi_v3_11_3::detail::iter_impl<nlohmann::json_abi_v3_11_3::basic_json<> > > >, main()::<lambda(auto:24&&)> >::_Iterator<false>]’:
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/stl_vector.h:705:9:   required from here
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/stl_iterator_base_types.h:250:11: error: no type named ‘iterator_category’ in ‘struct std::iterator_traits<std::ranges::transform_view<std::ranges::ref_view<nlohmann::json_abi_v3_11_3::detail::iteration_proxy<nlohmann::json_abi_v3_11_3::detail::iter_impl<nlohmann::json_abi_v3_11_3::basic_json<> > > >, main()::<lambda(auto:24&&)> >::_Iterator<false> >’
  250 |     using _RequireInputIter =
      |           ^~~~~~~~~~~~~~~~~

Compiler and operating system

Gentoo, GCC 13.2

Library version

3.11.3

Validation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant