5

I'm currently trying to wrap my head around the upcoming std::ranges. As an exercise I'd like to implement a "toupper" view from scratch which takes a range/view over some characters and transforms those to upper case.

Hacking together a view and some iterator was pretty straight forward, I don't get how operator() and | have to be overloaded in order to compose like the rest of ranges does though.

Here is the "toupper_fn" (to follow the ranges naming convention) I came up with so far. This is pretty much a copy/paste of some view function inside the ranges v3:

    struct toupper_fn {
      template <typename Rng>
      auto operator()(Rng&& rng) const {
        return toupper_view{std::forward<Rng>(rng)};
      }

      template <typename Rng>
      friend auto operator|(Rng&& rng, toupper_fn& c)
          -> decltype(c(std::forward<Rng>(rng))) {
        return c(std::forward<Rng>(rng));
      }

      template <typename Rng>
      friend auto operator|(Rng&& rng, toupper_fn const& c)
          -> decltype(c(std::forward<Rng>(rng))) {
        return c(std::forward<Rng>(rng));
      }

      template <typename Rng>
      friend auto operator|(Rng&& rng, toupper_fn&& c)
          -> decltype(std::move(c)(std::forward<Rng>(rng))) {
        return std::move(c)(std::forward<Rng>(rng));
      }
    };

The problem is that with those definitions both, the classic function call syntax (view(view...)) and the pipe syntax, only work if my own view is the last in the chain.

Here is the whole code on godbolt ->

Either with ranges-v3: https://godbolt.org/z/6RlNVC

Or cmcstl2: https://godbolt.org/z/wba_yW

Vinci
  • 1,382
  • 10
  • 12
  • Not sure whether that helps, but after inspecting Eric Niebler's `range-v3` implementation, I came across the `RANGES_INLINE_VARIABLE` macro that's used to create the variables. I'd look into that. – Fureeish Sep 20 '19 at 17:03
  • Afaik RANGES_INLINE_VARIABLE is just a compatibility macro for pre-C++17 compilers which do not support inline variables. – Vinci Sep 20 '19 at 17:30

1 Answers1

2

Ok I figured it out. The problem with the initial code was that my custom view neither satisfied the range nor the view concept. That's because my iterator was missing some traits...

Also it's a good idea to wrap the constructor argument inside a ranges::view::all to directly support containers as well. The view "all" creates either a no-op, a reference wrapper or a subrange depending on its input type.

Here is a modified working version: https://godbolt.org/z/5YOGNW

/edit

A CppCon2019 talk by Chris Di Bella clarifies the second part of my answer a bit. But instead of wrapping the constructor argument he uses a deduction guide for the view itself which is of course the preferred way:

template<typename R>
toupper_view(R&&)->toupper_view<ranges::all_view<R>>; // or in ranges v3 -> ranges::cpp20::all_view
Vinci
  • 1,382
  • 10
  • 12