Home > Net >  How to use a C 20 istream_view?
How to use a C 20 istream_view?

Time:12-03

I cannot find a single example on the web or in C 20 books that shows how the results of a std::ranges::istream_view can be used with follow-up operations. Every follow-up operation that I try to use results in compiler errors that are too difficult for me to figure out alone.

For example, how should I split a lazy stream of characters that comes in through an istream_view? The following code does not work, but hopefully communicates what I am trying to achieve:

#include <cstdlib>
#include <ranges>
#include <sstream>
#include <fmt/core.h>

auto main() -> int
{
  std::istringstream data{"a,b"};
  for (const auto& item:
      std::views::istream<std::string>(data)
    | std::views::split(",")
  ) {
    fmt::print("{}\n", item);
  }
  return EXIT_SUCCESS;
}

^ I expect this to write "a" on the first line, and "b" on the second line.

GCC 12.2 emits an intimidating error message:

Could not execute the program
Compiler returned: 1
Compiler stderr
<source>: In function 'int main()':
<source>:11:5: error: no match for 'operator|' (operand types are 'std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >' and 'std::ranges::views::__adaptor::_Partial<std::ranges::views::_Split, const char*>')
   10 |       std::views::istream<std::string>(data)
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |                                       |
      |                                       std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >
   11 |     | std::views::split(",")
      |     ^ ~~~~~~~~~~~~~~~~~~~~~~
      |                        |
      |                        std::ranges::views::__adaptor::_Partial<std::ranges::views::_Split, const char*>
In file included from <source>:2:
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/ranges:868:7: note: candidate: 'template<class _Lhs, class _Rhs>  requires (derived_from<_Lhs, std::ranges::views::__adaptor::_RangeAdaptorClosure>) && (derived_from<_Rhs, std::ranges::views::__adaptor::_RangeAdaptorClosure>) constexpr auto std::ranges::views::__adaptor::operator|(_Lhs, _Rhs)'
  868 |       operator|(_Lhs __lhs, _Rhs __rhs)
      |       ^~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/ranges:868:7: note:   template argument deduction/substitution failed:
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/ranges:868:7: note: constraints not satisfied
In file included from /opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/ranges:37:
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/concepts: In substitution of 'template<class _Lhs, class _Rhs>  requires (derived_from<_Lhs, std::ranges::views::__adaptor::_RangeAdaptorClosure>) && (derived_from<_Rhs, std::ranges::views::__adaptor::_RangeAdaptorClosure>) constexpr auto std::ranges::views::__adaptor::operator|(_Lhs, _Rhs) [with _Lhs = std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >; _Rhs = std::ranges::views::__adaptor::_Partial<std::ranges::views::_Split, const char*>]':
<source>:11:28:   required from here
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/concepts:67:13:   required for the satisfaction of 'derived_from<_Lhs, std::ranges::views::__adaptor::_RangeAdaptorClosure>' [with _Lhs = std::ranges::basic_istream_view<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char, std::char_traits<char> >]
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/concepts:67:28: note:   'std::ranges::views::__adaptor::_RangeAdaptorClosure' is not a base of 'std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >'
   67 |     concept derived_from = __is_base_of(_Base, _Derived)
      |                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/ranges:859:7: note: candidate: 'template<class _Self, class _Range>  requires (derived_from<typename std::remove_cvref<_Tp>::type, std::ranges::views::__adaptor::_RangeAdaptorClosure>) && (__adaptor_invocable<_Self, _Range>) constexpr auto std::ranges::views::__adaptor::operator|(_Range&&, _Self&&)'
  859 |       operator|(_Range&& __r, _Self&& __self)
      |       ^~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/ranges:859:7: note:   template argument deduction/substitution failed:
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/ranges:859:7: note: constraints not satisfied
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/ranges: In substitution of 'template<class _Self, class _Range>  requires (derived_from<typename std::remove_cvref<_Tp>::type, std::ranges::views::__adaptor::_RangeAdaptorClosure>) && (__adaptor_invocable<_Self, _Range>) constexpr auto std::ranges::views::__adaptor::operator|(_Range&&, _Self&&) [with _Self = std::ranges::views::__adaptor::_Partial<std::ranges::views::_Split, const char*>; _Range = std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >]':
<source>:11:28:   required from here
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/ranges:831:13:   required for the satisfaction of '__adaptor_invocable<_Self, _Range>' [with _Self = std::ranges::views::__adaptor::_Partial<std::ranges::views::_Split, const char*>; _Range = std::ranges::basic_istream_view<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char, std::char_traits<char> >]
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/ranges:832:9:   in requirements  [with _Args = {std::ranges::basic_istream_view<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char, std::char_traits<char> >}; _Adaptor = std::ranges::views::__adaptor::_Partial<std::ranges::views::_Split, const char*>]
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/ranges:832:44: note: the required expression 'declval<_Adaptor>()((declval<_Args>)()...)' is invalid
  832 |       = requires { std::declval<_Adaptor>()(declval<_Args>()...); };
      |                    ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~
