0

I try to get range views that behave like true-false masks. To do logical operations I want to implement ands and ors of masks. I have a working compile time or:

struct make_or_mask_fn
{
    template<typename... Msks>
    auto operator()(Msks&&... msks) const
    {
        CONCEPT_ASSERT((Range<Msks>() || ...));
        return ranges::view::zip(std::forward<Msks>(msks)...) |
               ranges::view::transform(
                   [](auto&& range_item) -> bool {
                       return tuple_or(range_item);
                   });
    }

private:
    template<typename... T>
    static bool variable_length_or(const T... v)
    {
        return (v || ...);
    }
    template<typename... T, std::size_t... Idx>
    static bool tuple_or(const std::tuple<T...> t,
                         std::index_sequence<Idx...>)
    {
        return variable_length_or(std::get<Idx>(t)...);
    }
    template<typename... T>
    static bool tuple_or(const std::tuple<T...> t)
    {
        return tuple_or(t, std::index_sequence_for<T...>{});
    }
};
RANGES_INLINE_VARIABLE(make_or_mask_fn, make_or_masker)

which I can call just fine

std::vector<bool> mask1 = ...
std::vector<bool> mask2 = ...
std::vector<bool> mask3 = ...
auto or_of_masks = make_or_masker(mask1, mask2, mask3);

What this cannot do at the moment is build the or of a number of masks that is unknown at compile time. My current attempt is to accept a vector of ranges, check its size and then call the variadic template or from before:

struct make_vector_or_mask_fn
{
    template<typename Msk>
    auto operator()(std::vector<Msk> msks)
        const // TODO const and reference types
    {
        CONCEPT_ASSERT(Range<Msk>());
        // todo return range with all true (an or of zero elements is true)
        assert(msks.size() != 0);
        if(msks.size() == 1)
            return or_ranges(msks[0]);
        if(msks.size() == 2)
            return or_ranges(msks[0], msks[1]);
        if(msks.size() == 3)
            return or_ranges(msks[0], msks[1], msks[2]);
        /// TODO: go until ... maybe 8 and recurse afterwards
    }
private:
    template<typename... Msks>
    static auto or_ranges(Msks&&... msks)
    {
        CONCEPT_ASSERT((Range<Msks>() || ...));
        return ranges::view::zip(std::forward<Msks>(msks)...) |
               ranges::view::transform(
                   [](auto&& range_item) -> bool {
                       return tuple_or(range_item);
                   });
    }
    template<typename... T>
    static bool variable_length_or(const T... v)
    {
        return (v || ...);
    }
    template<typename... T, std::size_t... Idx>
    static bool tuple_or(const std::tuple<T...> t,
                         std::index_sequence<Idx...>)
    {
        return variable_length_or(std::get<Idx>(t)...);
    }
    template<typename... T>
    static bool tuple_or(const std::tuple<T...> t)
    {
        return tuple_or(t, std::index_sequence_for<T...>{});
    }
};
RANGES_INLINE_VARIABLE(make_vector_or_mask_fn, make_vector_or_masker)

This does not compile with the following error:

../include/range/v3/view/mask.hpp:221:25: error: 'auto' in return type deduced as 'ranges::v3::transform_view<ranges::v3::zip_view<ranges::v3::iterator_range<__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > > >, ranges::v3::iterator_range<__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > >,
  __gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > > > >, (lambda at ../include/range/v3/view/mask.hpp:188:32)>' here but deduced as 'ranges::v3::transform_view<ranges::v3::zip_view<ranges::v3::iterator_range<__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > > > >,
  (lambda at ../include/range/v3/view/mask.hpp:188:32)>' in earlier return statement
                    return or_ranges(msks[0], msks[1]);
                    ^

To my understanding this is telling me that my or_ranges returns a different type depending on the number of arguments. (The zip I'm using retains knowledge of what got zipped together).

So I'm wondering, how do I type erase what is in a range?

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
pseyfert
  • 3,263
  • 3
  • 21
  • 47
  • Do you care if it is 3x-10x slower than the non-type erased version? Because that makes it easier. Have you tried any of the type erasure in rangesv3? – Yakk - Adam Nevraumont Aug 29 '18 at 20:10
  • This is meant to go into a time critical application. If type erasure comes at a price, I'll still give it a try and profile if I can afford it. – pseyfert Aug 30 '18 at 08:10

1 Answers1

1

Return ranges::v3::any_input_view < bool > instead of auto.

This will be 10x slower even in optimized builds. Type erasure is expensive.

A hand rolled solution that knows that the input range has a length and gets data in buffered chunks before doling it out could probably get the performance closer to non-type erased. I'd expect it would still be noticable slower however.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • One nitpick so far: this results in a warning `‘using any_input_view = struct ranges::v3::any_view instead. [-Wdeprecated-declarations]`. replacing `any_input_view` by `any_view` appears to work. – pseyfert Aug 31 '18 at 09:34
  • I have no idea why they deprecated `any__view` and force you to write `any_view<.., ranges::category::>` so much more to write. – Johannes Schaub - litb Aug 11 '23 at 10:16