Maik Klein
Maik Klein

Reputation: 16158

Filtering a tuple in boost hana

template<class... Ts, class T>
constexpr auto contains(T&&){
  auto types = hana::to<hana::tuple_tag>(hana::tuple_t<Ts...>);
  return hana::bool_c<hana::find(types, hana::type_c<T>) != hana::nothing>;
}

auto ht = hana::make_tuple(1,2,3,'c');

auto ht1 = hana::filter(ht, [](auto t){
  return contains<int,float,double>(t);
});
//prints 0
std::cout << hana::size(ht1) << std::endl;

I am not sure if I am using boost hana correctly but contains seems to work.

std::cout << contains<int,float,double>(5) << std::endl;   // 1
std::cout << contains<int,float,double>('c') << std::endl; // 0
std::cout << contains<int,float,double>(5.0f) << std::endl; // 1

Why is the size of ht1 0?

Upvotes: 3

Views: 786

Answers (3)

Louis Dionne
Louis Dionne

Reputation: 3093

The problem here actually has nothing to do with Hana, it has to do with the way universal references are deduced. Just to clear up things, hana::type_c<T> == hana::type_c<U> is precisely equivalent to std::is_same<T, U>{}. There is no reference or cv-qualifier removing when comparing hana::types. You can look at various articles (like this or this) for these rules.

Now, let me go through your code and modify some things, with comments. First,

  auto types = hana::to<hana::tuple_tag>(hana::tuple_t<Ts...>);

is redundant, because you're already creating a hana::tuple with hana::tuple_t. Hence, hana::tuple_t<T...> only is sufficient. Secondly, there's this line:

  return hana::bool_c<hana::find(types, hana::type_c<T>) != hana::nothing>;

Instead of checking hana::find(...) != hana::nothing, I would instead use hana::contains, which expresses your intent better and might be more optimized too. In general, and especially with a metaprogramming library with Hana, don't try to reason as to what will be faster. Just state your intent as clearly as possible and hope for me to do my job properly on the implementation side :-). Hence, you'll end up with

return hana::bool_c<hana::contains(types, hana::type_c<T>)>;

Now, that hana::bool_c<...> really is redundant, because hana::contains already returns a boolean integral_constant. Hence, the above is equivalent to the simpler

return hana::contains(types, hana::type_c<T>);

Finally, putting all the bits together and simplifying, you get

template<class... Ts, class T>
constexpr auto contains(T&&){
  return hana::contains(hana::tuple_t<Ts...>, hana::type_c<T>);
}

I'm personally not a fan of taking the T&& as an argument, when all you want is actually the type of that object. Indeed, that forces you to actually provide an object to contains function, which might be unwieldy in some circumstances (what if you don't have an object around?). Furthermore, it can be confusing to be comparing values with types:

contains<int, char, double>(3.5) // wtf, 3.5 is not in [int, char, double]!

Instead, I would write the following if it was my own code:

template<class... Ts, class T>
constexpr auto contains(T type){
  return hana::contains(hana::tuple_t<Ts...>, type);
}

// and then use it like
contains<int, char, double>(hana::type_c<double>)

But that is part of the interface of your function, and I guess you are a better judge than I to know what your needs are in terms of interface.

Upvotes: 5

Jason Rice
Jason Rice

Reputation: 1696

Just to add to your answer, your ht1 was calling contains with t an lvalue. The following demonstrates T&& in the case of passing an rvalue and an lvalue:

#include<boost/hana.hpp>

namespace hana = boost::hana;

template<class T>
void test1(T&&) {
  static_assert(hana::type_c<T> == hana::type_c<int>, "");
}

int main() {
  static_assert(hana::type_c<int> != hana::type_c<int&&>, "");
  test1(5);
  int x = 5;
  test1(x); //fails
}

clang output:

main.cpp:7:3: error: static_assert failed ""
  static_assert(hana::type_c<T> == hana::type_c<int>, "");
  ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:14:3: note: in instantiation of function template specialization 'test1<int &>' requested here
  test1(x); //fails
  ^
1 error generated.

Upvotes: 2

Maik Klein
Maik Klein

Reputation: 16158

The problem is the T&&, I think it deduces the type to be of type T& which means that hana::type_c<T> != hana::type_c<T&> The fix is to leave the && because they are unnecessary.

template<class... Ts, class T>
constexpr auto contains(T){
  auto types = hana::tuple_t<Ts...>;
  return hana::find(types, hana::type_c<T>) != hana::nothing;
}

Upvotes: 4

Related Questions