0

I'm wondering if it's possible to "pattern matching" function and callable object to extract (at compile time) the number of arguments of a function. In other words I would like to create a meta-function called count_arg that return the number of arguments of the function passed.

Consider this code:

#include <iostream>
#include <functional>
#include <type_traits>

#define LIFT_FUN(f) decltype(f)


template<typename T>
struct count_arg : public count_arg<decltype(&T::operator())>
{
};


template<typename R, typename ...Args>
struct count_arg<R(Args...)>
{
    static const size_t value = sizeof...(Args);
};

template <typename C, typename R, typename... Args>
struct count_arg<R(C::*)(Args...) const>
{
    static const size_t value = sizeof...(Args);
};

template<typename R, typename ...Args>
struct count_arg<R(*)(Args...)>
{
    static const size_t value = sizeof...(Args);
};

template<typename R, typename ...Args>
struct count_arg<R(&)(Args...)>
{
    static const size_t value = sizeof...(Args);
};


template <typename T>
struct show_type;


template <typename F>
decltype(auto) flip(F f)
{
    return [f](auto a, auto b){
        return f(b, a);
    };
}


int minus(int a, int b, int c)
{
    return a - b + c;
}


struct Minus
{
    int operator()(int a, int b) const
    {
        return a - b;
    }
};


int main()
{
    int a{5}, b{3};
    auto g = [](int a, int b){
        return a - b;
    };

    auto fmin = flip(g);

    std::cout << minus(a, b, 0) << std::endl;
    std::cout << fmin(a, b) << std::endl;

    std::cout << "minus arity:" << count_arg<LIFT_FUN(minus)>::value << std::endl;
    std::cout << "    g arity:" << count_arg<LIFT_FUN(g)>::value << std::endl;
    std::cout << "Minus arity:" << count_arg<LIFT_FUN(Minus{})>::value << std::endl;
}

This example works, but if I try with fmin :

std::cout << " fmin arity:" << count_arg<LIFT_FUN(fmin)>::value << std::endl;

(using g++ 4.9.2: g++ flip.cpp -o flip -std=c++14) I'm getting this error:

flip.cpp: In instantiation of ‘struct count_arg<flip(F) [with F = main()::<lambda(int, int)>]::<lambda(auto:1, auto:2)> >’:
flip.cpp:83:61:   required from here
flip.cpp:9:8: error: decltype cannot resolve address of overloaded function
 struct count_arg : public count_arg<decltype(&T::operator())>
        ^
flip.cpp: In function ‘int main()’:
flip.cpp:83:36: error: ‘value’ is not a member of ‘count_arg<flip(F) [with F = main()::<lambda(int, int)>]::<lambda(auto:1, auto:2)> >’
     std::cout << " fmin arity:" << count_arg<LIFT_FUN(fmin)>::value << std::endl;

Any idea?

EDIT

Following the suggestion of Vittorio, I've tryed the get_arity implementation (Get function arity from template parameter):

std::cout << "   g arity:" << get_arity<decltype(g)>::value << std::endl;
std::cout << "fmin arity:" << get_arity<decltype(fmin)>::value << std::endl;

But I still to have problem with fmin. If I change flip in this way (the returning lambda is not more generic):

template <typename F>
decltype(auto) flip(F f)
{
    return [f](int a, int b){
        return f(b, a);
    };
}

my code compiles ...

So my problem in now that I have to find a way to extract the number og arguments from a generic lambdas. Any idea?

Community
  • 1
  • 1
Gian Lorenzo Meocci
  • 1,136
  • 2
  • 14
  • 23
  • 4
    What's your desired behavior for overloaded functions and/or variadic template functions? – Vittorio Romeo Dec 27 '15 at 14:59
  • I' trying to understand why this: auto fmin = flip(g); has a different behaviour of g – Gian Lorenzo Meocci Dec 27 '15 at 15:04
  • 1
    You'll be unhappy to know that you'll need 48 specializations as of C++17 just to cover regular functions (`Ret(Args...)`). That includes `const`/`volatile`/both (x3), ellipses for C variadics (x2), reference qualifiers (x4), and newly `noexcept` (x2). Then you have to deal with member function pointers, pointers to data members, and functors. – chris Dec 27 '15 at 15:08
  • 2
    @GianLorenzoMeocci: flip retuns a generic C++14 lambda - its operator() is a template function. You need to explicitly instantiate it to use count_arg. – Vittorio Romeo Dec 27 '15 at 15:09
  • Hi @VittorioRomeo thanks for the suggestions! If I force the type of parameters inside the flip function works! – Gian Lorenzo Meocci Dec 27 '15 at 15:15
  • @chris You swapped 'x3' and 'x4' :) Also, regular functions just need 4 (ellipsis/noexcept); the 48 is for member functions (or really pointers to them) – T.C. Dec 27 '15 at 15:18
  • Hi @chris any suggestion how to do it in my exaple? – Gian Lorenzo Meocci Dec 27 '15 at 15:19
  • @T.C., Shoot, I did swap those, oops. And regular functions is not really a great term for it. I mean what the new proposal had a name for (degenerate functions?) as in the `Ret(Args...)` syntax that can actually still be `Ret(Args...) const` or `Ret(Args...) &` etc. All of the forms that `std::is_function` has to support, which can be used in the implementation of other things that deal with callables (e.g., `std::is_member_function_pointer` can be based on `T Class::*` where `std::is_function_v`). – chris Dec 27 '15 at 15:27
  • @GianLorenzoMeocci, I don't think you're going to have too much luck with existing C++. There have definitely been proposals for making it possible to lift overloaded functions, as well as one that would let you use `decltype` with a lambda wrapper around the function. For handling all of the different types of functions, you're going to have to redo the work or use someone else's library or something because nothing that exists exposes `sizeof...(Args)`. – chris Dec 27 '15 at 15:33
  • @GianLorenzoMeocci: You should ask a **new, more specific question** about the problem with determining number of arguments of overloaded functions and function templates. – Cheers and hth. - Alf Dec 27 '15 at 15:40
  • ok @Cheersandhth.-Alf I'll try to reformulate my question. – Gian Lorenzo Meocci Dec 27 '15 at 15:41

0 Answers0