SafePointer<Component>(this) and 'this' at the same time

When you make a lambda (for an asynchronous callback for example) and pass a SafePointer, you need to then use that pointer to access every member variable of your class.

myLambda = [safeThis = SafePointer<Component>(this)] ()
{
	if (safeThis == nullptr) return;

	safeThis->myMemberVariable1 = 0;
	safeThis->myMemberVariable3 = 1;
	safeThis->myMemberVariable4 = 2;
	…
}

This can become cumbersome depending on what you want to do inside the lambda.

I’m just wondering if you can pass a SafePointer and a ‘this’, and first test the SafePointer for being not null, and then just use your member variables without the SafePointer. In other words:

myLambda = [safeThis = SafePointer<Component>(this), this] ()
{
	if (safeThis == nullptr) return;

	myMemberVariable1 = 0;
	myMemberVariable3 = 1;
	myMemberVariable4 = 2;
	…
}

Is there any case where (this) would be invalid, but the safeThis would still be safe? Am I misunderstanding something? Sort of a dumb question, but the code is so much cleaner without the pointer everywhere…

The lambda object simple holds a pointer which then becomes invalid. Having an invalid pointer itself isn’t a problem unless you dereference it, i.e if you access the member variables like in your example.
So, yes this code is safe (assuming the lambda is called on the same thread as the thread where the component is destroyed, which usually is the main thread, otherwise the destruction of the component might happen during the callback).

2 Likes

I’m failing to see why you’d want to do this, personally. The first case (SafePointer->someMember) might seem cumbersome to type, but it will definitely keep you out of painting yourself in a corner by dereferencing a pointer that is no longer valid … you’re adding complexity by passing this and a SafePointer, and then also giving yourself an opportunity to slip up and deref something you shouldn’t, as in the case that @jellebakker indicates.

Better to keep the lambda small and tight, and leave all the “this”-semantics to the compiler…

I find that using SafePointer-> in the lambda can make it look messy but perhaps I should then replace the lambda with a class function instead… ;).

2 Likes

I was just going to write that: that is the best solution

1 Like

How do you replace this with a class function (I assume you mean member function)?

Assuming you want to assign a function to the AlertWindow callback (for example), which is:

std::function<void (int)> callback

and you normally would do this, with a pointer to the AlertWindow:

int value, other;
aw->callback = [this, value, other] (result)
{
	if (result = 1)
	{
		do something with ‘value’;
		do something with ‘other’;
	}
}

If inside my class, I make this function:

void myFunction(int value, int other)
{
	if (result = 1)
	{
		do something with ‘value’;
		do something with ‘other’;
	}
}

How do I get ‘result’ into it, or assign it to the callback? I can’t quite work out the syntax… I know I’m being dumb here… but just to understand this…

either add it as argument, but I think better for readability is to have different methods for each result code, which also serves as self documentation:

aw->callback = [safePointer] (result)
{
    if (!safePointer) 
        return;

    if (result == 1)
        safePointer->doThis();
    else if (result == 2)
        safePointer->doThat();
}
void doThis()
{
    // has access to your private variables
}
void doThat()
{
    // has access to your private variables
}

And your lambda is a highlevel description of what is happening, and you can easily change the result codes without a big refactor, if you want to change the order etc…

1 Like

Thanks! I can see that…

So you or the other poster didn’t mean to actually assign a member function to the callback, but put a member function inside the lambda?

Unless I misunderstood Jelle, yes.
I was thinking to have methods on the owning object which then accesses the private variables.
That is my understanding of encapsulation. And the std::function simply serves to glue it to the callback.

I thought maybe you could do something like:

aw->callback = std::function<void (int)>(myFunction(value, j, void *), void *result);

but this is not correct yet, I’m googling but I haven’t worked it out…

…but your suggestion makes sense.

You can do something like that, but I think it is horrible code:

aw->callback = [safePointer, &value, &other](int result)
{
    // do with the variables here and enjoy your pasta ;-)
}
1 Like

Yeah, assigning a member function to the callback I guess would defeat the point of the safePointer anyway… since your class could go away before the callback executes.

My point was that if the lambda grows to be a long function with many times safePointer-> in it, then you could just make the lambda call a class member function with the same arguments as the lambda. My approach so far has been to capture “this” in the lambda and access the class members directly but as already mentioned that can be tricky…

1 Like