Use of SafePointer with lambda Async functions

From reading the docs, it says that “If you’re using a component which may be deleted by another event that’s outside of your control, use a SafePointer instead of a normal pointer to refer to it, and you can test whether it’s null before using it to see if something has deleted it.”

So I’m just wondering, based on examples of its usage in JUCE Source, why it is sometimes tested and sometimes not?

As an example, here is some code from the JUCE AudioPluginHost, that launches the AudioSettings dialog:

     auto safeThis = SafePointer<MainHostWindow> (this);

     w->enterModalState (true,
                         ModalCallbackFunction::create
                         ([safeThis] (int)
                         {
                             auto audioState = safeThis->deviceManager.createStateXml();

                             getAppProperties().getUserSettings()->setValue ("audioDeviceState", audioState.get());
                             getAppProperties().getUserSettings()->saveIfNeeded();

                             if (safeThis->graphHolder != nullptr)
                                 if (safeThis->graphHolder->graph != nullptr)
                                     safeThis->graphHolder->graph->graph.removeIllegalConnections();
                         }), true);

From my reading, the first line (in the callback) would crash if safeThis was nullptr, wouldn’t it? Then, safePointer is tested for null later on, but not those first 3 lines.

Wondering what best practice is for this…

Not sure about your question, but as an implementation note, if you don’t want to pollute your local scope with the safeThis variable, you can assign it in your capture list:

[safeThis = SafePointer<MainHostWindow> (this)]
1 Like

I tried your suggestion on Mac - worked fine. Have you tried this on Windows? Because I just moved my projects over there, and VS2019 doesn’t like it: Code:

AlertWindow::showOkCancelBox (AlertWindow::WarningIcon,
                                            TRANS("Deleting Last Instrument Layer..."),
                                            TRANS("This will delete any Aux and Master Layers as well!"),
                                            TRANS("Continue"),
                                            TRANS("Cancel"),
                                        &pluginArea,
                                        ModalCallbackFunction::create
                                        ([safeThis = SafePointer<InstrumentLayer> (this)] (int result)
                                        {
                                            if (result == 1) // OK
                                                safeThis->pluginArea.clear();
                                        })
                                        );

Error:

Severity	Code	Description	Project	File	Line	Suppression State
Error	C2440	'<function-style-cast>': cannot convert from 'const InstrumentLayer::{ctor}::<lambda_c18e30967250f17d531ac716571d20be>::()::<lambda_d84ed578c87b0cccf7b36ad7977afe15> *' to 'juce::Component::SafePointer<InstrumentLayer>'	
Message		No constructor could take the source type, or constructor overload resolution was ambiguous
Error	C2119	'safeThis': the type for 'auto' cannot be deduced from an empty initializer	
Error	C2512	'InstrumentLayer::{ctor}::<lambda_c18e30967250f17d531ac716571d20be>::()::<lambda_d84ed578c87b0cccf7b36ad7977afe15>::()::<lambda_2e4964212d86284a96943fa6b90784c0>': no appropriate default constructor available	

Just doing it the standard way works…

IIRC generalized lambda captures are C++14. Could be that?

C++ Language Standard is set to “default (C++14)” in my Projucer Projects…

To be clear, this way works just fine (declare safeThis first):

auto safeThis = SafePointer<InstrumentLayer> (this);
AlertWindow::showOkCancelBox (AlertWindow::WarningIcon,
                                            TRANS("Deleting Last Instrument Layer..."),
                                            TRANS("This will delete any Aux and Master Layers as well!"),
                                            TRANS("Continue"),
                                            TRANS("Cancel"),
                                        &pluginArea,
                                        ModalCallbackFunction::create
                                        ([safeThis] (int result)
                                        {
                                            if (result == 1) // OK
                                                safeThis->pluginArea.clear();
                                        })
                                        );

Guess I’ll just have to “pollute my local scope”. :wink:

The original question, though, was "why use a safePointer if you’re not going to test for it being not a nullptr?

The original question, though, was "why use a safePointer if you’re not going to test for it being not a nullptr?

It’s a bug.

1 Like