C++11 Reference Capture

In a recent post I began exploring some of the features that have popped up in C++11 since I’ve been on a mostly .NET and C++/CLI path lately with InWorldz projects. One of the features I mentioned was the new lambda syntax available in C++11.

C++11 allows you to automatically capture variables that you use inside a lambda function by using special qualifiers in the beginning of the lambda definition. I began using lambdas after quickly glancing over the syntax thinking that I completely understood them from my previous experience in other languages. I hadn’t taken into account that C++ is not a garbage collected language, and the differences this would make when referencing variables in a lambda turned out to be extremely important.

There is one principle gotcha in lambda reference capture. This issue is also a common gotcha in C++ programming in general. If you guessed that I’m talking about object lifetime, you are correct.

I had the following code running in test mode on my development machine:

try {
	this->performAssetRequest(std::get(work), std::get(work));

} catch (const std::exception& e) {
	_ioService.post(
		[&]{ 
		AppLog::instance().out() 
			<< "[CLOUDFILES] Caught exception while trying to get asset: " 
			<< e.what() << std::endl; 
		}
	);
}

Randomly during execution of a long string of requests I would run into an access violation. The access violation would always happen inside the boost ioservice while it was calling my lambda. For a while I was quite confused since the exception message never seemed to be valid. It seemed like some kind of corruption was happening upon throwing the exception. But then I looked closer at the lambda and capture code.. What exactly does [&] mean to C++ besides “capture any variables mentioned in the lambda”.

It turns out that [&] tells the C++11 compiler to capture the std:: exception e by reference. What this means is that we are taking a reference to the memory that contains the exception. As long as that memory is guaranteed to be valid by the time the lambda is executed, this is an efficient way to capture a variable.

However, in my case there were no guarantees. In fact, after the execution of the catch block, e will be destroyed. After e was destroyed, sometime later boost::asio would call into my error reporting lambda and blammo, we’re accessing freed memory.

C++11 allows you to specify capture by value. Essentially, instead of being dependent on the lifetime of the original captured object, we instead make a copy of it that will be passed to the lambda function later. This is exactly what we need in this case because we know that e will go out of scope and be destroyed before we execute the lambda function.

To specify capture by value instead of by reference we change [&] to [=] as shown

try {
	this->performAssetRequest(std::get(work), std::get(work));

} catch (const std::exception& e) {
	_ioService.post(
		[=]{ 
		AppLog::instance().out() 
			<< "[CLOUDFILES] Caught exception while trying to get asset: " 
			<< e.what() << std::endl; 
		}
	);
}

Now this code will do what we want, and as an extra added bonus, it won’t crash. Finally, all is well in the world.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

Up ↑

%d bloggers like this: