7

I want to split, reverse, and then join a string using range-v3. However, code below won't compile.

#include <range/v3/all.hpp>
#include <iostream>

using namespace ranges;

int main(int argc, char *argv[])
{
    auto str = std::string("abc.def.ghi");
    auto sv = str
              | view::split('.')
              | view::reverse
              | view::join('.');
    std::cout<<sv;
    return 0;
}

Compiler output:

error: invalid operands to binary expression ('decltype(pipeable_access::impl<view<reverse_fn> >::pipe(static_cast<ranges::v3::split_view<ranges::v3::iterator_range<std::_String_iterator<std::_String_val<std::_Simple_types<char> > >, std::_String_iterator<std::_String_val<std::_Simple_types<char> > > >, ranges::v3::view::split_fn::element_pred<std::basic_string<char, std::char_traits<char>, std::allocator<char> > &> > &&>(arg), pipe))' (aka 'void') and 'decltype(make_view(view_access::impl<join_fn>::bind(this->view_, static_cast<char &&>(ts))))' (aka 'view<ranges::v3::detail::pipeable_binder<std::_Binder<std::_Unforced, ranges::v3::view::join_fn &, const std::_Ph<1> &, char> > >'))
range\v3\view\any_view.hpp:60: candidate function not viable: cannot convert argument of incomplete type 'decltype(pipeable_access::impl<view<reverse_fn> >::pipe(static_cast<ranges::v3::split_view<ranges::v3::iterator_range<std::_String_iterator<std::_String_val<std::_Simple_types<char> > >, std::_String_iterator<std::_String_val<std::_Simple_types<char> > > >, ranges::v3::view::split_fn::element_pred<std::basic_string<char, std::char_traits<char>, std::allocator<char> > &> > &&>(arg), pipe))' (aka 'void') to 'ranges::v3::category' for 1st argument
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.21.27702\include\regex:1219: candidate function not viable: cannot convert argument of incomplete type 'decltype(pipeable_access::impl<view<reverse_fn> >::pipe(static_cast<ranges::v3::split_view<ranges::v3::iterator_range<std::_String_iterator<std::_String_val<std::_Simple_types<char> > >, std::_String_iterator<std::_String_val<std::_Simple_types<char> > > >, ranges::v3::view::split_fn::element_pred<std::basic_string<char, std::char_traits<char>, std::allocator<char> > &> > &&>(arg), pipe))' (aka 'void') to 'std::_Node_flags' for 1st argument
range\v3\utility\functional.hpp:725: candidate template ignored: substitution failure [with Arg = void, Pipe = ranges::v3::view::view<ranges::v3::detail::pipeable_binder<std::_Binder<std::_Unforced, ranges::v3::view::join_fn &, const std::_Ph<1> &, char> > >, _concept_requires_724 = false, $3 = nullptr]: cannot form a reference to 'void'
range\v3\utility\functional.hpp:734: candidate template ignored: requirement 'false || (is_pipeable<void>() && is_pipeable<ranges::v3::view::view<ranges::v3::detail::pipeable_binder<std::_Binder<std::_Unforced, ranges::v3::view::join_fn &, const std::_Ph<1> &, char> > > >())' was not satisfied [with Pipe0 = void, Pipe1 = ranges::v3::view::view<ranges::v3::detail::pipeable_binder<std::_Binder<std::_Unforced, ranges::v3::view::join_fn &, const std::_Ph<1> &, char> > >, _concept_requires_733 = false]

Found a way of doing this:

    auto temp_container = str
              | view::split('.')
              | ::ranges::to_vector
              | action::reverse
              ;
    std::string output = temp_container 
              | view::all
              | view::join('.')
              ;

Any better ideas?

joyqat
  • 73
  • 5
  • Pretty sure they broke something, since now simple `ranges::view::group_by` seems to not work at all. I believe the problem lies in the fact that they might've broken *ranges of ranges*, but I cannot confirm. – Fureeish Jul 18 '19 at 08:55
  • @Fureeish None of the `ranges-v3` versions available on godbolt (`0.3.0`, `0.3.5`, `0.3.6`, `trunk`) compile this code snippet (on either `gcc` or `clang`). Example [here](https://godbolt.org/z/oqoUZE). But I also don't know what time frames you are thinking of when you say "now". – Max Langhof Jul 18 '19 at 09:17
  • @MaxLanghof talking about 0.5.0 release. Godbolt also does not compile simple `group_by`s. Given the fact that ranges are still in development, I wouldn't want to try guessing what's wrong as of now. The code should work. – Fureeish Jul 18 '19 at 09:25
  • Range-v3's `view::group_by` tests are passing. What is not working for you @Fureeish? – Eric Niebler Jul 25 '19 at 00:26
  • 1
    You can't pipe a temporary container into a view adaptor because it would create dangling references. Save the vector into a named variable first. – Eric Niebler Jul 26 '19 at 01:43

1 Answers1

7

view::split returns either a an input range or a forward range depending on the adapted range. view::reverse requires at least a bidirectional range in order to iterate it backwards.

In principle it would be possible to implement a split_view that is a bidirectional range. The problem is that the constructor would already need a complete traversal of the adapted range to find the last element, making it an o(n) operation. And as far as I know the construction of views is required to be o(1).

sv90
  • 522
  • 5
  • 11
  • Thanks for your explanation. I think there could be something like `as_list` or `as_vector`, so we can pipe the operations like `split | as_vector | reverse | join` when hold the o(1) construction of view. – joyqat Jul 19 '19 at 00:48
  • @joyqat: You can't have O(1) construction because it's a *vector*. Building it must perform `O(n)` copies. So you may as well just stick the result of `split` in a vector, then apply `reverse` and `join`. Why does it all need to be on one line? – Nicol Bolas Jul 19 '19 at 02:13
  • @NicolBolas Because such pipe operations can eliminate temporaries and be robust, I think. Moreover, it's really cool if I can do that using c++. :P – joyqat Jul 19 '19 at 08:57
  • 2
    @joyqat there are `ranges::to_vector` and `range::to`to construct a `std::vector` or an arbitrary `Container` from a range. But you cannot pipe it like that since `to_vector` returns a temporary vector that is a rvalue which are not pipeable with views. So you will have to store the vector as an intermediate result. This is a good thing since it would produce dangling references otherwise. – sv90 Jul 19 '19 at 13:19
  • @joyqat: How would you eliminate the temporary? Being able to reverse it requires that it first has to be built, and therefore must allocate memory and copy-initialize every element. The resulting container must then be used by every subsequent operation in the chain. – Nicol Bolas Jul 19 '19 at 13:23