Search code examples
c++templatesc++14variadic-templatesstd-function

Create a std::function type with limited arguments


Given the type of a callable function C, I want to get at compile time a std::function; the type of which:

  • has the same return type of function C
  • the argument types are the first N argument types of function C

This means that, for a given type void(int, char, double) and a given N, the type of the function is:

  • N = 1 => result type: std::function<void(int)>
  • N = 2 => result type: std::function<void(int, char)>
  • N = 3 => result type: std::function<void(int, char, double)>
  • N > 3 => compile time error

Example:

template<std::size_t N, typename R, typename... A>
constexpr auto get() {
    return /*(magically somehow)*/ std::function<R(FirstNFromA...)>
}

template<std::size_t N, typename R, typename... A>
struct S {
    using func = decltype(get<N, R, A...>());
};

Solution

  • It follows a possible solution:

    #include <tuple>
    #include <utility>
    #include <functional>
    #include <type_traits>
    
    template<
        typename R,
        typename... A,
        std::size_t... I,
        std::enable_if_t<(sizeof...(I)<=sizeof...(A))>* = nullptr
    > constexpr auto
    get(std::integer_sequence<std::size_t, I...>) {
        return std::function<R(std::tuple_element_t<I, std::tuple<A...>>...)>{};
    }
    
    template<std::size_t, typename>
    struct FuncType;
    
    template<std::size_t N, typename R, typename... A>
    struct FuncType<N, R(A...)> {
        using Type = decltype(get<R, A...>(std::make_index_sequence<N>{}));
    };
    
    int main() {
        static_assert(
            std::is_same<
                FuncType<2, void(int, char, double, int)>::Type,
                std::function<void(int, char)>
            >::value,
            "!"
        );
    }
    

    The basic idea is to use a tuple and those utilities that are part of the Standard template Library (as an example, std::tuple_element), mix them with a pack expansion placed in the right place and that's all.
    To do that, I used a constexpr function that returns an empty std::function object of the given type. Then the type of the function is taken by means of a decltype and exported as a type of the FuncType object using an alias.
    Everything takes place at compile time.
    In order to deduce the right type for that function, I used a well known pattern that involves a std::integer_sequence and actually unpack the types of a tuple by expanding the indexes.