Search code examples
c++templatestype-deduction

Deduce function object argument types


Given a set of argument types and a function object, the return type can be deduced with decltype. What about deducing argument types?

For a function pointer, both return and argument types can be deduced with a funny pattern matching syntax. As an example, here's a silly program that uses deduced argument types to print out a string describing the return and argument types of a function pointer.

#include <iostream>
#include <string>

using std::string;

template<class T>
string type_str();

template<>
string type_str<int>() { return "int"; }

template<>
string type_str<char>() { return "char"; }

string arg_types_str()
{
    return "";
}

template<class T>
string arg_types_str()
{
    return type_str<T>();
}

template<class T, class U, class... Args>
string arg_types_str()
{
    return type_str<T>() + ", " + arg_types_str<U, Args...>();
}

template<class R, class... Args>
void print_fptr_type(R (*fptr)(Args...))
{
    std::cout << type_str<R>() << " (*)(" << arg_types_str<Args...>() << ")" << std::endl;
}

int main()
{
    int (*fptr)(char, int);
    print_fptr_type(fptr);
}

Output:

int (*)(char, int)

Live demo.

Is it possible to write a program similar to the example that instead prints the return and argument types of function objects?

It seems like for a function object with exactly one operator(), the argument types can in principle be deduced unambiguously.


Solution

  • Yes, it can be done. The strategy is similar but with a function object it's a two stage process. First, a member function pointer to operator() is acquired and then a similar pattern matching method for type deduction can be used.

    Live demo.

    The key parts of the new program are:

    template<class T, class R, class... Args>
    void print_memfptr_types(R (T::*memfptr)(Args...))
    {
        std::cout << type_str<R>() << " (" << type_str<T>() << "*)(" << arg_types_str<Args...>() << ")" << std::endl;
    }
    
    template<class T, class R, class... Args>
    void print_cmemfptr_types(R (T::*memfptr)(Args...))
    {
        std::cout << type_str<R>() << " (const " << type_str<T>() << "*)(" << arg_types_str<Args...>() << ")" << std::endl;
    }
    
    template<class T>
    void print_fnc_obj_types(T&)
    {
        print_memfptr_types(&T::operator());
    }
    
    template<class T>
    void print_fnc_obj_types(const T&)
    {
        print_cmemfptr_types(&T::operator());
    }