3

So I am trying to make a library using boost::hana that requires the functionality to get the index of a element based on the value:

constexpr auto tup = boost::hana::make_tuple(3_c, boost::hana::type_c<bool>);

auto index = get_index_of_first_matching(tup, boost::hana::type_c<bool>);
//   ^^^^^ would be a boost::hana::int_<1>

Is there a possible way to do this? Better yet, is it already in hana and I don't know about it?

Thanks for the support!

Russell Greene
  • 2,141
  • 17
  • 29
  • 2
    I'm sure [Louis Dionne](http://stackoverflow.com/users/627587/louis-dionne) will be able to give the real answer soon, but a possible (probably silly) approach could be `hana::size(hana::take_while(tup,hana::not_equal.to(hana::type_c)))`. Keep in mind that this does not return an `int_c<1>` but a `size_c<1>` (I think it's `integral_constant`). – llonesmiz Nov 29 '15 at 09:20

2 Answers2

6

Hana does not provide an algorithm to do this out-of-the-box. If it seems like a much desired feature, I could add such an algorithm fairly easily. It would probably fit well as part of the interface of any Iterable, since Iterables are those sequences for which indices are meaningful.

For the time being, I would go with something very close to what @cv_and_he proposed in his comment:

#include <boost/hana.hpp>
namespace hana = boost::hana;

template <typename Iterable, typename T>
constexpr auto index_of(Iterable const& iterable, T const& element) {
    auto size = decltype(hana::size(iterable)){};
    auto dropped = decltype(hana::size(
        hana::drop_while(iterable, hana::not_equal.to(element))
    )){};
    return size - dropped;
}

constexpr auto tuple = hana::make_tuple(hana::int_c<3>, hana::type_c<bool>);
constexpr auto index = index_of(tuple, hana::type_c<bool>);
static_assert(index == hana::size_c<1>, "");

int main() { }

A few notes about the above code. First, indices are required to be non-negative in Hana, so it is probably a good idea to use an unsigned type. Secondly, I'm using hana::drop_while instead of hana::take_while, because the former only requires an Iterable, while the latter requires a Sequence. While it may seem like I'm doing more work (computing the size twice), it turns out that computing the size of most sequences you'll encounter is very fast, so it's not really a concern. Finally, I'm enclosing the hana::size(hana::drop_while(...)) in decltype, which ensures that no work whatsoever will be done at runtime.

Louis Dionne
  • 3,104
  • 1
  • 15
  • 35
  • Out of curiosity, what is the purpose of doing so many decltype( ... ){} -- aren't these empty types so it doesn't matter if they are default constructed? – Russell Greene Nov 30 '15 at 01:08
  • 2
    `hana::drop_while` will create a new sequence at runtime if you don't enclose it in `decltype`. This might not be a problem for your current use case, but consider what happens if your `iterable` contains `std::string`s, for example. Then, running `hana::drop_while` will create copies of those strings, which is completely useless. Enclosing the whole thing in `decltype` ensures that no runtime work is done. – Louis Dionne Nov 30 '15 at 02:14
  • 4
    I'd love to see `index_of` or something similar in Hana. – krzaq Jan 04 '17 at 17:53
0

How about using boost::detail::index_if:

#include <boost/hana.hpp>

template <typename Haystack, typename Needle>
constexpr auto get_index_of_first_matching(Haystack&&, Needle&& n)
{
  using Pred = decltype(boost::hana::equal.to(n));
  using Pack = typename boost::hana::detail::make_pack<Haystack>::type;
  constexpr auto index = boost::hana::detail::index_if<Pred, Pack>::value;
  return boost::hana::int_c<index>;
}

int main()
{
  using namespace boost::hana::literals;
  constexpr auto tup = boost::hana::make_tuple(3_c, boost::hana::type_c<bool>);
  constexpr auto index = get_index_of_first_matching(tup, boost::hana::type_c<bool>);
  static_assert(index == boost::hana::int_c<1>, "index is wrong");
  return 0;
}
m.s.
  • 16,063
  • 7
  • 53
  • 88
  • 1
    Using the `detail` namespace is living dangerously, since I reserve the right to change anything in there without any regards to what user code it may break. [Edit: Apart from that, this solution looks fine for `hana::tuple`s (but not for general `hana::Sequence`s).] – Louis Dionne Nov 29 '15 at 16:42
  • @LouisDionne Is there any other suggestion that you have? I thought about the `index_if` idea, but maybe there is a better one? I come from `boost::mpl` world where `find` returns an iterator, which is easy enough to do a `distance` on. Any suggestions ? – Russell Greene Nov 29 '15 at 18:15
  • @LouisDionne sure, one could just copy the current implementation of those `detail` implementations; this was merely a shortcut to show a possible implementation – m.s. Nov 29 '15 at 20:17