Overloading lambda's in C++11

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

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 F1class F2>
struct overloaded : F1F2
{
    overloaded(F1 x1F2 x2) : F1(x1), F2(x2) {}
    using F1::operator();
    using F2::operator();
};
 
template<class F1class F2>
overloaded<F1F2overload(F1 f1F2 f2)
{
    return overloaded<F1F2>(f1f2);
}

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 F1class F2>
struct overloaded_function<F1F2> : F1F2
{
   overloaded_function(F1 f1F2 f2) : F1(f1), F2(f2) { }

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

template<class F1class F2class... Fn>
struct overloaded_function<F1F2Fn...> : F1overloaded_function<F2Fn...>
{
    overloaded_function(F1 f1F2 f2Fn... fn) : F1(f1), overloaded_function<F2Fn...>(f2fn...) { }

    using F1::operator();
    using overloaded_function<F2Fn...>::operator();
};

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

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

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

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

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

template<class Tstruct 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

A more useful bind() for Javascript en

By .oisyn on Wednesday 30 January 2013 23:53 - Comments (4)
Category: Random code farts, Views: 2.211

bind or similar functionality has been a part of many javascript codebases and has made its official introduction in Javascript 1.8.5. Coming from a C++ background myself, where std::bind made its way through the TR1 library into standard C++11 after years of perculating within the boost library, I found that Javascript's version is lacking in certain features.

Recently this blog post was brought to my attention. Disregarding the underlying message of the post for the moment, the issue at hand was that the following Javascript expression:

JavaScript:
1
['10','10','10','10','10'].map(parseInt);

resulted in:

JavaScript:
1
[10NaN234]


While this might not be very intuitive for the average non-javascript developer, the actual problem lies in the fact that Array.prototype.map calls the supplied callback with 3 arguments (respectively: value, index and the array being mapped), and that parseInt accepts 2 - value and radix - with the latter being optional. Thusly, the array indices 0 through 4 are being interpreted as radix, causing the string '10' to be parsed for all 5 radices.

API design considerations aside, it would've been nice if we somehow were able to express: hey, I have this function accepting 2 argument, and I'd like to bind the second argument to a fixed value. Sounds like a job for... (queue drumroll)... Function.prototype.bind()! Well, it sounds like that, except that it really isn't. It would've suited the job perfectly if you wanted to fix the first argument, but the second? Come on! Been smoking crack lately?

And of course, some might say that simply using a lambda would suffice:

JavaScript:
1
['10','10','10','10','10'].map(function(x) { return parseInt(x10); });

and some would be right. But then what is the point of bind's existence, really? Would it not have been cool if you could simply use bind to say: pass the first argument through, but set the second argument to 10. For example, using this syntax:

JavaScript:
1
2
var f = parseInt.bind(null$_10);
f('34'); // calls parseInt('34', 10)


Well, you can now :). Inspired by C++'s std::bind, I've implemented a new version, Function.prototype.bind2(). Specifically, one that also accepts the following placeholders as its arguments:
  • $this - represents the 'this' that is used to call the function with
  • $1..$9 - represent the arguments 1 through 9
  • $_ - represents the argument at that location
  • $end - a special placeholder to indicate that further arguments are to be ignored (can only be used as the last argument to bind2().
Here are some examples:

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
['1''2''3''4'].map(parseInt.bind2(null$_10)); // [1, 2, 3 ,4]

foo.bind2(null$2$1)("hello""world"); // foo("world", "hello")

foo.bind2(null$4$_$1)(1234567); // foo(4, 2, 1, 3, 5, 6, 7)

foo.bind2(null$4$_$1$end)(1234567); // foo(4, 2, 1)

foo.bind2(null$2$299$2)(123); // foo(2, 2, 99, 2, 1, 3)

foo.bind2($2$1)(12); // foo.call(2, 1), in other words: (2).foo(1)

String.prototype.output = alert.bind2(null$this);
"meow".output(); // alert("meow")

Number.prototype.concatTo = Array.prototype.concat.bind2($1$this);
(3).concatTo([12]); // [1, 2].concat(3) => [1, 2, 3]



Source code:

JavaScript:
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
function Placeholder(i) { if (ithis.index = i; }
$this = new Placeholder();
$_ = new Placeholder();
$end = new Placeholder();
for (var i = 1i <= 9i++)
    window['$' + i] = new Placeholder(i);

Function.prototype.bind2 = function()
{
    var func = this;
    var remap = [];
    var bound = [];
    var used = [];
    var numArgs = arguments.length;
    var hasEnd = false;

    for (var i in arguments)
    {
        i = +i// make sure it's an int
        var a = arguments[i];
        
        if (a === $end)
        {
            if (i != arguments.length-1)
                throw new TypeError("bind2 - only allowed to use $end als last parameter");
            hasEnd = true;
            numArgs = +i;
        }
        else if (a instanceof Placeholder)
        {
            var idx;
            if (a === $this)
                idx = 0;
            else if (a === $_)
                idx = i;
            else
                idx = a.index;
            
            remap[i] = idx;
            numArgs = Math.max(numArgsidx + 1);
            used[idx] = true;
        }
        else
        {
            bound[i] = a;
        }
    }
    
    var firstFree = 0;
    while(used[++firstFree]);
    if (!hasEnd)
    {
        for (var i = arguments.lengthi < numArgsi++)
        {
            remap[i] = firstFree;
            while(used[++firstFree]);
        }
    }
    
    var r = function()
    {
        var outThis = (0 in bound) ? bound[0] : remap[0] == 0 ? this : arguments[remap[0] - 1];
        var outArgs = [];
        for (var i = 1i < numArgsi++)
            outArgs[i-1] = (i in bound) ? bound[i] : remap[i] == 0 ? this : arguments[remap[i] - 1];
            
        if (!hasEnd)
            outArgs = outArgs.concat(Array.prototype.splice.call(argumentsfirstFree - 1));

        return func.apply(outThisoutArgs);
    };
    
    var nop = function() { };
    nop.prototype = func.prototype;
    r.prototype = new nop();
    return r;
}


Disclaimer: I'm not a javascript afficionado. I know above code polutes the global namespace (in particular with the Placeholder constructor), and you might not want that. You could wrap it in a local scope if you want...