k06a
k06a

Reputation: 18745

Determining number of arguments of C++ template functor

Suppose we have:

template<typename F, typename T1, typename T2>
void my_magic_method(F func, T1 t1, T2 t2)
{
    if (???)
        func(t1);
    else
        func(t1,t2);
}

What can help me to determine:

  1. Number of arguments

  2. Maybe types of each argument

  3. Type of return value

I cant use variadic templates because of MSVS 2010...

UPDATE

My first solution:

template<typename F>
auto my_magic_func(F f) -> decltype(f(1))
{
    return f(1);
}

template<typename F>
auto my_magic_func(F f, void * fake = NULL) -> decltype(f(2,3))
{
    return f(2,3);
}

int main()
{
    auto x1 = my_magic_func([](int a){ return a+100; });
    auto x2 = my_magic_func([](int a, int b){ return a*b; });
    // x1 == 1+100
    // x2 == 2*3
}

Thats my way of function-type overloading. It works, but maybe a better solutuion?

Upvotes: 1

Views: 494

Answers (2)

addaon
addaon

Reputation: 1152

Here's a few approaches; all assume C++11. Tested on clang++ 3.2 with -std=c++11.

//Taking a function pointer argument (template types inferred)

template <typename ret, typename ... Args>
constexpr int arg_count(ret (*f)(Args...)) {
    return sizeof...(Args);
}

//Taking a function type (or related) directly

template <typename T>
struct ArgCount {
    static const int value = 0;
};

template <typename Ret, typename ... Args>
struct ArgCount<Ret(Args...)> {
    static const int value = sizeof...(Args);
};

template <typename Ret, typename ... Args>
struct ArgCount<Ret(*)(Args...)> {
    static const int value = sizeof...(Args);
};

template <typename Ret, typename ... Args>
struct ArgCount<Ret(&)(Args...)> {
    static const int value = sizeof...(Args);
};

//Using the latter for dispatch

template <int N>
struct helper {
    template<typename F, typename T1, typename T2>
    static void call(F func, T1 t1, T2 t2);
};

template <>
struct helper<1> {
    template<typename F, typename T1, typename T2>
    static void call(F func, T1 t1, T2 t2) {
        func(t1);
    }
};

template <>
struct helper<2> {
    template<typename F, typename T1, typename T2>
    static void call(F func, T1 t1, T2 t2) {
        func(t1, t2);
    }
};

template<typename F, typename T1, typename T2>
void my_magic_method(F func, T1 t1, T2 t2)
{
    helper<ArgCount<F>::value>::call(func, t1, t2);
}

//Testing

#include <cstdio>

void a(int a, int b) { printf("%i\n", a + b); }
void b(int x) { printf("%i\n", x); }

int main() {
    printf("%i %i\n", arg_count(a), arg_count(b));
    printf("%i %i\n", ArgCount<decltype(a)>::value, ArgCount<decltype(b)>::value);
    my_magic_method(a, 1, 2);
    my_magic_method(b, 1, 2);
}

Upvotes: 0

ildjarn
ildjarn

Reputation: 62975

Not exactly what you asked for, but if I understand your intent correctly, in VC++ 2010 it's possible (but ugly) via simple overloading based on arity:

#include <utility>
#include <string>
#include <iostream>

template<typename F, typename T1>
auto my_magic_method(F&& func, T1&& t1) ->
    decltype(std::forward<F>(func)(std::forward<T1>(t1)))
{
    return std::forward<F>(func)(std::forward<T1>(t1));
}

template<typename F, typename T1, typename T2>
auto my_magic_method(F&& func, T1&& t1, T2&& t2) ->
    decltype(std::forward<F>(func)(std::forward<T1>(t1), std::forward<T2>(t2)))
{
    return std::forward<F>(func)(std::forward<T1>(t1), std::forward<T2>(t2));
}

struct string_to_float_functor
{
    float operator ()(std::string const& s) const
    {
        return std::stof(s);
    }
};

int main()
{
    auto a = my_magic_method([](std::string const& x) { return x + x; }, "foo");
    auto b = my_magic_method([](double x, int y) { return x * y; }, 21.5, 3);
    auto c = my_magic_method(string_to_float_functor(), "3.14159265");
    std::cout << a << '\n' << b << '\n' << c << '\n';
}

This supports unary and binary functors – continue the pattern and add overloads for other arities as needed.

Upvotes: 3

Related Questions