Overloading lambda's in C++11

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

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

Volgende: A more useful bind() for Javascript 01-'13 A more useful bind() for Javascript

Reacties


Door Tweakers user H!GHGuY, zaterdag 2 februari 2013 09:19

Ik neem aan dat het eerste voorbeeld, als je lambda's vervangt door functors, ook al werkt in C++03, right?

Bij het tweede voorbeeld zal dit iets moeilijker (lees veel meer code) zijn door de variadic templates.

Door Tweakers user Caelorum, zaterdag 2 februari 2013 11:10

Leuke artikelen telkens :) Ik moest wel eerst een paar keer de code doorlezen voordat ik het snapte hoor ^^
Ben je van plan om vaker wat te posten?

Door Tweakers user .oisyn, zaterdag 2 februari 2013 23:05

H!GHGuY schreef op zaterdag 02 februari 2013 @ 09:19:
Ik neem aan dat het eerste voorbeeld, als je lambda's vervangt door functors, ook al werkt in C++03, right?
Klopt inderdaad.
Caelorum schreef op zaterdag 02 februari 2013 @ 11:10:
Ben je van plan om vaker wat te posten?
Op zich wel :). Ik loop al jaren rond met het idee om af en toe wat te posten. Normaal plaats ik dit soort onzin in het devschuur coffee corner topic, maar toen dacht ik bij mezelf, waarom er niet gewoon over bloggen.

Wat ik in ieder geval wel wil doen is af en toe wat updates plaatsen rondom de nieuwste ontwikkelingen op het gebied van C++ standaardisatie.

Reageren is niet meer mogelijk