Search code examples
c++templatesc++17template-meta-programmingtemplate-argument-deduction

C++: Extracting parameter type from static function in struct


Say I have something like this:

template<typename T, typename R>
struct MyStruct {
    static R myfunc(T);
};

struct MyStructInst: S<int, double> {
    static double myfunc(int i) { return i; }
};

Then later, I have a template that takes M, a type that I will assume has a static myfunc function with one parameter (e.g. MyStructInst). I want to extract the parameter type and the result type of myfunc:

template<typename M,
    typename ParamType = ???,
    typename ResultType = decltype(declval(M::myfunc))> // I think this works?
struct MyStruct2 {
    ...
};

What's the easiest way to get ParamType?


Solution

  • What about

    template <typename R, typename A0, typename ... As>
    constexpr A0 firstArg (R(*)(A0, As...));
    
    template <typename M,
              typename PT = decltype(firstArg(&M::myfunc)),
              typename RT = decltype(M::myfunc(std::declval<PT>()))>
    struct MyStruct2 
     { };
    

    ?

    I mean... if you declare (there is no needs to implement it because is used only inside a decltype(), so only the returned type is important) a function that receive a function type (i remember that a pointer to a static method is like a pointer to a traditional function) that receive one or more arguments and return the type of the first argument

    template <typename R, typename A0, typename ... As>
    constexpr A0 firstArg (R(*)(A0, As...));
    

    given a template typename M, you can obtain the first parameter of the myFunc() method (if any) with

    typename PT = decltype(firstArg(&M::myfunc))
    

    Given PT (the type of the first type parameter) you can obtain the returned type simply simulating (inside decltype(), using std::declval()) a call to myfunc() with a object of type PT

    typename RT = decltype(M::myfunc(std::declval<PT>()))
    

    The following is a full compiling example

    #include <string>
    #include <type_traits>
    
    template <typename T, typename R>
    struct MyStruct
     { static R myfunc(T); };
    
    struct MyStructInst 
     { static double myfunc(int i) { return i; } };
    
    template <typename R, typename A0, typename ... As>
    constexpr A0 firstArg (R(*)(A0, As...));
    
    template <typename M,
              typename PT = decltype(firstArg(&M::myfunc)),
              typename RT = decltype(M::myfunc(std::declval<PT>()))>
    struct MyStruct2 
     { using type1 = PT; using type2 = RT; };
    
    int main ()
     {
       static_assert( std::is_same<int,
          typename MyStruct2<MyStructInst>::type1>{}, "!");
       static_assert( std::is_same<double,
          typename MyStruct2<MyStructInst>::type2>{}, "!");
       static_assert( std::is_same<long,
          typename MyStruct2<MyStruct<long, std::string>>::type1>{}, "!");
       static_assert( std::is_same<std::string,
          typename MyStruct2<MyStruct<long, std::string>>::type2>{}, "!");
     }