lambda

Lambda type Multithreading to process out of scope parameters (Cocos2dx, Android, C++)

Posted on Updated on

Lets pretend you have a vector containing int data, and you also have a class object which you need to pass into a Thread for some heavy duty processing. Lets also pretend that even if this process isn’t heavy at all but the program requires the order for everything to be processed in in exact logical order.

How do we do that? A thread in Cocos2dx with C++ looks like this:

    auto thread = std::thread([](){
        CCLog("start thread");
 
        // code goes here
 
        Director::getInstance()->getScheduler()->performFunctionInCocosThread([](){
            CCLog("finish another thread");
        });
    });
    thread.detach();

The problem:
Great! except our requirement was to pass in some data (a vector containing several int type variables and some class object)

With the above code we can’t do that because a Lambda doesn’t allow you to pass in data from outside of its scope even if it is of the same class. So what can we do about it?

The solution:
The solution is to use std::function. Lets make an example.

Header file:

template                <typename T, typename O>
std::function<T()>      MyThreadClass(T int_vector, O class_object);

I use a template for my variable types for 2 reasons:

1) With templates, You don’t need to worry about what data type goes in, everything is automated for you in the background.

2) I’m lazy (actually, I had a deadline writing this and I couldn’t be bothered to check)

Now in our implementation class we can write something like this:

/**
 * thread class
 */
template <typename T, typename M, typename B>
std::function<T()> MyThreadClass(T int_vector, O class_object) {
    CCLog("start thread");
    auto thread = std::thread([int_vector, class_object](){

        for (std::vector<int>::const_iterator itl = int_vector.begin(); itl != int_vector.end(); ++itl) {
            switch(*itl) {
				case 0: class_object.addNumbers(); 		break;
				case 1: class_object.subtractNumbers(); break;
				case 2: class_object.multiplyNumbers(); break;
				case 4: class_object.divideNumbers(); 	break;      
            }
        }

        Director::getInstance()->getScheduler()->performFunctionInCocosThread([int_vector, class_object);
    });
    thread.detach();
    return 0;
}

Might look a bit intimidating but its actually quite simple to understand.
In our Lambda we have to ‘capture’ data in order to process them. This is where we want to pass data into our thread. But Lambda won’t let us because it cannot see anything outside of its scope. So we pass data into our function which then is captured using something like below:

auto thread = std::thread([int_vector, class_object](){
	// ...
}
	// ...
Director::getInstance()->getScheduler()->performFunctionInCocosThread([int_vector, class_object);
	// ...

If we have no data to pass in from the outside, we can just remove int_vector and class_object.

Happy coding!