开发者

c++0x: overloading on lambda arity

I'm trying to create a function which can be called with a lambda that takes either 0, 1 or 2 arguments. Since I need the code to work on both g++ 4.5 and vs2010(which doesn't support variadic templates or lambda conversions to function pointers) the only idea I've come up with is to choose which implementation to call based on arity. The below is my non working guess at how this should look. Is there any way to fix my code or is there a better way t开发者_开发知识库o do this in general?

#include <iostream>
#include <functional>
using namespace std;

template <class Func> struct arity;

template <class Func>
struct arity<Func()>{ static const int val = 0; };

template <class Func, class Arg1>
struct arity<Func(Arg1)>{ static const int val = 1; };

template <class Func, class Arg1, class Arg2>
struct arity<Func(Arg1,Arg2)>{ static const int val = 2; };

template<class F>
void bar(F f)
{
    cout << arity<F>::val << endl;
}

int main()
{
    bar([]{cout << "test" << endl;});
}


A lambda function is a class type with a single function call operator. You can thus detect the arity of that function call operator by taking its address and using overload resolution to select which function to call:

#include <iostream>

template<typename F,typename R>
void do_stuff(F& f,R (F::*mf)() const)
{
    (f.*mf)();
}

template<typename F,typename R,typename A1>
void do_stuff(F& f,R (F::*mf)(A1) const)
{
    (f.*mf)(99);
}

template<typename F,typename R,typename A1,typename A2>
void do_stuff(F& f,R (F::*mf)(A1,A2) const)
{
    (f.*mf)(42,123);
}

template<typename F>
void do_stuff(F f)
{
    do_stuff(f,&F::operator());
}

int main()
{
    do_stuff([]{std::cout<<"no args"<<std::endl;});
    do_stuff([](int a1){std::cout<<"1 args="<<a1<<std::endl;});
    do_stuff([](int a1,int a2){std::cout<<"2 args="<<a1<<","<<a2<<std::endl;});
}

Be careful though: this won't work with function types, or class types that have more than one function call operator, or non-const function call operators.


I thought the following would work but it doesn't, I'm posting it for two reasons.

  1. To save people the time if they had the same idea
  2. If someone knows why this doesn't work, I'm not 100% sure I understand (although I have my suspicions)

Code follows:

#include <iostream>
#include <functional>

template <typename Ret>
unsigned arity(std::function<Ret()>) { return 0; }

template <typename Ret, typename A1>
unsigned arity(std::function<Ret(A1)>) { return 1; }

template <typename Ret, typename A1, typename A2>
unsigned arity(std::function<Ret(A1, A2)>) { return 2; }

// rinse and repeat 

int main() 
{
    std::function<void(int)>  f = [](int i) { }; // this binds fine

    //  Error: no matching function for call to 'arity(main()::<lambda(int)>)'
    std::cout << arity([](int i) { }); 
} 


Compile time means of obtaining the arity of a function or a function object, including that of a lambda:

int main (int argc, char ** argv) {
    auto f0 = []() {};
    auto f1 = [](int) {};
    auto f2 = [](int, void *) {};

    std::cout << Arity<decltype(f0)>::value << std::endl; // 0
    std::cout << Arity<decltype(f1)>::value << std::endl; // 1
    std::cout << Arity<decltype(f2)>::value << std::endl; // 2

    std::cout << Arity<decltype(main)>::value << std::endl; // 2
}

template <typename Func>
class Arity {
private:
    struct Any {
        template <typename T>
        operator T ();
    };

    template <typename T>
    struct Id {
        typedef T type;
    };

    template <size_t N>
    struct Size {
        enum { value = N };
    };

    template <typename F>
    static Size<0> match (
        F f,
        decltype(f()) * = nullptr);

    template <typename F>
    static Size<1> match (
        F f,
        decltype(f(Any())) * = nullptr,
        decltype(f(Any())) * = nullptr);

    template <typename F>
    static Size<2> match (
        F f,
        decltype(f(Any(), Any())) * = nullptr,
        decltype(f(Any(), Any())) * = nullptr,
        decltype(f(Any(), Any())) * = nullptr);

public:
    enum { value = Id<decltype(match(static_cast<Func>(Any())))>::type::value };
};


This way works:

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

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

template<typename F>
auto call(F f, void * fake = 0, void * fake2 = 0) -> decltype(f(4,5,6))
{
    return f(4,5,6);
}

int main()
{
    auto x1 = call([](int a){ return a*10; });
    auto x2 = call([](int a, int b){ return a*b; });
    auto x3 = call([](int a, int b, int c){ return a*b*c; });
    // x1 == 1*10
    // x2 == 2*3
    // x3 == 4*5*6
}

It works for all callable types (lambdas, functors, etc)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