Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write the best possible is_callable trait for templated operator()

Tags:

c++

c++11

sfinae

I have is_callable trait defined like this:

#ifndef IS_CALLABLE_HPP
#define IS_CALLABLE_HPP

#include <type_traits>

namespace is_callable_detail
{
    struct no   {};
    struct yes  { no x[2]; };

    template<bool CallableArgs, typename Callable, typename ReturnType, typename ...Args>
    struct check_return
    {
        static const bool value = std::is_convertible<decltype(std::declval<Callable>()(std::declval<Args>()...)), ReturnType>::value;
    };

    template<typename Callable, typename ReturnType, typename ...Args>
    struct check_return<false, Callable, ReturnType, Args...>
    {
        static const bool value = false;
    };
}

template<typename Callable, typename Function>
struct is_callable;

template<typename Callable, typename ReturnType, typename ...Args>
struct is_callable<Callable, ReturnType(Args...)>
{
    private:
        template<typename T>
        static is_callable_detail::yes check(decltype(std::declval<T>()(std::declval<Args>()...)) *);
        template<typename T>
        static is_callable_detail::no  check(...);

        static const bool value_args = sizeof(check<Callable>(nullptr)) == sizeof(is_callable_detail::yes);
        static const bool value_return = is_callable_detail::check_return<value_args, Callable, ReturnType, Args...>::value;
    public:
        static const bool value = value_args && value_return;
};

#endif // IS_CALLABLE_HPP

My question is how to detect templated operator() which doesn't have arguments and has only return type T

template<typename T>
T operator()()
{
  // ...
}

or

template<typename T, typename U>
auto operator()() -> decltype(std::declval<T>() + std::declval<U>())
{
  // ...
}

I know that this situations are rare, but I wanted to ask is there any way to detect presence of templated operator() with no arguments and with one or more template arguments.

like image 915
Goran Aranđelović Avatar asked Feb 10 '12 16:02

Goran Aranđelović


1 Answers

If you know in advance operator() is not going to be overloaded, you can try to take its address. If operator() is possibly overloaded, then a positive result would mean that there is an operator() present but a negative result would mean that either no operator() is present, or at least two overloads are.

Notice that a template will (as expected) bring several overloads of operator(). However, if you do know the number of template parameters that are not defaulted you can try taking the address of operator()<T> (for some type T that hopefully won't trigger SFINAE).

As a final note, I'd suggest not trying to spend too much time trying to inspect functors (or member functions, for the same reasons) without knowing what arguments to pass, just like what you already have. C++11 makes it very easy to write and use generic code that functions at the expression level.

like image 55
Luc Danton Avatar answered Sep 21 '22 20:09

Luc Danton