C++ member access operator

I came across this line in the ESP32 examples:

server.available().stop();

I haven't encountered this syntax before (multiple . operators and member functions) and I've been trying to learn more about it .. unfortunatly I don't know what this is actually caled so my attempt to find info on the web has failed.

There is another example here that is even more confusing:

ArduinoOTA
    .onStart([]() {
      String type;
      if (ArduinoOTA.getCommand() == U_FLASH)
        type = "sketch";
      else // U_SPIFFS
        type = "filesystem";

      // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
      Serial.println("Start updating " + type);
    })
    .onEnd([]() {
      Serial.println("\nEnd");
    })
    .onProgress([](unsigned int progress, unsigned int total) {
      Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
    })
    .onError([](ota_error_t error) {
      Serial.printf("Error[%u]: ", error);
      if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
      else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
      else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
      else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
      else if (error == OTA_END_ERROR) Serial.println("End Failed");
    });

Can anyone help, or point me to a good tutorial on this feature of C++

You just call the stop method on the result of the server method.

server.available().stop(); 
// is equivalent to
auto client = server.available();
client.stop();

The second snippet uses lambda functions: Lambda expressions (since C++11) - cppreference.com

The calling of the methods is exactly the same, the onStart, onEnd, etc. methods return a reference to the object itself, so the next method in the chain is called on the same object as the previous one.

BillBigge:
.. unfortunatly I don't know what this is actually caled so my attempt to find info on the web has failed.

FWIW, I call it Method Chaining.

Thanks - The chaining concept makes sense now.

Lambda functions are something I have come across but struggled to get my head around the concept. I think I'm getting there now so . . . if I have understood correctly:

the line Serial.printf("Progress: %u%%\r", (progress / (total / 100))); is the body of a function that is being passed to the ArduinoOTAClass, which in turn expects a function with two ints as arguments when using the onProgress() method.

The ArduinoOTAClass can then call this function and pass it whatever values it needs to.

If thats correct then is it similar to passing a function pointer, except that the lambda function effectively becomes a member of ArduinoOTAClass, rather than being external to it?

Thanks for the help!

BillBigge:
Lambda functions are something I have come across but struggled to get my head around the concept.

It looks like a Lambda function "[] (...){...}" is just a function with no name. Just like any other function declaration, the stuff between the parens is the argument list and the stuff in the curly braces is the body of the function.

BillBigge:
Thanks - The chaining concept makes sense now.

Lambda functions are something I have come across but struggled to get my head around the concept. I think I'm getting there now so . . . if I have understood correctly:

the line Serial.printf("Progress: %u%%\r", (progress / (total / 100))); is the body of a function that is being passed to the ArduinoOTAClass, which in turn expects a function with two ints as arguments when using the onProgress() method.

The ArduinoOTAClass can then call this function and pass it whatever values it needs to.

If thats correct then is it similar to passing a function pointer, except that the lambda function effectively becomes a member of ArduinoOTAClass, rather than being external to it?

Exactly.

johnwasser:
It looks like a Lambda function "[] (...){...}" is just a function with no name.

It's even more than that, it can actually be a closure, it can capture variables from the scope in which it's contained.

int i = 0;
auto lambda = [&] () {
  Serial.println(i);
};
i = 42;
lambda(); // prints "42"

This means that, unlike "normal" functions, lambda functions are actually objects, and they have a lifetime, etc.

In the particular case where a lambda doesn't capture any variables, it can be converted to a normal function (pointer). So no object required to call a non-capturing lambda function.

Thanks - I hadn't understood capturing variable properly but this has helped a lot.

Does this mean that the Lambda function above is equavalent to this (in a very basic sense):

int i = 0;
void myFunc(int& var) {
  Serial.println(var);
};
i = 42;
myFunc(i); // prints "42"

i.e. it will always access the current value stored in i, and doesn't need to have a reference to i explicitly passed to it when called?

More or less, yes.

Basically, a lambda is just a struct that saves the captured values, either by reference, or by value.

The following snippets are pretty much equivalent:

        int i = 0;

        auto lambda1 = [&i] () { // capture i by reference
            Serial.println(i);
        };

        auto lambda2 = [i] () { // capture i by value
            Serial.println(i);
        };

        i = 42;
        lambda1(); // 42
        lambda2(); // 0
        int i = 0;

        struct { // DIY functor instead of a lambda, saves reference to i
            void operator()() const {
                Serial.println(i);
            }
            int &i;
        } functor1{i};

        struct { // DIY functor instead of a lambda, saves value of i
            void operator()() const {
                Serial.println(i);
            }
            int i;
        } functor2{i};

        i = 42;
        functor1(); // 42
        functor2(); // 0