Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Special Interest Groups
  3. C++ Gurus
  4. static in lambda

static in lambda

Scheduled Pinned Locked Moved Unsolved C++ Gurus
18 Posts 5 Posters 2.8k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • JonBJ Offline
    JonBJ Offline
    JonB
    wrote on last edited by JonB
    #1

    Yes, I could doubtless look this up, but I always welcome explanations from the C++ experts here instead :)

    Friend @J-Hilk offered the following lambda over at https://forum.qt.io/topic/123558/how-do-i-know-that-the-client-has-connected-to-the-server/18

                QObject::connect(socket, &QTcpSocket::bytesWritten, socket, [socket, bytesToSend = data.size()](qint64 bytes)->void{
                    static qint64 bytesSend{0};
                    bytesSend += bytes;
                    if(static_cast<qint64>(bytesToSend) == bytesSend)
                        socket->close();
                });
    

    How "safe" is that static qint64 bytesSend{0}; in a lambda? I presume this is correctly supported in C++ as his code intends? "Where" is that static variable stored such that it is "local"/applies only to this lambda? If you were to call this connect code more than once, would it create multiple lambdas, would each one have its own static variable or would they share the same one?

    J.HilkJ 1 Reply Last reply
    0
    • Christian EhrlicherC Offline
      Christian EhrlicherC Offline
      Christian Ehrlicher
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Just try it out and print out the address of the static :)
      I would expect the same rules apply for unnamed and named functions here.

      Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
      Visit the Qt Academy at https://academy.qt.io/catalog

      JonBJ 1 Reply Last reply
      0
      • Christian EhrlicherC Christian Ehrlicher

        Just try it out and print out the address of the static :)
        I would expect the same rules apply for unnamed and named functions here.

        JonBJ Offline
        JonBJ Offline
        JonB
        wrote on last edited by JonB
        #3

        @Christian-Ehrlicher
        I don't have access to Qt today, and it might only tell me how gcc implements it. You guys being experts you could just tell me! :)

        I would expect the same rules apply for unnamed and named functions here.

        Then the code looks "naughty" to me! If the static were in a named function it would for sure be shared across multiple instances (sockets), and wouldn't do what @J-Hilk intends, unless it's only called once. Right?

        KroMignonK 1 Reply Last reply
        0
        • JonBJ JonB

          @Christian-Ehrlicher
          I don't have access to Qt today, and it might only tell me how gcc implements it. You guys being experts you could just tell me! :)

          I would expect the same rules apply for unnamed and named functions here.

          Then the code looks "naughty" to me! If the static were in a named function it would for sure be shared across multiple instances (sockets), and wouldn't do what @J-Hilk intends, unless it's only called once. Right?

          KroMignonK Offline
          KroMignonK Offline
          KroMignon
          wrote on last edited by
          #4

          @JonB said in static in lambda:

          You guys being experts you could just tell me! :)

          I am not an expert, but I've got issues with static in lambda and MS-VS2013.
          I don't remember exactly the problem, but it was not really working.

          It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

          JonBJ 1 Reply Last reply
          1
          • KroMignonK KroMignon

            @JonB said in static in lambda:

            You guys being experts you could just tell me! :)

            I am not an expert, but I've got issues with static in lambda and MS-VS2013.
            I don't remember exactly the problem, but it was not really working.

            JonBJ Offline
            JonBJ Offline
            JonB
            wrote on last edited by
            #5

            @KroMignon
            That is interesting. And if true might confirm this could differ between compiler implementations, as I suggested? Or perhaps VS 2013 is too old to be trusted, I don't know.

            1 Reply Last reply
            0
            • Christian EhrlicherC Offline
              Christian EhrlicherC Offline
              Christian Ehrlicher
              Lifetime Qt Champion
              wrote on last edited by Christian Ehrlicher
              #6

              VS2013 is simply not fully c++11 conform so ... :)

              Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
              Visit the Qt Academy at https://academy.qt.io/catalog

              1 Reply Last reply
              2
              • Chris KawaC Offline
                Chris KawaC Offline
                Chris Kawa
                Lifetime Qt Champion
                wrote on last edited by
                #7

                @JonB For the purpose of this example think of a lambda as a class with operator(). That static is just a normal static variable in member function scope so it behaves like such. It is accessible as long as instance of that lambda exists so as long as the connection does.

                If you wrote a separate connect with exactly the same lambda it would be a separate static, because each lambda (even if identical) is a separate "class" to the compiler. If you intended to reuse the same static you'd need to assign that lambda to na auto variable and reuse that in both connects.

                @KroMignon Lambdas in VS2013 are buggy and all sorts of non-conformant to the standard. Expect it to misbehave and definitely don't assume anything about the standard based on that compiler.

                JonBJ 1 Reply Last reply
                4
                • JonBJ JonB

                  Yes, I could doubtless look this up, but I always welcome explanations from the C++ experts here instead :)

                  Friend @J-Hilk offered the following lambda over at https://forum.qt.io/topic/123558/how-do-i-know-that-the-client-has-connected-to-the-server/18

                              QObject::connect(socket, &QTcpSocket::bytesWritten, socket, [socket, bytesToSend = data.size()](qint64 bytes)->void{
                                  static qint64 bytesSend{0};
                                  bytesSend += bytes;
                                  if(static_cast<qint64>(bytesToSend) == bytesSend)
                                      socket->close();
                              });
                  

                  How "safe" is that static qint64 bytesSend{0}; in a lambda? I presume this is correctly supported in C++ as his code intends? "Where" is that static variable stored such that it is "local"/applies only to this lambda? If you were to call this connect code more than once, would it create multiple lambdas, would each one have its own static variable or would they share the same one?

                  J.HilkJ Offline
                  J.HilkJ Offline
                  J.Hilk
                  Moderators
                  wrote on last edited by
                  #8

                  @JonB to be more confusing, you do not have to use a static at all let me introduce you to:

                      QCoreApplication app(argc, argv);
                  
                      auto bytesWritten = [bytesToSend = 64 ](int bytes)mutable ->void{
                          bytesToSend -= bytes;
                          if(bytesToSend <= 0){
                              qDebug() <<"finished";
                              qApp->exit();
                          }
                      };
                  
                      for( int i{0}; i < 8; i++){
                          bytesWritten(8);
                      }
                      return app.exec();
                  

                  Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                  Q: What's that?
                  A: It's blue light.
                  Q: What does it do?
                  A: It turns blue.

                  JonBJ Chris KawaC 2 Replies Last reply
                  2
                  • Chris KawaC Chris Kawa

                    @JonB For the purpose of this example think of a lambda as a class with operator(). That static is just a normal static variable in member function scope so it behaves like such. It is accessible as long as instance of that lambda exists so as long as the connection does.

                    If you wrote a separate connect with exactly the same lambda it would be a separate static, because each lambda (even if identical) is a separate "class" to the compiler. If you intended to reuse the same static you'd need to assign that lambda to na auto variable and reuse that in both connects.

                    @KroMignon Lambdas in VS2013 are buggy and all sorts of non-conformant to the standard. Expect it to misbehave and definitely don't assume anything about the standard based on that compiler.

                    JonBJ Offline
                    JonBJ Offline
                    JonB
                    wrote on last edited by JonB
                    #9

                    @Chris-Kawa
                    I don't understand enough of what you are saying, but from reading around I'm beginning to think that a static local variable in a lambda is equivalent to taking it outside the lambda, putting it into the calling function, and passing it by reference. So here:

                    static qint64 bytesSend{0};
                    QObject::connect(..., [&bytesSend]());
                    

                    In which case, if this connect() is called more than once they will all share the same bytesSend and code would not behave as intended (which is to count bytes sent on one socket).

                    ?

                    1 Reply Last reply
                    0
                    • J.HilkJ J.Hilk

                      @JonB to be more confusing, you do not have to use a static at all let me introduce you to:

                          QCoreApplication app(argc, argv);
                      
                          auto bytesWritten = [bytesToSend = 64 ](int bytes)mutable ->void{
                              bytesToSend -= bytes;
                              if(bytesToSend <= 0){
                                  qDebug() <<"finished";
                                  qApp->exit();
                              }
                          };
                      
                          for( int i{0}; i < 8; i++){
                              bytesWritten(8);
                          }
                          return app.exec();
                      
                      JonBJ Offline
                      JonBJ Offline
                      JonB
                      wrote on last edited by
                      #10

                      @J-Hilk said in static in lambda:

                      auto bytesWritten = [bytesToSend = 64 ](int bytes)mutable ->void{

                      Same question! Is that bytesToSend a separate variable for each call of this line or the same one? If it's separate, is that really the same as using static, as per my previous post just above? :)

                      1 Reply Last reply
                      0
                      • J.HilkJ J.Hilk

                        @JonB to be more confusing, you do not have to use a static at all let me introduce you to:

                            QCoreApplication app(argc, argv);
                        
                            auto bytesWritten = [bytesToSend = 64 ](int bytes)mutable ->void{
                                bytesToSend -= bytes;
                                if(bytesToSend <= 0){
                                    qDebug() <<"finished";
                                    qApp->exit();
                                }
                            };
                        
                            for( int i{0}; i < 8; i++){
                                bytesWritten(8);
                            }
                            return app.exec();
                        
                        Chris KawaC Offline
                        Chris KawaC Offline
                        Chris Kawa
                        Lifetime Qt Champion
                        wrote on last edited by Chris Kawa
                        #11

                        @JonB No, that's not how it works. It's closer to:

                        class SomeDummyClass
                        {
                           static qint64 bytesSend;
                           operator()(int bytes) { ... }
                        } dummy_instance;
                        
                        connect(..., &dummy_instance, SomeDummyClass::operator())
                        

                        so this will have separate statics:

                        connect(..., []{ static int foo; });
                        connect(..., []{ static int foo; });
                        

                        and this will share one:

                        auto func = []{ static int foo; };
                        connect(..., func);
                        connect(..., func);
                        

                        @J-Hilk Just to note - this requires C++14 but is a great replacement for such statics.

                        @JonB said:

                        Is that bytesToSend a separate variable for each call of this line or the same one?

                        It's equivalent to defining a non-static member in that class above so it's unique to an instance of the lambda.

                        JonBJ 1 Reply Last reply
                        3
                        • Chris KawaC Chris Kawa

                          @JonB No, that's not how it works. It's closer to:

                          class SomeDummyClass
                          {
                             static qint64 bytesSend;
                             operator()(int bytes) { ... }
                          } dummy_instance;
                          
                          connect(..., &dummy_instance, SomeDummyClass::operator())
                          

                          so this will have separate statics:

                          connect(..., []{ static int foo; });
                          connect(..., []{ static int foo; });
                          

                          and this will share one:

                          auto func = []{ static int foo; };
                          connect(..., func);
                          connect(..., func);
                          

                          @J-Hilk Just to note - this requires C++14 but is a great replacement for such statics.

                          @JonB said:

                          Is that bytesToSend a separate variable for each call of this line or the same one?

                          It's equivalent to defining a non-static member in that class above so it's unique to an instance of the lambda.

                          JonBJ Offline
                          JonBJ Offline
                          JonB
                          wrote on last edited by JonB
                          #12

                          @Chris-Kawa
                          Hmm, OK, then from what you are saying @J-Hilk is good, they will be separate statics....

                          But some post I was reading over at stackoverflow was saying something like: "Because the lambda definitions are identical in multiple calls, this means the static variable is as though it lives in the calling function and is passed (by reference) to the lambda", as per my code interpretation above. Which you are saying is not correct....

                          So I wonder where the separate instances of the static variable are actually stored, as we need a distinct one for each invocation....?

                          1 Reply Last reply
                          0
                          • Chris KawaC Offline
                            Chris KawaC Offline
                            Chris Kawa
                            Lifetime Qt Champion
                            wrote on last edited by Chris Kawa
                            #13

                            @JonB Well lambdas are their own thing so neither your nor mine is 100% correct, but mine is closer to how it works in terms of encapsulation and lifetime.
                            What @J-Hilk wrote is good and it doesn't use statics. A variable defined in the capture clause is a non-static member variable in that dummy class, so something along the lines of:

                            class SomeDummyClass
                            {
                               qint64 bytesSend = 64;
                               operator()(int bytes) { ... }
                            } dummy_instance;
                            

                            The statics are part of that dummy class, so follow the rules of the usual static class members. A lambda defined inline in the connect is just instantiating that dummy class, so like someFunction(QString()) - creating local instance in place. The difference is that lambdas don't have user visible type, so the DummyClass is "private" and exposed only internally to the compiler, so while this:

                            someFunc(QString());
                            someFunc(QString());
                            

                            would share a static between different QString instances does not apply to lambdas, becase this:

                            someFunc([]{});
                            someFunc([]{});
                            

                            is to the compiler this:

                            someFunc(SomeDummyClass1());
                            someFunc(SomeDummyClass2());
                            

                            so each lambda is its own separate class.

                            JonBJ 1 Reply Last reply
                            1
                            • Chris KawaC Chris Kawa

                              @JonB Well lambdas are their own thing so neither your nor mine is 100% correct, but mine is closer to how it works in terms of encapsulation and lifetime.
                              What @J-Hilk wrote is good and it doesn't use statics. A variable defined in the capture clause is a non-static member variable in that dummy class, so something along the lines of:

                              class SomeDummyClass
                              {
                                 qint64 bytesSend = 64;
                                 operator()(int bytes) { ... }
                              } dummy_instance;
                              

                              The statics are part of that dummy class, so follow the rules of the usual static class members. A lambda defined inline in the connect is just instantiating that dummy class, so like someFunction(QString()) - creating local instance in place. The difference is that lambdas don't have user visible type, so the DummyClass is "private" and exposed only internally to the compiler, so while this:

                              someFunc(QString());
                              someFunc(QString());
                              

                              would share a static between different QString instances does not apply to lambdas, becase this:

                              someFunc([]{});
                              someFunc([]{});
                              

                              is to the compiler this:

                              someFunc(SomeDummyClass1());
                              someFunc(SomeDummyClass2());
                              

                              so each lambda is its own separate class.

                              JonBJ Offline
                              JonBJ Offline
                              JonB
                              wrote on last edited by
                              #14

                              @Chris-Kawa said in static in lambda:

                              What @J-Hilk wrote is good and it doesn't use statics.

                              I was (originally) asking about his original code, which does use the static.

                              so each lambda is its own separate class.

                              I don't doubt that you are correct, the stackoverflow post was claiming that because the lambdas were identical (being invoked from the same line of code) this made them share the same, one class, like

                              someFunc(SomeDummyClass1());
                              someFunc(SomeDummyClass1());
                              

                              Maybe I misunderstood what I read there!

                              Chris KawaC 1 Reply Last reply
                              1
                              • JonBJ JonB

                                @Chris-Kawa said in static in lambda:

                                What @J-Hilk wrote is good and it doesn't use statics.

                                I was (originally) asking about his original code, which does use the static.

                                so each lambda is its own separate class.

                                I don't doubt that you are correct, the stackoverflow post was claiming that because the lambdas were identical (being invoked from the same line of code) this made them share the same, one class, like

                                someFunc(SomeDummyClass1());
                                someFunc(SomeDummyClass1());
                                

                                Maybe I misunderstood what I read there!

                                Chris KawaC Offline
                                Chris KawaC Offline
                                Chris Kawa
                                Lifetime Qt Champion
                                wrote on last edited by Chris Kawa
                                #15

                                @JonB Maybe they meant the case with lambda reuse via variable, like I posted above. This

                                someFunc(SomeDummyClass1());
                                someFunc(SomeDummyClass1());
                                

                                is simply not possible because you don't have access to the class, so you can't reuse it. You can only reuse an instance of that class (i.e. the auto variable). Think of lambdas as class that can only be instantiated once.

                                It's to the point that this won't work:

                                auto foo = []{};
                                auto bar = decltype(foo)();
                                

                                i.e. you can't try to query lambdas type and then create another instance of it.

                                JonBJ 1 Reply Last reply
                                0
                                • Chris KawaC Chris Kawa

                                  @JonB Maybe they meant the case with lambda reuse via variable, like I posted above. This

                                  someFunc(SomeDummyClass1());
                                  someFunc(SomeDummyClass1());
                                  

                                  is simply not possible because you don't have access to the class, so you can't reuse it. You can only reuse an instance of that class (i.e. the auto variable). Think of lambdas as class that can only be instantiated once.

                                  It's to the point that this won't work:

                                  auto foo = []{};
                                  auto bar = decltype(foo)();
                                  

                                  i.e. you can't try to query lambdas type and then create another instance of it.

                                  JonBJ Offline
                                  JonBJ Offline
                                  JonB
                                  wrote on last edited by JonB
                                  #16

                                  @Chris-Kawa
                                  Would you care to have a look at https://stackoverflow.com/a/60592393/489865. The interesting bit (excerpt):

                                  void bar() {
                                     auto f = []() { static int k = 1; cout << k++ << "\n";}; // define k as static
                                     f();
                                     f();
                                  }
                                  
                                  void test() {
                                     bar();
                                     bar();  // k is persistent through lambda instantiations
                                     return 0;
                                  }
                                  

                                  Is this correct? Am I misunderstanding, or does this mean one instance of static int k shared across multiple invocations? Wouldn't that make @J-Hilk's original code (at potentially) wrong, as separate sockets would all shre the same bytes-written count? Or, is this behaviour to do with the fact that in this example the lambda is stored in a variable auto f, and that makes it different from him passing it inline to connect()?

                                  My brain hurts! This was suppsoed to eb a quick question, I'm spending all morning discussing it :)

                                  1 Reply Last reply
                                  0
                                  • Chris KawaC Offline
                                    Chris KawaC Offline
                                    Chris Kawa
                                    Lifetime Qt Champion
                                    wrote on last edited by
                                    #17

                                    @JonB Yeah, I guess I went too far in my class analogy :) That's because lambdas are a little different and it's not exactly that there is only one instance of it. It's more that the compiler is the only one allowed to name its type, not user in their code. So you're right - this example will share the static because the type of the lambda is the same in this case. Note however that it's not the same as

                                    auto f = []() { static int k = 1; cout << k++ << "\n";}; // define k as static
                                    f();
                                    auto f2 = []() { static int k = 1; cout << k++ << "\n";}; // define k as static
                                    f2();
                                    

                                    these lambdas, although look the same are of different types internally.

                                    JonBJ 1 Reply Last reply
                                    0
                                    • Chris KawaC Chris Kawa

                                      @JonB Yeah, I guess I went too far in my class analogy :) That's because lambdas are a little different and it's not exactly that there is only one instance of it. It's more that the compiler is the only one allowed to name its type, not user in their code. So you're right - this example will share the static because the type of the lambda is the same in this case. Note however that it's not the same as

                                      auto f = []() { static int k = 1; cout << k++ << "\n";}; // define k as static
                                      f();
                                      auto f2 = []() { static int k = 1; cout << k++ << "\n";}; // define k as static
                                      f2();
                                      

                                      these lambdas, although look the same are of different types internally.

                                      JonBJ Offline
                                      JonBJ Offline
                                      JonB
                                      wrote on last edited by JonB
                                      #18

                                      @Chris-Kawa said in static in lambda:

                                      these lambdas, although look the same are of different types internally.

                                      Yes, that I do realise! Just as in C/C++ struct { int foo; } and another struct { int foo; } are not the same type. (But I think they are in Pascal-like languages....)

                                      So you're right - this example will share the static because the type of the lambda is the same in this case

                                      Which I think would make @J-Hilk's original not behave as anticipated if more than connect() is made! :)

                                      1 Reply Last reply
                                      0

                                      • Login

                                      • Login or register to search.
                                      • First post
                                        Last post
                                      0
                                      • Categories
                                      • Recent
                                      • Tags
                                      • Popular
                                      • Users
                                      • Groups
                                      • Search
                                      • Get Qt Extensions
                                      • Unsolved