Better way to connect lambda to dynamically created components?


#1

So, I have something like this…

	void addLambdaToLabel(const int &arrayIndex)
	{
		auto label = ownedArrayOfLabels[arrayIndex];

		switch (arrayIndex)
		{
		case 0:
			label->onTextChange =
				[&] {mainComponent->labelChanged(0); };
			break;
		case 1:
			label->onTextChange =
				[&] {mainComponent->labelChanged(1); };
			break;

			// etc...
		}
	}

… which works great, but is a pain for large arrays of components that require massive switch statements.

I’d rather have something like…

	void addLambdaToLabel(const int &arrayIndex)
	{
		auto label = ownedArrayOfLabels[arrayIndex];

		label->onTextChange =
			[&] {mainComponent->labelChanged(label->getName()); };
	}

… or even …

	for (auto label : ownedArrayOfLabels)
	{
		label->onTextChange =
			[&] {mainComponent->labelChanged(label); };
	}

… but despite these attempts (and many others) I can’t seem to find a way to dispense with the switch statement approach without getting access violations.

I’m hoping someone has a less verbose means of accomplishing the same result. Please do tell.


#2

You could do a jump table kind of think like

std::vector <std::function<void(Label&)> jumpTable;

... 

ownedArrayOfLabels[i].onTextChange = [this, i] () { jumpTable[i] (ownedArrayOfLabels[i]); }

The performance catch is that creating a lambda with a capture list whose size is greater than a pointer will cause a heap allocation, and there’s a little bit of indirection through pointers in looking up the lambda in the jump table and then calling it. But it’s probably not too bad.

That said, if your lambdas are known at compile time, why do you need a switch statement?


#3

Thanks for this! I will give it a try. I don’t know how many labels will be created, I only know the maximum number, hence the switch.


#4

This has an error, you are capturing the for loop variable “label” as a reference which is going to be dangling after the loop :

for (auto label : ownedArrayOfLabels) 
{ label->onTextChange = [&]() {mainComponent->labelChanged(label); }; }

Change the lambda capture to [this,label] instead of the [&].


#5

I will try that as well. Thanks for the explanation!


#6

You should generally be very careful when capturing references or pointers in lambdas. But especially the capturing local variables by reference is a particularly nasty mistake.


#7

For now, I’ve simply changed my code to…

void addLambdaToLabel(const int &arrayIndex)
{
	auto label = ownedArrayOfLabels[arrayIndex];
	label->onTextChange = [this, arrayIndex] () {mainComponent->labelChanged(arrayIndex); };
}

… which is working brilliantly. Thanks for getting me straightened out on this!