2

Can I write a trait metafunction to figure out if a type is a functor or not? There are tons of code which can check functor using SFINAE of decltype(&T::operator()), for instance,

template<class T>
struct is_functor {
    template<class F>
    static auto test(decltype(&F::operator())) -> std::true_type;
    template<class F>
    static auto test(...) -> std::false_type;
    static constexpr bool value = decltype(test<T>(0))::value;
};

However, this doesn't work for generic lambda because generic lambda 's operator() is a template function.

There are some code for limited case for generic lambda version which make some constraints on the argument type of generic lambda. For instance, an answer here(https://stackoverflow.com/a/5117641/2580815) won't work if lambda expression contains any expression which cannot be valid for int type such as member access operation.

I don't need any generality for arity. In fact, I only need to know a type can be a functor which accepts only one parameter.

How can I implement my is_functor?

Use case:

I'm trying to validate if given parameter is a functor or not for a template function, that is, I want to like some overloaded tempalte functions, for instance:

template<class F, class = enable_if_t<is_functor<std::decay_t<F>>::value>>
auto make_func(F &&f) { return std::forward<F>(f); }
template<class F, class = enable_if_t<!is_functor<std::decay_t<F>>::value>>
auto make_func(F &&f) { return [f=std::forward<F>(f)] (auto&&) { return f; }; }
Community
  • 1
  • 1
slyx
  • 2,063
  • 1
  • 19
  • 28
  • If your compiler is new enough to support some C++17 features, you could perhaps use [`std::is_callable`](http://en.cppreference.com/w/cpp/types/is_callable). – Some programmer dude Feb 17 '17 at 09:40
  • @Someprogrammerdude it only works for known argument types. – n. m. could be an AI Feb 17 '17 at 09:46
  • @Someprogrammerdude Nope, `std::is_callable` is totally different thing. It requires a functor type `F` and arugment types `Args...` simultaneously to resolve ambiguous overloaded functions. I don't care about ambiguity of arguments. I only care about existence of operator(). – slyx Feb 17 '17 at 09:47
  • 2
    Why would you need to know whether `operator()` exists and can accept *some unknown type of argument*? – n. m. could be an AI Feb 17 '17 at 09:48
  • @n.m. I've added an example of use case. – slyx Feb 17 '17 at 09:58
  • I don't understand your use case at all. Can you explain what it does in plain English? – n. m. could be an AI Feb 17 '17 at 11:19
  • @n.m. I'd like to write a template function so called 'make_func' here which always returns a functor. If given paramter is a functor, make_func will just return the paramter. Otherwise, make_func will return a lambda with returns given parameter. – slyx Feb 28 '17 at 09:00
  • So what will you do with the returned value? You know have a functor but *you don't know how to call it*, there's no information about its arity or argument types. – n. m. could be an AI Feb 28 '17 at 09:13

2 Answers2

1

There is no proper way of doing this (at least until we get static reflection). The best you can do is check that an object is callable with a certain degree of confidence:

  1. Try getting its operator() address. If it fails, then the object may either be non-callable or its operator() could be overloaded/templated.

  2. Try calling the object with a dummy any_type instance that provides an interface for commonly used function. This might help you deduce its arity.

  3. If everything fails, force the user to somehow help the arity deduction or manually specify the arity.

One way you could approach this is by having a deduced_arity set of tags:

namespace deduced_arity
{
    template <std::size_t TS>
    struct deducible_t : std::integral_constant<std::size_t, TS>
    {
    };

    struct undeducible_t
    {
    };

    constexpr undeducible_t undeducible{};
    constexpr deducible_t<1> unary{};
}

You will also need some sort of function_traits implementation that will statically tell you the exact arity of a function object. This can be found in Boost.

Then you also need an implementation of any_type.

Afterwards, you can use something like the following type trait to check whether or not a function object may be overloaded, using the detection idiom:

template <typename T>
using is_not_overloaded_impl = decltype(&T::operator());

template <typename T>
using is_not_overloaded =
    std::experimental::is_detected<is_not_overloaded_impl, T>;

Then you can use a chain of if constexpr(...) (or any other compile-time branching mechanism) to make a good guess - example:

if constexpr(is_not_overloaded<T>{})
{
    // use `function_traits` here
}
else if constexpr(std::is_callable<T(any_type)>{})
{
    return deduced_arity::unary;
}
else if constexpr(/* user manually marked arity */)
{
    /* deal with user-defined deduction helpers */
}
else
{
    return deduced_arity::undeducible;
}
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
1

Reusing most of the code from my answer to a twin question (that one about finding a function's arity):

template <class, std::size_t N, class = std::make_index_sequence<N>, class = void_t<>>
struct CanCall : std::false_type { };

template <class F, std::size_t N, std::size_t... Idx>
struct CanCall<
    F, N,
    std::index_sequence<Idx...>,
    void_t<decltype(std::declval<F>()((Idx, std::declval<Any const&&>())...))>
> : std::true_type { };

CanCall<F, N> will return whether F is callable with N parameters of arbitrary type. The Any helper type has templated implicit conversion operators that allows it to morph into any desired parameter type.

Then, for your specific use-case (unary functors):

template <class F>
struct IsUnaryFunctor : CanCall<F, 1u> { };

See it live on Coliru

Community
  • 1
  • 1
Quentin
  • 62,093
  • 7
  • 131
  • 191
  • 1
    Your answer won't work for `auto l3 = [](auto&& v) { return v.a_member; };`. – slyx Feb 17 '17 at 10:00
  • @xylosper mmh, I see. I don't think that's fixable with this approach, since deducing the return type requires instantiating the lambda's body... – Quentin Feb 17 '17 at 10:33