cc1plus: note: set '-fconcepts-diagnostics-depth=' to at least 2 for more detail
In file included from /opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/streambuf:41,
                 from /opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/bits/streambuf_iterator.h:35,
                 from /opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/iterator:66,
                 from /opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/ranges:43:
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/bits/ios_base.h:87:3: note: candidate: 'constexpr std::_Ios_Fmtflags std::operator|(_Ios_Fmtflags, _Ios_Fmtflags)'
   87 |   operator|(_Ios_Fmtflags __a, _Ios_Fmtflags __b)
      |   ^~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/bits/ios_base.h:87:27: note:   no known conversion for argument 1 from 'std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >' to 'std::_Ios_Fmtflags'
   87 |   operator|(_Ios_Fmtflags __a, _Ios_Fmtflags __b)
      |             ~~~~~~~~~~~~~~^~~
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/bits/ios_base.h:130:3: note: candidate: 'constexpr std::_Ios_Openmode std::operator|(_Ios_Openmode, _Ios_Openmode)'
  130 |   operator|(_Ios_Openmode __a, _Ios_Openmode __b)
      |   ^~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/bits/ios_base.h:130:27: note:   no known conversion for argument 1 from 'std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >' to 'std::_Ios_Openmode'
  130 |   operator|(_Ios_Openmode __a, _Ios_Openmode __b)
      |             ~~~~~~~~~~~~~~^~~
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/bits/ios_base.h:170:3: note: candidate: 'constexpr std::_Ios_Iostate std::operator|(_Ios_Iostate, _Ios_Iostate)'
  170 |   operator|(_Ios_Iostate __a, _Ios_Iostate __b)
      |   ^~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/bits/ios_base.h:170:26: note:   no known conversion for argument 1 from 'std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >' to 'std::_Ios_Iostate'
  170 |   operator|(_Ios_Iostate __a, _Ios_Iostate __b)
      |             ~~~~~~~~~~~~~^~~
In file included from /opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/span:42,
                 from /opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/ranges:45:
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/cstddef:132:3: note: candidate: 'constexpr std::byte std::operator|(byte, byte)'
  132 |   operator|(byte __l, byte __r) noexcept
      |   ^~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/cstddef:132:18: note:   no known conversion for argument 1 from 'std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >' to 'std::byte'
  132 |   operator|(byte __l, byte __r) noexcept
      |             ~~~~~^~~

CodePudding user response:

views::istream is an input range. So any adaptor that you pipe that into has to work on input ranges. views::split requires forward-or-better, which is why you can't split an istream.

But there is also a views::lazy_split that provides a slight differently interface such that it does work for input ranges (see P2210 for added context for why we wanted the forward split adaptor).

However, then you have the issue that views::istream<string> is a range of string that you're trying split on a comma. But that's not quite right, what you want you want is a range of char so that you end up with a range of range of char. What you mean is views::istream<char>.

Fixing that, this works:

auto main() -> int
{
  std::istringstream data{"a,b"};
  fmt::print("{}\n",
      std::views::istream<char>(data)
    | std::views::lazy_split(','));
}

That prints [['a'], ['b']]

  • Related