0

I'm trying to get a return type and a number of arguments for every function-like type (e.g functions, lambdas, comparators).

/**
 * @brief Get some info about any function-like object at compile-time.
 * 
 * @tparam F function template.
 */
template<typename F, typename = void> 
struct function_traits;

// Catch functions
template<typename R, typename ...Args>
struct function_traits<R (Args...)>
{ 
    using return_type = R;

    static constexpr std::size_t argc = sizeof...(Args);

    template<size_t i> 
    struct get_arg
    {
        using type = std::tuple_element_t<i, std::tuple<Args...>>;
    };

    template<size_t i>
    using get_arg_t = typename get_arg<i>::type;
};

// Catch function pointers
template<typename R, typename ...Args>
struct function_traits<R (*)(Args...)> : function_traits<R (Args...)> {};

// Catch member functions
template<typename R, typename C, typename ...Args>
struct function_traits<R (C::*)(Args...)> : function_traits<R (Args...)> {};

// Catch member const functions
template<typename R, typename C, typename ...Args>
struct function_traits<R (C::*)(Args...) const> : function_traits<R (C::*)(Args...)> {};

// Catch lambda and structs with one operator()
template<typename F> 
struct function_traits<F, void> : function_traits<decltype(&F::operator())> {};

This works for everything but std::bind because it has multiple operator() functions. How can I improve my code to get info from binds?

gavrilikhin.d
  • 554
  • 1
  • 7
  • 20
  • 5
    I would leverage `std::function` like I do [here](https://stackoverflow.com/questions/53673442/simplest-way-to-determine-return-type-of-function/53673648?) and let it figure it out for you. Then you can extract the information from the template parameters of the `std::function` oject – NathanOliver Jan 14 '21 at 16:40
  • 1
    The result type of `std::bind` has infinitely many `operator()`, with up to infinitely many parameters. Which one do you want? – Caleth Jan 14 '21 at 16:40
  • 1
    It doesn't really answer your question, but I think the best solution is to not use `std::bind`. Lambdas are the better choice, always. – super Jan 14 '21 at 16:40
  • Aside: shouldn't the `C`s be part of the arguments, or be mentioned somewhere? – Caleth Jan 14 '21 at 16:44
  • @Caleth, why? It's only used for auto deduction. – gavrilikhin.d Jan 14 '21 at 16:59
  • because you need a `C` to call that member function. E.g. overload resolution talks about the *implicit object argument* that is bound to `this` in the body – Caleth Jan 14 '21 at 17:08
  • @Caleth, but I never intend to call it. I only use other arguments. – gavrilikhin.d Jan 14 '21 at 18:15
  • @NathanOliver, is there really no way to get the number of std::bind arguments? – gavrilikhin.d Jan 15 '21 at 05:55
  • `std::bind`'s `operator()` is a variadic template. There isn't *the number* of arguments. You have to do some trickery to work out what the minimum number would be. – Caleth Jan 15 '21 at 11:48

1 Answers1

2

This works for everything but std::bind

You've found one of the (many) cases where there isn't a unique answer.

It will also fail for overloaded functions, transparent comparators, visitors and other types that can be called in multiple ways.

I think this is why there is no std::function_traits, and we instead have std::is_invocable et.al.

Caleth
  • 52,200
  • 2
  • 44
  • 75