Search code examples
c++c++20c++-conceptsboost-hana

How to formulate as a concept that elements of a boost::hana::Sequence all satisfy a concept?


Assume I have the following concept:

#include <boost/hana.hpp>
#include <iostream>
namespace hana = boost::hana;
   
// this is only for consistency
template <typename S> concept hana_sequence = hana::Sequence<S>::value;

template <typename T> concept callable_with_int = requires(T&& t, int i) { t(i); };

I would like to define functions that work on hana_sequences whose elements all satisfy this concept, like e.g.:

void applyCallable(const sequence_of_callables auto& ha, int i)
{
  hana::for_each(ha, [&] (auto e) {std::cerr<<"inside apply – "; e(i);});
}

int main()
{
  applyCallable(hana::make_tuple(
    [] (int i) {std::cerr<<"inside lambda0 "<<2*i<<"!\n";},
    [] (int i) {std::cerr<<"inside lambda1 "<<i<<"!\n";}/*,
    [] () {std::cerr<<"inside lambda2!\n";}*/ // this would spoil compilation, as expected
  ),4);
}

My problem is how to define sequence_of_callables as a C++20 concept. After some tribulations, I came up with the following solution:

// spurious helper needed?
template <typename T> constexpr bool sequenceOfCallablesHelper = false;

template <typename... T> constexpr bool sequenceOfCallablesHelper<hana::tuple<T...>>
  = hana::fold(hana::tuple_t<T...>, true, [] (bool s, auto element) {
    return s && callable_with_int<typename decltype(element)::type>;});

template <typename S>
concept sequence_of_callables = ( hana_sequence<S> && sequenceOfCallablesHelper<S> );

This works as expected, but the need for the helper and its specialization is a bit ugly.

Can this be done more directly?


Solution

  • You can first use hana::transform to convert S from hana::tuple<T...> to hana::tuple<hana::type<T>...>, and then use hana::all_of to check whether each type satisfies the callable_with_int concept:

    #include <boost/hana.hpp>
    
    namespace hana = boost::hana;
    
    // this is only for consistency
    template <typename S> 
    concept hana_sequence = hana::Sequence<S>::value;
    
    template <typename T> 
    concept callable_with_int = requires(T&& t, int i) { t(i); };
    
    template <typename S>
    concept sequence_of_callables = hana_sequence<S> && 
      !!hana::all_of(
        decltype(hana::transform(std::declval<S>(), hana::typeid_)){},
        []<class T>(T) { return callable_with_int<typename T::type>; });
    

    demo