How to send a parameter to lambda function if used in timer.setTimeout

Hi. I want to use a lambda function as a second arg in Blynk’s timer.setTimeout function, and to pass some variable(s) to it in such a way, that the values used in lambda must be exactly the same as at the moment they were passed. Even if the values of the variables changed before the timeout expires. In other words, I want to pass values, not pointers. So, in the example below, ‘45’ should be written to serial port instead of ‘99’.
I googled this issue and concluded that the answer should be like “use [=]() instead of [&]()”. But this doesn’t work. I get 99 instead of 45 in serial port.

    #include <BlynkSimpleEsp8266_SSL.h>

    BlynkTimer timer;
    int testParameter = 45;

    void setup()
    {
      Serial.begin(115200);

      //setTimeout gives an error if declare testParameter as local and use it in lambda
      //int testParameter = 45;

      timer.setTimeout(1000L, [ = ]()  {
        Serial.println(testParameter);
      } );
      testParameter = 99;
    }

    void loop()
    {
      timer.run();
    }

The second issue is about declaration of the variable passed. If it is declared not as global I got an error: "no matching function for call to 'BlynkTimer::setTimeout(long int, setup()::__lambda0)'"
It would be great if someone clears this out to me, too. Thanks!

P.S. Please, don’t ask me why I want to use this and why not to use setTimeout with the 4th arg as a parameter (because Blynk’s timer library allows to make this). By the way, the sketch above is intentionally written just as a simple example with no useful functionality.
P.P.S. And one more, I found another topic with nearly the same question created much earlier (How can I pass parameters to lamba timer expression), but the author didn’t get the answer to his initial question and the topic went in another way. That’s why I created a new one and hope to get the answer here))

I can’t see that working without creating another int with the value that you actually want to send as when the timer triggers the value of testParameter has changed.

Maybe:

#include <BlynkSimpleEsp8266_SSL.h>

BlynkTimer timer;
int testParameter = 45;

void setup()
{
  Serial.begin(115200);

  //setTimeout gives an error if declare testParameter as local and use it in lambda
  int tempParameter = testParameter;

  timer.setTimeout(1000L, [ = ]()  {
    Serial.println(tempParameter);
  } );
  testParameter = 99;
}

void loop()
{
  timer.run();
}

Thanks for you reply! But this gives the same error as if just use local var. I mentioned it

Ah yes, sorry about that.

You would have to declare that globally too then (tested this - working):

#include <BlynkSimpleEsp8266_SSL.h>

BlynkTimer timer;
int testParameter = 45;
int tempParameter = 0;

void setup()
{
  Serial.begin(115200);

  //setTimeout gives an error if declare testParameter as local and use it in lambda
  tempParameter = testParameter;

  timer.setTimeout(1000L, [ = ]()  { 
    Serial.println(tempParameter);
  } );
  testParameter = 99;
}

void loop()
{
  timer.run();
}

Yep, I understood what you wanted to say)) But the point is that using another var as temp will not help. Assume that timer is created not in setup func, but in some repeatedly (every 50msec for example) called func (when something triggers to create timer in that func). In that case when testParameter changes, tempParameter changes, too, before the lambda gets in play.

Here is another example sketch, that is closer to the point (but more complicated, that’s why I used the first simple sketch):

#include <BlynkSimpleEsp8266_SSL.h>

BlynkTimer timer;
int testParameter; //var that need to be passed to lambda by its value not pointer
bool setTimer=true; //helps to set timer with lambda only once

void setup()
{
  Serial.begin(115200);
  //Let's call some function twice in period of 200msec
  timer.setTimer(200L,someFunc,2);
}

void loop()
{
  timer.run();
}

