Search code examples
c++17variadic-templates

Use variadic args to call subsequent method


Assume I have function that accept 1 callable F and variadic list of arguments:

template <class F, class ...Args>
void combine(F& f, Args&& ... args)

F may have any number of arguments from 1 to sizeof...(Args). So I need using c++17 to invoke F and after that just put not used args to std::cout like that

{
    std::invoke(f, [some way to restrict args]);
    std::cout << [The rest of args] << "\n";
}

    

Solution

  • The great problem is detect the number of arguments of F.

    As pointed by Igor Tandetnik in a comment, this isn't possible in general, because the f func can be variadic, of a lambda with a template operator(), or a instance of a class/struct with multiple operator().

    Anyway... in some cases you can detect the number of the arguments with something as follows

    template <typename T>
    struct num_args 
       : public num_args<decltype(&T::operator())>
     { };
     
    template <typename R, typename ... Args>
    struct num_args<R(*)(Args...)>
       : public std::integral_constant<std::size_t, sizeof...(Args)>
     { };
    
    // other specialization follows
    

    At this point, you can write combine() to call an helper function with an appropriate couple of indexes and a tuple of arguments

    template <typename F, typename ... Args>
    void combine (F & func, Args && ... as)
     { 
       constexpr auto n1 { num_args<F>::value };
       constexpr auto n2 { sizeof...(Args) - n1 };
    
       combine_helper(std::make_index_sequence<n1>{},
                      std::make_index_sequence<n2>{},
                      func,
                      std::forward_as_tuple(std::forward<Args>(as)...));
     }
    

    and the helper function can be simply as follows

    template <std::size_t ... Is1, std::size_t ... Is2, typename F, typename T>
    void combine_helper (std::index_sequence<Is1...>,
                         std::index_sequence<Is2...>,
                         F & func, T && t)
     {
       func(std::get<Is1>(std::forward<T>(t))...);
       (std::cout << ... << std::get<sizeof...(Is1)+Is2>(std::forward<T>(t)))
          << '\n';
     }
    

    The following is a full compiling C++17 example

    #include <iostream>
    #include <utility>
    #include <tuple>
    
    template <typename T>
    struct num_args 
       : public num_args<decltype(&T::operator())>
     { };
     
    template <typename R, typename ... Args>
    struct num_args<R(*)(Args...)>
       : public std::integral_constant<std::size_t, sizeof...(Args)>
     { };
    
    template <typename R, typename ... Args>
    struct num_args<R(Args...)>
       : public std::integral_constant<std::size_t, sizeof...(Args)>
     { };
    
    template <typename R, typename C, typename ... Args>
    struct num_args<R(C::*)(Args...)>
       : public std::integral_constant<std::size_t, sizeof...(Args)>
     { };
    
    template <typename R, typename C, typename ... Args>
    struct num_args<R(C::*)(Args...) const>
       : public std::integral_constant<std::size_t, sizeof...(Args)>
     { };
    
    template <std::size_t ... Is1, std::size_t ... Is2, typename F, typename T>
    void combine_helper (std::index_sequence<Is1...>,
                         std::index_sequence<Is2...>,
                         F & func, T && t)
     {
       func(std::get<Is1>(std::forward<T>(t))...);
       (std::cout << ... << std::get<sizeof...(Is1)+Is2>(std::forward<T>(t)))
          << '\n';
     }
    
    template <typename F, typename ... Args>
    void combine (F & func, Args && ... as)
     { 
       constexpr auto n1 { num_args<F>::value };
       constexpr auto n2 { sizeof...(Args) - n1 };
    
       combine_helper(std::make_index_sequence<n1>{},
                      std::make_index_sequence<n2>{},
                      func,
                      std::forward_as_tuple(std::forward<Args>(as)...));
     }
    
    
    void func_1 (int a, int b, int c)
     { std::cout << "the product is: " << a*b*c << '\n'; }
    
    int main()
     {
       auto extra  { 100 };
       auto func_2 { [&](int a, int b, int c, int d)
        { std::cout << "the extra sum is: " << extra+a+b+c+d << '\n'; } };
    
       combine(func_1, 1, 2, 3, 4, 5, 6);
       combine(func_2, 1, 2, 3, 4, 5, 6);
     }