Jason R
Jason R

Reputation: 11758

How can I use Boost.Hana to determine whether a functor has a call operator that can be invoked with a particular template argument?

In my application, I want to determine at compile time whether an arbitrary functor type Func has a nullary call operator that can be invoked with a given explicit template argument T. Based on a previous SO answer that I found, I came up with the following:

#include <boost/hana.hpp>
#include <iostream>
#include <type_traits>

namespace hana = boost::hana;

namespace detail 
{
    template <typename T>
    auto can_call = hana::is_valid([](auto &&f) -> 
        decltype(f.template operator()<T>()) { });
}

template <typename Func, typename T>
constexpr auto can_call() ->
    decltype(detail::can_call<typename std::remove_reference<T>::type>(
        std::declval<Func>())) { return {}; }

struct foo
{
    template <typename T, typename = 
        std::enable_if_t<!std::is_same<T, char>::value>>
    void operator()() const { }
};

int main()
{
    std::cout << "char: " << can_call<foo, char>() << std::endl;
    std::cout << "int: " << can_call<foo, int>() << std::endl;
}

I would expect this example to print out:

char: 0
int: 1

Since the char template argument type is explicitly enable_if-ed out in foo. I've tried the following compilers:

It seems to not like my use of hana::is_valid() to determine whether the specified operator exists. However, I think the way I'm using it is consistent with its intended use.

Is this a bug in gcc, a more lenient implementation in contemporary clang versions, or did I implement this type of check incorrectly? It seems like this is definitely within Hana's wheelhouse; I'm just trying to wrap my head around its new model of constexpr metaprogramming.

Upvotes: 2

Views: 320

Answers (1)

Jason Rice
Jason Rice

Reputation: 1696

Here is a workaround that uses a struct "functor" instead of a lambda and an extra layer of indirection for the type of the is_valid instance to appease gcc.

namespace detail 
{
    template <typename T>
    struct check_can_call { 
      template <typename F>
      constexpr auto operator()(F&& f) -> 
        decltype(f.template operator()<T>()) { }
    };

    template <typename T>
    using is_call_valid = decltype(hana::is_valid(check_can_call<T>{}));

    template <typename T>
    constexpr is_call_valid<T> can_call{};
}

Upvotes: 2

Related Questions