Author Topic: C++ question: Use (and abuse) of lambdas in a code. AKA request for critics  (Read 2164 times)

0 Members and 1 Guest are viewing this topic.

Offline martinribelottaTopic starter

  • Regular Contributor
  • *
  • Posts: 56
  • Country: ar
  • A camel is a horse designed by a committee
    • Martin Ribelotta design services
Hi everyone, I ar working in C++ code using Qt library... Lately, I've been using and abusing the qt to lambdas signals by trapping "this" ([...this...] (...) {...})

The link of interest code is here:
https://github.com/martinribelotta/gdbfrontend/blob/master/widget.cpp#L135

In some cases, the usage of lambdas y very nice to localize logic and condensate code in a single function like this:

Code: [Select]
    commandAndResponse(QString{"-var-update --all-values %1"}.arg(name), [this](const QVariant& r) {
        auto changeList = r.toMap().value("changelist").toList();
        QStringList changedNames;
        for(const auto& e: changeList) {
            auto m = e.toMap();
            /// Some of bullshit here...
            self->varsWatched.insert(name, var);
        }
        emit variablesChanged(changedNames);
    });

I'm good with the code above but not happy with the spaghetti code in the main widget constructor...

My question (and request for critics) is:

  • Is time to return to old-school signal/slot in separated functions?
  • Anyone know some elegant form of clean the spaghetti-lambda code?
  • Some others tools like rxcpp for reactive programming and localized code?

In short, my idea is to put the code for review and comments. I'm not happy with the spaghetti in the main widget, but maybe some refactoring do good results...

Last: The code is a gdb frontend using Qt with embedded C/C++ in mind...
 

Offline maginnovision

  • Super Contributor
  • ***
  • Posts: 1963
  • Country: us
For me that just looks like it should be a standard function. Is it otherwise going to clean up the code, make it easier to read, or somehow optimize its run time?
 
The following users thanked this post: martinribelotta

Offline lone wolf1

  • Newbie
  • Posts: 5
  • Country: gb
Not used QT myself but have used C++. 
I second what the other person said.

In that you are accessing self and doing a multiple of things inside the lambda.  I would simplify the lambda and just call a method from inside the lambda.
 
The following users thanked this post: martinribelotta

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14309
  • Country: fr
The question itself would probably be better answered on a very specialized forum.

So at least for the less informed ones, now that you raised the topic, can anyone explain what are "lambdas" in C++, how and what they are used for?
 

Offline maginnovision

  • Super Contributor
  • ***
  • Posts: 1963
  • Country: us
https://en.cppreference.com/w/cpp/language/lambda
Quote
Constructs a closure: an unnamed function object capable of capturing variables in scope.

I personally use lambdas almost exclusively for spawning threads. but it's not the only time you'd use them. Bjarne Stroustrup is of a mind that virtually anything non-trivial should have its own named function.

Code: [Select]
thread save([](BMP* ss) {ss->write_image(); delete ss; }, image);
save.detach();
« Last Edit: February 25, 2020, 09:40:13 pm by maginnovision »
 
The following users thanked this post: SiliconWizard

Offline ve7xen

  • Super Contributor
  • ***
  • Posts: 1192
  • Country: ca
    • VE7XEN Blog
To expand a little bit, a lambda is also known as an anonymous function or closure. It's a function defined inline with no name, and that can optionally import some variables from the scope it's defined in. In C++ they are quite useful with the algorithms in STL, so you can call things like std::for_each with the handler defined inline and having access to local scope. Also excellent in cases like OP's where there is an event library that expects 'function pointers' as handlers, and you're gonna need lots of them, which gets cumbersome if you have to declare and name them when all they do is change one variable in the class or something.

They're a convenience more than anything else, but very happy they are now (hah...for 9 years now) in C++. With lambdas, the auto type (type inference), constexpr and static if, initializer lists, proper fixed size arrays, 'foreach' (called 'range based') loops... I could probably go on... C++11 is pretty compelling for embedded too, there are a lot of quality of life advantages over C once you get a handle on this stuff, and it doesn't really 'cost' anything.

With regard to OP's query, I would probably refactor most of these into named functions, because they perform some task that is independent of how they are invoked,  which you may want to invoke in another manner, and they don't seem to need access to any of the local context. Then just have your lambda just call the member function, or if it needs a loop you could use for_each.
« Last Edit: February 25, 2020, 07:35:29 pm by ve7xen »
73 de VE7XEN
He/Him
 

Offline martinribelottaTopic starter

  • Regular Contributor
  • *
  • Posts: 56
  • Country: ar
  • A camel is a horse designed by a committee
    • Martin Ribelotta design services
The question itself would probably be better answered on a very specialized forum.

So at least for the less informed ones, now that you raised the topic, can anyone explain what are "lambdas" in C++, how and what they are used for?

Additionaly to others responses, the lambdas (in all my known) is implemented as anonymous class with operator() implemented with the body of the lambda, and the capture list as member variables initialized in the constructor...

By example, this lambda code:
Code: [Select]
#include <iostream>

int main() {
    int sum;
    auto accu = [&sum](int n) { sum += n; };

    sum = 0;
    for (int i=1; i<4; i++) accu(i);
    std::cout << sum << "\n";
}

can be translated to C++-pre11 as:
Code: [Select]
#include <iostream>

int main() {
    class Accu_t {
        int& sum;
    public:
        Accu_t(int& ref): sum(ref) {}
        void operator() (int n) const { sum += n; }
    };
    int sum;
    Accu_t accu(sum);
    sum = 0;
    for (int i=1; i<4; i++) accu(i);
    std::cout << sum << "\n";
}

Yes, the code is bullshit, but you can see the paralelism 1by1
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14309
  • Country: fr
