Search code examples
c++functiontemplatesvariadic-templatesparameter-pack

c++ Extract parameter type list from function pointer


Im trying to get the argument types from a function pointer

This should be the working end product

std::function<void(TestAppObject*, MemberFuncArgs<decltype(&TestAppObject::TestMethod)>::InputArgs)> func = &TestAppObject::TestMethod;

Current MemberFuncArgs class

template<typename T>
struct MemberFuncArgs;

template<typename RT, typename Owner, typename ...Args>
struct MemberFuncArgs<RT(Owner::*)(Args...)>
{
    static const size_t ArgCount = sizeof...(Args);
    typedef RT ReturnType;
    typedef Args InputArgs;
};

Compiler throws the error 'Args': parameter pack must be expanded in this context.

I just need a way to extract the Args... type from the function pointer, its probably just a syntax issue that im too dumb to see...


Solution

  • The compiler say the you can't define a single type InputArgs

    typedef Args InputArgs;
    

    given that Args is a variadic list.

    Maybe you can define a type base on a tuple

    using InArgsTuple = std::tuple<Args...>;
    

    so you can extract the single types in Args... using std::tuple_element

    So, with a little template meta-programming, you should be able to write something as

    using TplT = MemberFuncArgs<decltype(&TestAppObject::TestMethod)>::InArgsTuple;
    
    std::function<void(TestAppObject*, typename std::tuple_element<Is, TplT>::type ...)>
       func = &TestAppObject::TestMethod;
    

    assuming that Is... is a variadic sequence of template integer values from zero to sizeof...(Args)-1.

    The following is a full compiling C++20 example

    #include <tuple>
    #include <functional>
    
    struct TestAppObject
    {
      int TestMethod (char, short, int, long, long long)
      { return 0; }
    };
    
    template <typename T>
    struct MemberFuncArgs;
    
    template <typename RT, typename Owner, typename ... Args>
    struct MemberFuncArgs<RT(Owner::*)(Args...)>
    {
      static constexpr std::size_t ArgCount = sizeof...(Args);
    
      using ReturnType  = RT;
      using InArgsTuple = std::tuple<Args...>;
    };
        
    int main()
    {
      using MFA  = MemberFuncArgs<decltype(&TestAppObject::TestMethod)>;
      using FunT = decltype([]<std::size_t ... Is>(std::index_sequence<Is...>)
         -> std::function<void(TestAppObject*,
               typename std::tuple_element<Is, MFA::InArgsTuple>::type ...)>
         { return {}; }
         (std::make_index_sequence<MFA::ArgCount>{}));
    
      FunT  func = &TestAppObject::TestMethod;
    }
    

    If you can't use C++20 (so no template lambda and no lambda in not evaluated context) you can substitute the lambda with a traditional template function, only declared (because is used only inside decltype().

    The following is a full compiling C++14/C++17 example. #include #include

    struct TestAppObject
    {
      int TestMethod (char, short, int, long, long long)
      { return 0; }
    };
    
    template <typename T>
    struct MemberFuncArgs;
    
    template <typename RT, typename Owner, typename ... Args>
    struct MemberFuncArgs<RT(Owner::*)(Args...)>
    {
      static constexpr std::size_t ArgCount = sizeof...(Args);
    
      using ReturnType  = RT;
      using InArgsTuple = std::tuple<Args...>;
    };
    
    template <typename MFA, std::size_t ... Is>
    std::function<void(TestAppObject*,
       typename std::tuple_element<Is, typename MFA::InArgsTuple>::type ...)>
          extra_function (std::index_sequence<Is...>);
    
    int main()
    {
      using MFA  = MemberFuncArgs<decltype(&TestAppObject::TestMethod)>;
      using FunT = decltype(extra_function<MFA>
                               (std::make_index_sequence<MFA::ArgCount>{}));
    
      FunT  func = &TestAppObject::TestMethod;
    }