Why is `boost::hana::range_c` not a Sequence?
Asked Answered
S

1

5
#include <string>
#include <utility>
#include <vector>
#include <boost/hana.hpp>
namespace hana = boost::hana;

template <typename ...T>
void indexed_T_work(T&& ...args)
{
    auto indices = hana::range_c<std::size_t, 0, sizeof...(T)>;
    auto types = hana::make_tuple(std::forward<T>(args)...);
    hana::for_each(
        hana::zip(indices, types)
      , [](auto&& pair_) { /* Do index-dependent work with each `T` */ }
        );
}

int main()
{
    indexed_T_work(5, 13, std::vector<std::string>{}, 32.f, 42, "foo");
}

I'd like to use hana::zip on a hana::tuple and hana::range_c, but hana::range_c is not considered a Sequence, which is a requirement for hana::zip. What is the reasoning behind this decision? How can I (idiomatically) accomplish my goal while respecting that decision?

Switchback answered 16/10, 2015 at 19:29 Comment(0)
H
8

First, there are several solutions:

Solution 1

auto indices = hana::to<hana::tuple_tag>(hana::range_c<std::size_t, 0, sizeof...(T)>);
auto types = hana::make_tuple(std::forward<T>(args)...);
hana::for_each(hana::zip(indices, types), hana::fuse([](auto i, auto&& x) {
    // ...
}));

Solution 2

auto indices = hana::range_c<std::size_t, 0, sizeof...(T)>;
auto types = hana::make_tuple(std::forward<T>(args)...);
hana::for_each(indices, [&](auto i) {
    auto& x = types[i];
    // ...
});

Solution 3

auto types = hana::make_tuple(std::forward<T>(args)...);
hana::size_c<sizeof...(T)>.times.with_index([&](auto i) {
    auto& x = types[i];
    // ...
});

Solution (1) has the disadvantage of making a copy of each args because zip returns a sequence of sequences, and everything in Hana is by value. Since this is probably not what you want, you should pick whichever you prefer between solutions (2) and (3), which are really equivalent.

Now, the reason why ranges do not model the Sequence concept is because that wouldn't make sense. The Sequence concept requires that we be able to create an arbitrary Sequence using the hana::make function. Hence, for any Sequence tag S, hana::make<S>(...) must create a Sequence of tag S that contains .... However, a range must contain contiguous integral_constants in some interval. Hence, if range was a Sequence, hana::make<hana::range_tag>(...) should contain whatever ... is, which breaks the invariant of a range if ... are not contiguous integral_constants. Consider for example

hana::make<hana::range_tag>(hana::int_c<8>, hana::int_c<3>,
                            hana::int_c<5>, hana::int_c<10>)

This should be a range containing integral_constants 8,3,5,10, which does not make sense. Another similar example showing why a range can't be a Sequence is the permutations algorithm. The permutations algorithm takes a Sequence and returns a Sequence of Sequences containing all the permutations. Clearly, since a range can only hold integral_constants, it does not make sense to try and create a range of ranges. Examples like this abound.

In other words, ranges are too specialized to model the Sequence concept. The upside of having such a specialized structure is that it's very compile-time efficient. The downside is that it's not a general-purpose container and some operations can't be done on it (like zip). However, you can totally take a range and convert it to a full-blown sequence, if you know what the tradeoff is.

Holsworth answered 17/10, 2015 at 1:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.