I see. So yes, they are basically closures, which exist in other languages. Not sure how I feel about them introduced in C++, and about closures in general, but that's a different debate. They can certainly make some things more elegant to write. They do pose potential "safety" issues IMO though, but yeah that would be another topic.

 

Offline SethGI

  • Regular Contributor
  • *
  • Posts: 51
  • Country: us
This is likely more of a philosophical response than you wanted, but here goes.

There will always be many (infinitely many) ways to make your code behave as desired. In fact, many of them will compile the exact same way. The trap of languages like C++ is (by design) you can get away with pretty much anything, and the code will often work. This doesn't make it *good* code.

When writing in such an open-ended language, choosing methods that are easy to read and debug is super important both for yourself and for other people down the line. To me, that could be a normal function. I see why you like it as is, but I'd argue it's worth the programming overhead of just writing it as a bog-standard function to save your future self and any potential future collaborators from dealing with some very confusing-to-read code.
 

Offline hulk69

  • Contributor
  • Posts: 22
  • Country: fr
For me readability is EVERYTHING.
the less time you have to figure out what the coed does the better.

It looks like you are using some function of the C++ language for no particular reasons other than to learn them.

Readability 0/10
 

Offline greenpossum

  • Frequent Contributor
  • **
  • Posts: 408
  • Country: au
Every language feature, except perhaps the most obvious, will be unreadable to somebody. Sometimes the solution really is hard and readers have to get up to speed with the developer. Not just for languages but also for libraries, frameworks, any organised body of code.

Ask yourself why you want to use a particular feature. If there is no gain from it for your code, then perhaps you are just using it because you can. I can see that lambdas can be useful to avoid creating more identifiers, which might be important in automatically generated or expanded code. You can find a list of uses in the Wikipedia entry for anonymous functions.

Some languages use them extensively, the one I have in mind is Javascript. I always had to indent code appropriately to see the event handlers, for example, being passed to setup code.
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14309
  • Country: fr
Every language feature, except perhaps the most obvious, will be unreadable to somebody. Sometimes the solution really is hard and readers have to get up to speed with the developer.

That's right. And this is why complex languages such as modern C++ are a problem. They just contain too many features, which leads to almost every developer using their own subset - which makes the language "fragmented" and hard to understand to others.

There's no real answer to this if you DO use C++. I mean, suggesting never to use lambdas or such or such feature would be vain IMO. There are just so many features to choose from. That's why I favor simpler languages.
 

Online Mechatrommer

  • Super Contributor
  • ***
  • Posts: 11537
  • Country: my
  • reassessing directives...
i didnt aware about lambda before but doing fine programming in C/C++, just because its much more complicated doesnt make it unsuitable for my use. lets take Altium Designer for example, there are alot of features that i'm not yet aware of other than making circuit and simple pcb, but that doesnt justify i have to go back to KiCAD. when i need it or have some problem/limitation in C/C++ i'll ask in forum and i will be glad if there is an answer/solution. i dont like the answer such as... your program cant do that, use and learn another program. if you've built libraries at significant size in your existing program/IDE/EDA etc, you'll know how PITA it is trying to learn new system. learning it is not the problem, trying to port your existing libraries is... mind you i tried to learn Qt for several years now i think i got a grasp on it, language is not the problem i've been programming C/C++ in VC++ since ages for creating dll. but when trying to start making a simple program (Qt Windows GUI), thinking oh i need this module.. pieces chunk of code but... they are in VB :palm: so i have to go back to VB where i can snap a functional program in few minutes, where all my libraries are. ymmv.
Nature: Evolution and the Illusion of Randomness (Stephen L. Talbott): Its now indisputable that... organisms “expertise” contextualizes its genome, and its nonsense to say that these powers are under the control of the genome being contextualized - Barbara McClintock
 

Offline alanambrose

  • Frequent Contributor
  • **
  • Posts: 377
  • Country: gb
Yeah +1 for:

"write it like you expect the most junior member on the team to be able to understand it / amend it / debug it"
“A foolish consistency is the hobgoblin of little minds"
 

Offline martinribelottaTopic starter

  • Regular Contributor
  • *
  • Posts: 56
  • Country: ar
  • A camel is a horse designed by a committee
    • Martin Ribelotta design services
Quote
Yeah +1 for:

"write it like you expect the most junior member on the team to be able to understand it / amend it / debug it"

This is good rule when you known the team members... but in a colaborative project like open source:

Waht is the desired level of programming to ensure good colaboration????

Maybe this loss the point of original question but the focus is nearly valid...

If you mess the code with esoteric characteristics, the colaboration turn into impossible chalenge, but if your code is "correct" but very disperse to maintain, the bugs appear day by day...

In my understand, lambdas in C++ is good when you try to maintain atomicity of the code in a little place... when your code grow to few lines, the lambdas will be a chauvinistic tool because force the code to be too-many-indented... Obviously, this is a rule of thumb from my experience, but I see this replicated in many forms in others languages (like java or javascript)

I have two projects with use-and-abuse of lambdas:

https://github.com/martinribelotta/embedded-ide

And

https://github.com/martinribelotta/gdbfrontend

In the last project, the use-of-lambdas have a sense but in embedded-ide use is abuse, and need some refactoring (eventually jejejeje)
A sample of abuse-of-lambdas: https://github.com/martinribelotta/embedded-ide/blob/master/ide/clangautocompletionprovider.cpp#L216

My tentative approach is identificate the piece of codes with independent entity and break the lambdas in individual functions. Many of this can move to methods in impl object (maybe many pimpl objects) and others can be move to public API.

Well, I see the point more clean in this area and can put my effort in productive direction with the lambda problems even though it is not a topic with a direct answer....
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf