Lamba list


#1

A question for one of you c++ experts here. I am modifying some existing windows code so I can run in on android. But I am changing to use a FileChooser as done in DialogsDemo.h and I don’t understand the code. Here is the snippet:
fc.reset (new FileChooser (“Choose a file to open…”, File::getCurrentWorkingDirectory(),
“*”, useNativeVersion));

            fc->launchAsync (FileBrowserComponent::canSelectMultipleItems | FileBrowserComponent::openMode
                                 | FileBrowserComponent::canSelectFiles,
                             [] (const FileChooser& chooser)
                             {
                                 String chosen;
                                 auto results = chooser.getURLResults();

                                 for (auto result : results)
                                     chosen << (result.isLocalFile() ? result.getLocalFile().getFullPathName()
                                                                     : result.toString (false)) << "\n";

                                 AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
                                                                   "File Chooser...",
                                                                   "You picked: " + chosen);
                             });

This compiles.

But calling a function instead of the alert window such as:
MyFunction(chosen);
This gives an error about local variables in a lambda body.cannot be referenced unless int he capture list. How resolve I do this???


#2

you need to put the variables you want to capture in the [] in the lambda. that’s the [] (const FileChooser& chooser) part.

you can name the variables you want to capture explicitly, or just put & to capture all of the variables in the current scope by reference.

if you want to capture class member variables and use class member functions, capture this

void functionThatTakesALambda(std::function<void()> func) { func(); }; 
void MyClass::privateFunction() {}
void MyClass::func()
{
    auto lambda = [this](/* args */)
    {
        this->privateFunction();
    };
    functionThatTakesALambda(lambda);
}

https://en.cppreference.com/w/cpp/language/lambda


#3

You have to be careful with async stuff that you don’t capture anything in the lambda that could be destroyed before the async function has been executed. So for example local variables should not be captured by reference when in an async context. Local variables captured by copy are OK, but of course that might not work like you want, either.

Also when capturing “this”, there are obviously chances the “this” object may have been deleted before the async operation has run.


#4

I don’t think I get it yet. I think I need this broken down a little more and the link to lambda confuses me more.

the auto lambda = this{this->f();};
in this case the the ‘this’ the one coming from the caller of func(); if so, why not make the func take the callers this as an argument like this:
void MyClass(auto callersthis){
callersthis->privateFunction();
functionThatTakesALambda(callersThis);
}


#5

What you are constrained here by, on purpose to reduce coupling of classes, is the std::function that is in the signature of FileChooser::launchAsync. Additional data is not part of the callback function signature but you can still have it by making the lambda capture the needed additional state. (The additional state here being the “this” pointer so you can call its methods from inside the lambda.)

The code you need to have, assuming MyFunction is a method of your “this” class is :

fc->launchAsync (FileBrowserComponent::canSelectMultipleItems | FileBrowserComponent::openMode
                                 | FileBrowserComponent::canSelectFiles,
                             [this] (const FileChooser& chooser)
                             {
                                MyFunction(chooser.getResult());
                             });

#6

if you understand operator overloading, lambdas basically build off of the ability to overload operator(), the function call operator. take a look at this structure:

struct MainContentComponent;
struct Anonymous
{
    int& a;
    int& b;
    MainContentComponent& mcc;
    Anonymous(const int& a_, const int& b_, MainContentComponent& mcc_) : 
    a(a_), 
    b(b_), 
    mcc(mcc_) 
    {
        //constructor 
    }

    void operator()(int c, int d) 
    {
        mcc.setBounds( a, b, c, d ); 
    } 
};

You could create an instance of this struct locally, and supply it with the appropriate arguments, like this:

void MainContentComponent::someFunc()
{
    int a = 2;
    int b = 2;
    Anonymous anon(a, b, *this);
    anon(4,5); //calls the Anonymous::operator(int c, int d) member function 
}

Do you see how the struct’s constructor captures the local variables in the surrounding scope and stores references to them?

Lambdas perform the same task, but with syntactic sugar, so to speak.

This does the same thing:

void MainContentComponent::someFunc()
{
    int a = 2;
    int b = 2;
    auto anon = [&a, &b, this](int c, int d)
    {
         this->setBounds(a, b, c, d);
    };    
    anon(4, 5);
}

#7

Thanks, I think all is working now.


#8

One last question.
fc->launchAsync(FileBrowserComponent::canSelectMultipleItems | FileBrowserComponent::openMode
| FileBrowserComponent::canSelectFiles,
[this](const FileChooser& chooser){}

I do a get results on chooser inside the braces. But if the user cancels out of the file chooser, the code in the braces is still executed. How do I either skip {} on cancel or get the status inside {} to do nothing?


#9

The documentation isn’t clear about it, but I would assume

if (chooser.getResult()==File())

can be used inside the callback function/lambda to detect the selection of the file was cancelled or failed in some other manner.


#10

Thanks that worked.

But now I have another problem. works fine on windows but not Android. I am saving properties to getCurrentDir(). This returns “/” as a path so I cannot save properties there. Everything I see on the internet is that ap data goes folder data directory named for the ap. But that directory does not exist. So 2 questions I have not found yet:
1- How does the ap data directory get create? (Do I have to create it manually?)
2-how do I get the path name to it?


#11

As a temporary method (Until someone can tell me the correct way) I am just storing the properties file in /storage/emulated/0/Android/data. Works on all my devices atleast.