void someFunc () {
  //just simple condition triggered at first call of someFunc (or any other condition in common case)
  if (setTimer==true) {
    testParameter = 45;
    Serial.println("Initial parameter is:" + (String)testParameter);
    //we set timer that triggers after 1000ms and needs to use 45
    timer.setTimeout(1000L, [ = ]()  {
      Serial.println("Lambda gets:" + (String)testParameter);
    } );
  } else {
    testParameter = 99; //but testParameter changes at 2nd call of someFunc, i.e. after 200ms (any moment early than 1 sec in common case)
    Serial.println("Some function changed parameter to:" + (String)testParameter);
  }
  setTimer=false;
}

Anyway, this question is more about c++ itself, I assume, and not about some sketch tricks or smth. I just don’t understand why lambda gets changed value and not the initial one in spite of the reason that [=] means to get vars by value.

Yes I understand now. (Not the fix, but the problem)

It is as though the lambda ignores the capture flag, as in the links from the other thread " *this " should copy the values also but this causes an error as you had before.

This is over my head I’m afraid, hopefully someone else can help? Or maybe worth posting on Stackoverflow?

Okey, Everyone! And Blynk’s devs especially (@Dmitriy ))) I think I found the answer.
Remember, I said that using local variable in lambda function results in error?

So, probably BlynkTimer does not support lambda (as far as the way it should be), because it is a fork of Marcello Romani’s Simple timer, that neither does. But there is another fork on github, where the support of lambdas stated as a feature: GitHub - schinken/SimpleTimer: Fork of Arduino SimpleTimer Library (http://playground.arduino.cc/Code/SimpleTimer)
I looked through it a little, and the only significant difference with the original one is just in the way of timer_callback’s typedefs. But for Blynk timer some additional changes also needed. As a result, supporting std::function or lambda-expressions known from C++11 will get an ability to pass local vars to lambda.

I tried it with SimpleTimer fork in spite of BlynkTimer and, finally, I got the functionality of my over-simplified initial sketch as I wanted to (by just adding and passing a local var):

Simple sketch code
#include <SimpleTimer.h>

SimpleTimer timer;
int testParameter = 45;

void setup()
{
  Serial.begin(115200);

  int tempPar=testParameter;
  timer.setTimeout(1000L, [=]()  {
    Serial.println(tempPar);
  } );
  testParameter = 99;
}

void loop()
{
  timer.run();
}

//Get 45 in lambda output, not 99

Or in more usefull way with repeatedly called function:

improved sketch code
//#include <BlynkSimpleEsp8266_SSL.h>
#include <SimpleTimer.h>

//BlynkTimer timer;
SimpleTimer timer;

int testParameter=45; //var that need to be passed to lambda by its value not pointer
bool setTimer=true; //helps to set timer with lambda only once

void setup()
{
  Serial.begin(115200);
  //Let's call some function twice in period of 200msec
  timer.setTimer(200L,someFunc,2);
}

void loop()
{
  timer.run();
}

void someFunc () {
  int tempPar;
  //just simple condition triggered at first call of someFunc (or at any other moment in common case)
  if (setTimer==true) {
    tempPar = testParameter;
    Serial.println("Initial parameter is:" + (String)tempPar);
    //we set timer that triggers after 1000ms and needs to use 45 even if the original var changes its value
    timer.setTimeout(1000L, [=]()  {
      Serial.println("Lambda uses value:" + (String)tempPar);
    } );
    tempPar=70; //local var can be changed immediately with no affect to lambda
    Serial.println("Some function changed local parameter to:" + (String)tempPar);
  } else {
    testParameter = 99; //testParameter changes at 2nd call of someFunc, i.e. after 200ms (any moment early than 1 sec in common case)
    tempPar=testParameter;
    Serial.println("Some function changed global parameter to:" + (String)tempPar);
  }
  setTimer=false;
}

//Get 45 in lambda output even after global and local vars changed

So, Blynk developers, please change several lines in BlynkTimer’s lib to add lambdas compatibility to BlynkTimer. I’ll also create an issue on Github.

3 Likes

Glad you got it sorted.

I’ve been pulling my hair out all afternoon trying to figure it out. It would be a useful feature to have actually now I’ve been playing around with it.

:+1:t2: