Geeks With Blogs
Timo Heinäpurola

The recently ratified C++11 language specification provides a range of cool new features. Many of which have been part of other programming languages for some time now. One such new feature that I value a lot is the concept of lambdas.

Lambdas are great in many ways. They enable you to create callbacks that are called for specific items, for instance, or you can implement events with them, as is the case in this blog post. The following is an example of an event implemented in C++11 using lambdas.

RREvent1Arg<int> e;
e += [](int i) { printf( "%d", i ); };

e( 100 );

This looks exactly like C# style events and makes for a very neat way of creating notifications that the user can request to be triggered at pre-specified points. The method introduced here was not possible in previous versions of C++ but with the introduction of lambdas we now have the power to define these kinds of constructs in a developer friendly manner.

The lambda is actually a nameless function body wrapped with a nameless type. This type can also store captured stack variable values if you so choose. This is possible by specifying the captured fields between the [ ] brackets. You also have the option to specify default capture behavior. If you wish to capture values you use by reference you should just write the letter ‘&’ between the brackets and if you wish to capture by value, you should write the letter ‘=’. You can also prefix the name of a single field with the letter ‘&’ to capture it by reference. You can use all the fields you have captured, either explicitly or implicitly, within the function body. The following shows a few examples.

int stackValue = 200;
RREvent1Arg<int> e;
e += [=](int i) { printf( "%d\n", i + stackValue ); };
e += [&](int i) { printf( "%d\n", i + stackValue ); stackValue += 100; };
e += [&stackValue](int i) { printf( "%d\n", i + stackValue ); };
e += [stackValue](int i) { printf( "%d\n", i + stackValue ); };

e( 100 );

// Prints:
// 300
// 300
// 400
// 300

Because lambdas are defined as nameless types, you can’t directly define the type of a variable that will contain a lambda. This is one major reason why C++11 introduces the “auto” keyword. When using the auto keyword, as is done in the following example, the compiler actually deduces the type of the variable without you having to explicitly specify it.

auto l = [](int i) { printf( "%d\n", i ); };

What about templates then? We’d like to define a template called RREvent1Arg that does just what we’ve seen at the beginning of this post. We’d like to be able to create instances of references to the lambdas that we’re adding to the event. This is where std::function comes in. This template class allows for defining a function prototype that the lambda function must comply with and then pointing to the given function with the given prototype. We’re also storing an array of these constructs thus allowing the user to register multiple event handlers.

Cutting a long story short, the magic behind the event implementation above goes as follows.

template<typename T1>
class RREvent1Arg
{
public:
    typedef std::function<void (T1)> Func;

public:
    void Call( T1 arg )
    {
        for( auto i = m_handlers.begin(); i != m_handlers.end(); i++ )
        {
            (*i)( arg );
        }
    }

    void operator ()(T1 arg)
    {
        Call( arg );
    }

    RREvent1Arg& operator += ( Func f )
    {
        m_handlers.push_back( f );
        return *this;
    }

    RREvent1Arg& operator -= ( Func f )
    {
        for( auto i = m_handlers.begin(); i != m_handlers.end(); i++ )
        {
            if ( (*i).target<void (T1)>() == f.target<void (T1)>() )
            {
                m_handlers.erase( i );
                break;
            }
        }

        return *this;
    }

private:
    vector<Func> m_handlers;
};

Note that you have to define a new class for each parameter count. This is not pretty, but if you’re working with Visual Studio 2010 it’s a must because VS2010 does not support variadic templates. So you’ll have to wait for VS11 to be able to make the code prettier.

Of course lambdas are not the only way of implementing these kinds of events. We’ve actually been using this kind of event structure since the dawn of time in our game engine at Raccoon Interactive. We’re actually not able to implement events with lambdas at this point in time due to limitations in compilers on some of the major platforms we’re targeting (Android being one example) but instead we simply store pointers to this call methods.

To conclude all this, lambdas open up a range of new ways in which we can be more productive bringing C++ closer to languages like C# in those terms. The updated STL actually uses lambdas a lot allowing you to write much simpler iteration code, for instance. Events are just one example of how C++11 is bringing C++ back to the “mainstream” in not just the embedded world but for desktop applications as well.

Posted on Wednesday, September 28, 2011 11:50 PM | Back to top


Comments on this post: Lambdas and events in C++

# re: Lambdas and events in C++
Requesting Gravatar...
Good post, Timo. The first thing I heard about the new C++ standard was the inclusion of lambdas - I would have pushed for adoption of the standard for this feature alone.
Left by Joe Miller on Oct 05, 2011 4:04 PM

# re: Lambdas and events in C++
Requesting Gravatar...
RREvent1Arg& operator -= ( Func f ) {
m_handlers.erase( find( begin( m_handlers ), end( m_handlers ), f ) );
return *this;
}

:)
Left by Aszarsha on Nov 15, 2011 3:50 PM

# re: Lambdas and events in C++
Requesting Gravatar...
void Call( T1 arg ) {
for_each( begin( m_handlers ), end( m_handlers ), []( Func & f ) {
f( arg );
});
}

That would be the C++11 :)
Left by Aszarsha on Nov 15, 2011 3:56 PM

# re: Lambdas and events in C++
Requesting Gravatar...
event operator -= (function f)
{
m_handlers.erase
( std::remove_if(std::begin(m_handlers), std::end(m_handlers), [&f](Func & vf)
{
return (f.target_type() == vf.target_type());
})
, end(m_handlers)
);

return *this;

}
Left by George Jackson on Nov 20, 2011 3:12 PM

# re: Lambdas and events in C++
Requesting Gravatar...



RREvent1Arg& operator -= ( Func f )
{
m_handlers.erase
( std::remove_if(std::begin(m_handlers), std::end(m_handlers), [&f](Func & vf)
{
return (f.target_type() == vf.target_type());
})
, end(m_handlers)
);

return *this;

}
Left by George Jackson on Nov 20, 2011 3:15 PM

Your comment:
 (will show your gravatar)


Copyright © raccoon_tim | Powered by: GeeksWithBlogs.net