Overloading lambda's in C++11

Door .oisyn op zaterdag 2 februari 2013 01:56 - Reacties (3)
Categorie: Random code farts, Views: 2.936

Een C++ proposal dat lambda expressions generiek maakt (ofwel, templated arguments), N3418, bespreekt toevallig ook een techniek om lambda's te overloaden die momenteel in C++11 al werkt en die ik jullie niet wilde laten ontgaan. Leipe shit, ouwe!


C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
template<class F1, class F2>
struct overloaded : F1, F2
{
    overloaded(F1 x1, F2 x2) : F1(x1), F2(x2) {}
    using F1::operator();
    using F2::operator();
};
 
template<class F1, class F2>
overloaded<F1, F2> overload(F1 f1, F2 f2)
{
    return overloaded<F1, F2>(f1, f2);
}

int main()
{
    auto f = overload
    (
        [](int i) { std::cout << "int" << std::endl; }
        [](float f) { std::cout << "float" << std::endl; }
    );

    f(34);
    f(34.f);
}



Lambda's zijn stiekem gewoon classes met een operator(). Je kunt er dus van overerven, en bovenstaande truc werkt dan ook door een class van twee lambda's over te laten erven en beide operator()'s in de huidige scope te stoppen dmv een using declaration.

Bovenstaande code is ook uit te breiden zodat hij werkt met een arbitrair aantal lambda's, en daarnaast ook met losse functies en pointer-to-members:


C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <iostream>
#include <type_traits>
#include <functional>

template<class... Fn>
struct overloaded_function;

template<class F1, class F2>
struct overloaded_function<F1, F2> : F1, F2
{
   overloaded_function(F1 f1, F2 f2) : F1(f1), F2(f2) { }

    using F1::operator();
    using F2::operator();
};

template<class F1, class F2, class... Fn>
struct overloaded_function<F1, F2, Fn...> : F1, overloaded_function<F2, Fn...>
{
    overloaded_function(F1 f1, F2 f2, Fn... fn) : F1(f1), overloaded_function<F2, Fn...>(f2, fn...) { }

    using F1::operator();
    using overloaded_function<F2, Fn...>::operator();
};

template<class R, class... Args>
struct function_wrapper
{
    function_wrapper(R (*f)(Args...)) : func(f) { }
    
    R operator()(Args... args)
    {
        return func(args...);
    }

    R (*func)(Args...);
};

template<class T, bool B = std::is_member_pointer<T>::value>
struct callable_wrapper_helper
{
    typedef T type;
};

template<class T>
struct callable_wrapper_helper<T, true>
{
    typedef decltype(std::mem_fn(T())) type;
};

template<class R, class... Args>
struct callable_wrapper_helper<R (*)(Args...), false>
{
    typedef function_wrapper<R, Args...> type;
};

template<class T> struct callable_wrapper_type
{
    typedef typename callable_wrapper_helper<typename std::remove_reference<T>::type>::type type;
};

template<class T>
typename callable_wrapper_type<T>::type wrap_callable(T t)
{
   return typename callable_wrapper_type<T>::type(t);
}

template<class... Fn>
overloaded_function<typename callable_wrapper_type<Fn>::type...> overload(Fn... fn)
{
    return { wrap_callable(fn)... };
}

const char * foo(std::nullptr_t) { return "nullptr"; }

int main()
{
    auto f = overload
    (
        [](int i) { return "int"; },
        [](float f) { return "float"; },
        [](const char * s) { return "const char*"; },
        foo,
        &std::string::c_str
    );

    std::string s("std::string");

    std::cout << f(34) << std::endl;
    std::cout << f(34.f) << std::endl;
    std::cout << f("34") << std::endl;
    std::cout << f(nullptr) << std::endl;
    std::cout << f(&s) << std::endl;
}



Werkt in zowel VC++ met November '12 CTP compiler als in GCC 4.7.2