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. C++ terseness challenge!

C++ terseness challenge!

Scheduled Pinned Locked Moved Solved C++ Gurus
14 Posts 6 Posters 1.4k 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

    As an exercise, I set myself a goal: given a QButtonGroup of QCheckBoxes --- non-exclusive, so multiple ones can be checked --- return a list/vector of which buttons/checkboxes are checked.

    I'm finding my way around some the std:: stuff. I'd like the "tersest"/shortest/neatest solution. This is for C++ less than C++20 (there's a reason for that, i.e. no std::ranges!).

    So here is what I have come up with:

        auto buttons = ui->checkButtonGroup->buttons();
        std::vector<QAbstractButton *> checkedButtons;
        std::copy_if(buttons.begin(), buttons.end(), std::back_inserter(checkedButtons), [](const QAbstractButton *btn) {
            return btn->isChecked();
        });
        return checkedButtons;
    

    Now, I am not a great fan of Python, but I have to admit here my (untested) solution would be:

    return [btn for btn in ui.checkButtonGroup.buttons() if btn.isChecked()]
    

    You have to admit, it's an awful lot shorter/simpler!

    So.... can any C++ guru offer anything "better" for C++ < 20?! :)

    JKSHJ 1 Reply Last reply
    0
    • Christian EhrlicherC Online
      Christian EhrlicherC Online
      Christian Ehrlicher
      Lifetime Qt Champion
      wrote on last edited by Christian Ehrlicher
      #2
      auto buttons = ui->checkButtonGroup->buttons();
      buttons.erase(std::remove_if(buttons.begin(), buttons.end(), [](const QAbstractButton *btn) { return btn->isChecked(); }), buttons.end());
      return buttons;
      

      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
      4
      • Christian EhrlicherC Christian Ehrlicher
        auto buttons = ui->checkButtonGroup->buttons();
        buttons.erase(std::remove_if(buttons.begin(), buttons.end(), [](const QAbstractButton *btn) { return btn->isChecked(); }), buttons.end());
        return buttons;
        
        JonBJ Offline
        JonBJ Offline
        JonB
        wrote on last edited by JonB
        #3

        @Christian-Ehrlicher
        This is interesting, but I regard it as "cheating" ;-)

        You are altering the QList<> returned by ui->checkButtonGroup->buttons(). In practice I might well want the function to take a const QList<> & parameter of the buttons, instead of passing in the QButtonGroup *. It's too late now to alter, perhaps instead of auto buttons = ui->checkButtonGroup->buttons(); I should have demanded you start with, say, const auto buttons ..., or equivalent. That stops your buttons.erase()/std::remove_if()!

        Nonetheless, your approach shows that it can be done shorter if we allow modification. I will buy you a warm English beer instead of a cold German beer for this :)

        I imagine this is as good as it gets from C++... I wish they had a Python equivalent!

        1 Reply Last reply
        0
        • JonBJ JonB

          As an exercise, I set myself a goal: given a QButtonGroup of QCheckBoxes --- non-exclusive, so multiple ones can be checked --- return a list/vector of which buttons/checkboxes are checked.

          I'm finding my way around some the std:: stuff. I'd like the "tersest"/shortest/neatest solution. This is for C++ less than C++20 (there's a reason for that, i.e. no std::ranges!).

          So here is what I have come up with:

              auto buttons = ui->checkButtonGroup->buttons();
              std::vector<QAbstractButton *> checkedButtons;
              std::copy_if(buttons.begin(), buttons.end(), std::back_inserter(checkedButtons), [](const QAbstractButton *btn) {
                  return btn->isChecked();
              });
              return checkedButtons;
          

          Now, I am not a great fan of Python, but I have to admit here my (untested) solution would be:

          return [btn for btn in ui.checkButtonGroup.buttons() if btn.isChecked()]
          

          You have to admit, it's an awful lot shorter/simpler!

          So.... can any C++ guru offer anything "better" for C++ < 20?! :)

          JKSHJ Offline
          JKSHJ Offline
          JKSH
          Moderators
          wrote on last edited by JKSH
          #4

          @JonB said in C++ terseness challenge!:

          C++ terseness .... stl...

          Qt is the thing that allows C++ to be terse and readable. "stl" and "terse" don't fit in the same sentence ;-P

          I imagine this is as good as it gets from C++... I wish they had a Python equivalent!

          What's the reason for excluding C++20 and std::ranges?

          Nonetheless, your approach shows that it can be done shorter if we allow modification.

          No modification required in Qt:

          return QtConcurrent::blockingFiltered( ui->checkButtonGroup->buttons(), [](const QAbstractButton *btn) {
              return btn->isChecked();
          });
          

          EDIT: Warning: This code technically violates the API rules, as widget methods are only allowed to be called from the GUI thread. However, isChecked() is a const lookup of a bitfield and blockingFiltered() blocks the GUI thread so it's harmless.

          Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

          JonBJ 1 Reply Last reply
          5
          • JKSHJ JKSH

            @JonB said in C++ terseness challenge!:

            C++ terseness .... stl...

            Qt is the thing that allows C++ to be terse and readable. "stl" and "terse" don't fit in the same sentence ;-P

            I imagine this is as good as it gets from C++... I wish they had a Python equivalent!

            What's the reason for excluding C++20 and std::ranges?

            Nonetheless, your approach shows that it can be done shorter if we allow modification.

            No modification required in Qt:

            return QtConcurrent::blockingFiltered( ui->checkButtonGroup->buttons(), [](const QAbstractButton *btn) {
                return btn->isChecked();
            });
            

            EDIT: Warning: This code technically violates the API rules, as widget methods are only allowed to be called from the GUI thread. However, isChecked() is a const lookup of a bitfield and blockingFiltered() blocks the GUI thread so it's harmless.

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

            @JKSH said in C++ terseness challenge!:

            What's the reason for excluding C++20 and std::ranges?

            Too easy? :) And too new!

            QtConcurrent::blockingFiltered(...)

            Oohh! That is indeed the sort of thing I was looking for, but did not know existed! (Never used QtConcurrent.) So Qt does have the sort of "filtering" which I looked for in std but did not find?!

            I don't mean to be picky now, but: I don't want/need anything to be done concurrently. Of course I can see why this is a useful QtConcurrent feature. But isn't there any Qt non-concurrent equivalent to this, because it's useful (for my question) to have this available for bog-standard serial programming?

            JKSHJ 1 Reply Last reply
            0
            • SGaistS Offline
              SGaistS Offline
              SGaist
              Lifetime Qt Champion
              wrote on last edited by
              #6

              Hi,

              blockingFiltered will block until everything has been processed.

              Interested in AI ? www.idiap.ch
              Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

              JonBJ 1 Reply Last reply
              0
              • SGaistS SGaist

                Hi,

                blockingFiltered will block until everything has been processed.

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

                @SGaist
                Yes, I do realize that! And I do realize that having blockingFiltered in QtConcurrent means that it can, in principle, take advantage of parallelism. I just would be happy with a plain, serial implementation which did not require concurrent/live in the concurrent namespace/library. So I wondered whether Qt had a non-concurrent method for filtering container-lists, but it seems not?

                1 Reply Last reply
                0
                • JonBJ JonB

                  @JKSH said in C++ terseness challenge!:

                  What's the reason for excluding C++20 and std::ranges?

                  Too easy? :) And too new!

                  QtConcurrent::blockingFiltered(...)

                  Oohh! That is indeed the sort of thing I was looking for, but did not know existed! (Never used QtConcurrent.) So Qt does have the sort of "filtering" which I looked for in std but did not find?!

                  I don't mean to be picky now, but: I don't want/need anything to be done concurrently. Of course I can see why this is a useful QtConcurrent feature. But isn't there any Qt non-concurrent equivalent to this, because it's useful (for my question) to have this available for bog-standard serial programming?

                  JKSHJ Offline
                  JKSHJ Offline
                  JKSH
                  Moderators
                  wrote on last edited by
                  #8

                  @JonB said in C++ terseness challenge!:

                  I don't mean to be picky now, but: I don't want/need anything to be done concurrently.... isn't there any Qt non-concurrent equivalent to this, because it's useful (for my question) to have this available for bog-standard serial programming?

                  Not that I know of, but I believe you can force your vector/list elements to be processed one at a time by calling QThreadPool::globalInstance()->setMaxThreadCount(1); beforehand.

                  P.S. You're not the only one who wants Qt Concurrent to not run concurrently

                  Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

                  JonBJ 1 Reply Last reply
                  2
                  • JKSHJ JKSH

                    @JonB said in C++ terseness challenge!:

                    I don't mean to be picky now, but: I don't want/need anything to be done concurrently.... isn't there any Qt non-concurrent equivalent to this, because it's useful (for my question) to have this available for bog-standard serial programming?

                    Not that I know of, but I believe you can force your vector/list elements to be processed one at a time by calling QThreadPool::globalInstance()->setMaxThreadCount(1); beforehand.

                    P.S. You're not the only one who wants Qt Concurrent to not run concurrently

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

                    @JKSH
                    I saw that one earlier on... :) No, I do not mean I do not want QtConcurrent to run concurrently! Like I said, I totally see why this is a useful method in concurrent. All I meant here, purely out of interest, is whether/why Qt does not bother to supply this convenience function against containers for the non-concurrent case. (Just to save me having to write it myself like in my code in first post in this thread.) Like the Python. That's all.

                    JKSHJ 1 Reply Last reply
                    0
                    • JonBJ JonB

                      @JKSH
                      I saw that one earlier on... :) No, I do not mean I do not want QtConcurrent to run concurrently! Like I said, I totally see why this is a useful method in concurrent. All I meant here, purely out of interest, is whether/why Qt does not bother to supply this convenience function against containers for the non-concurrent case. (Just to save me having to write it myself like in my code in first post in this thread.) Like the Python. That's all.

                      JKSHJ Offline
                      JKSHJ Offline
                      JKSH
                      Moderators
                      wrote on last edited by JKSH
                      #10

                      @JonB said in C++ terseness challenge!:

                      All I meant here, purely out of interest, is whether/why Qt does not bother to supply this convenience function against containers for the non-concurrent case.

                      I suspect there's no strong reason; it's just that nobody has wanted it badly enough to put it into Qt.

                      Now, given that QtAlgorithms is largely deprecated and C++20 has landed, I think it's highly unlikely that Qt will ever get a non-concurrent filter function.

                      No, I do not mean I do not want QtConcurrent to run concurrently!

                      My bad, I shouldn't jest like that :-D

                      Seriously though, there are legitimate reasons to avoid concurrent processing. For example, your program can crash if you use QtConcurrent to run a non-const function on the elements of a QList<QWidget*>, because widgets are not thread-safe.

                      Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

                      1 Reply Last reply
                      1
                      • S Offline
                        S Offline
                        SimonSchroeder
                        wrote on last edited by
                        #11

                        I would say that you already have one of the best solutions. I see basically 2 options: use declarative syntax like you did or use imperative syntax. By itself the language part of C++ is imperative programming, only the STL is more declarative. You want to copy elements under certain circumstances? std::copy_if exactly states this intent. I have learned that since C++11 you should use std::begin/end instead (or their constant counterparts, respectively):

                        std::copy_if(std::cbegin(buttons), std::cend(buttons), std::back_inserter(checkedButtons), [](const QAbstractButton *btn) {
                                     return btn->isChecked();
                            });
                        

                        This would make it more general that in certain cases you could also use an array instead of higher level data structures.

                        There is one more shortcut to your approach. The predicate of copy_if can be used directly:

                        std::copy_if(std::cbegin(buttons), std::cend(buttons), std::back_inserter(checkedButtons), std::mem_fn(&QAbstractButton::isChecked));
                        

                        isChecked is already a predicate. The only problem is that it is a member function, hence the use of mem_fn to wrap it.

                        Now to the second solution: imperative programming:

                        for(auto button : buttons)
                            if(button->isChecked())
                                checkButtons.push_back(button);
                        

                        To me this looks like a valid solution which at least is less to type. Since the loop is short I would judge this to be readable. This is down to taste if you prefer imperative or declarative programming.

                        Finally, even without using C++20 you could still use ranges as the proposal is based on an older implementation of this library: https://github.com/ericniebler/range-v3 . Would this "cheat" be allowed?

                        JonBJ 1 Reply Last reply
                        5
                        • S SimonSchroeder

                          I would say that you already have one of the best solutions. I see basically 2 options: use declarative syntax like you did or use imperative syntax. By itself the language part of C++ is imperative programming, only the STL is more declarative. You want to copy elements under certain circumstances? std::copy_if exactly states this intent. I have learned that since C++11 you should use std::begin/end instead (or their constant counterparts, respectively):

                          std::copy_if(std::cbegin(buttons), std::cend(buttons), std::back_inserter(checkedButtons), [](const QAbstractButton *btn) {
                                       return btn->isChecked();
                              });
                          

                          This would make it more general that in certain cases you could also use an array instead of higher level data structures.

                          There is one more shortcut to your approach. The predicate of copy_if can be used directly:

                          std::copy_if(std::cbegin(buttons), std::cend(buttons), std::back_inserter(checkedButtons), std::mem_fn(&QAbstractButton::isChecked));
                          

                          isChecked is already a predicate. The only problem is that it is a member function, hence the use of mem_fn to wrap it.

                          Now to the second solution: imperative programming:

                          for(auto button : buttons)
                              if(button->isChecked())
                                  checkButtons.push_back(button);
                          

                          To me this looks like a valid solution which at least is less to type. Since the loop is short I would judge this to be readable. This is down to taste if you prefer imperative or declarative programming.

                          Finally, even without using C++20 you could still use ranges as the proposal is based on an older implementation of this library: https://github.com/ericniebler/range-v3 . Would this "cheat" be allowed?

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

                          @SimonSchroeder
                          Thank you for your further tips.

                          One of my "goals" is (often) to get things down to no more than one line of typing/screen estate. (I don't think you can overestimate how useful to the programming experience it is to have as much code as possible visible in one vertical page when editing!). So your

                          std::copy_if(std::cbegin(buttons), std::cend(buttons), std::back_inserter(checkedButtons), std::mem_fn(&QAbstractButton::isChecked));
                          

                          is pleasing :)

                          The "irritation" for me of the standard for(auto button : buttons) loop comes when debugging the app. As you use "step over" in the calling code quickly you end up in a loop you have no interest in. Yes, you can then do a "run to" (if debugger has it), or place a breakpoint afterward and go "continue", but it's a pain. One day I'd like to write code which does not have a single for or while in any method (only factored away into sub-functions) so as to avoid this! Yes, I could write my own function for those 3 lines, but that's a hassle, and yet more lines of my own code to write.

                          Like I said, I'm not a great fan from my Python experience, but the [ something for something in somewhat if somesuch ], while looking a bit ugly, plus the variety of all()/any()/filter() built-ins, does make it easy to have one-liners to do what you want and step over in one go.

                          Kent-DorfmanK S 2 Replies Last reply
                          0
                          • JonBJ JonB

                            @SimonSchroeder
                            Thank you for your further tips.

                            One of my "goals" is (often) to get things down to no more than one line of typing/screen estate. (I don't think you can overestimate how useful to the programming experience it is to have as much code as possible visible in one vertical page when editing!). So your

                            std::copy_if(std::cbegin(buttons), std::cend(buttons), std::back_inserter(checkedButtons), std::mem_fn(&QAbstractButton::isChecked));
                            

                            is pleasing :)

                            The "irritation" for me of the standard for(auto button : buttons) loop comes when debugging the app. As you use "step over" in the calling code quickly you end up in a loop you have no interest in. Yes, you can then do a "run to" (if debugger has it), or place a breakpoint afterward and go "continue", but it's a pain. One day I'd like to write code which does not have a single for or while in any method (only factored away into sub-functions) so as to avoid this! Yes, I could write my own function for those 3 lines, but that's a hassle, and yet more lines of my own code to write.

                            Like I said, I'm not a great fan from my Python experience, but the [ something for something in somewhat if somesuch ], while looking a bit ugly, plus the variety of all()/any()/filter() built-ins, does make it easy to have one-liners to do what you want and step over in one go.

                            Kent-DorfmanK Offline
                            Kent-DorfmanK Offline
                            Kent-Dorfman
                            wrote on last edited by Kent-Dorfman
                            #13

                            @JonB Interesting that the first line of your previous made me think exactly of debugging issues. I spend a lot of time right now in ddd/gdb doing network protocol design so I need a lot of single line auto var operations as breakpoints to intelligently inspect state. It takes a while to customize but ddd is still the only gui gdb front end for me. I spent hours with resedit identifing X11 resources to override and trying to get an init gdb command file to batch execute my target app to the point where I expect the error to occur and automatically show the offending blobs. off course it also has to spred the separate displayes across my monitors in a fashion that eases my OCD.

                            I also cannot stand coding style rules that spread an if construct over two scrolled pages. Like you, I want to see a completely expressed idea in a concise manner on as few lines as possible.

                            1 Reply Last reply
                            1
                            • JonBJ JonB

                              @SimonSchroeder
                              Thank you for your further tips.

                              One of my "goals" is (often) to get things down to no more than one line of typing/screen estate. (I don't think you can overestimate how useful to the programming experience it is to have as much code as possible visible in one vertical page when editing!). So your

                              std::copy_if(std::cbegin(buttons), std::cend(buttons), std::back_inserter(checkedButtons), std::mem_fn(&QAbstractButton::isChecked));
                              

                              is pleasing :)

                              The "irritation" for me of the standard for(auto button : buttons) loop comes when debugging the app. As you use "step over" in the calling code quickly you end up in a loop you have no interest in. Yes, you can then do a "run to" (if debugger has it), or place a breakpoint afterward and go "continue", but it's a pain. One day I'd like to write code which does not have a single for or while in any method (only factored away into sub-functions) so as to avoid this! Yes, I could write my own function for those 3 lines, but that's a hassle, and yet more lines of my own code to write.

                              Like I said, I'm not a great fan from my Python experience, but the [ something for something in somewhat if somesuch ], while looking a bit ugly, plus the variety of all()/any()/filter() built-ins, does make it easy to have one-liners to do what you want and step over in one go.

                              S Offline
                              S Offline
                              SimonSchroeder
                              wrote on last edited by
                              #14

                              @JonB said in C++ terseness challenge!:

                              Yes, I could write my own function for those 3 lines

                              I once heard from a colleague that they sold their code to a different company. They had a rule that all functions were allowed to have at most 3 lines of code. So, they started intensive refactoring to meat that goal. In your case you did plan for a separate function in your small example. This would be a 6-line function which I would say is okay. But as I said, this is down to preference.

                              I thought that there were proposals to extend C++ so that the algorithm functions can take a container directly instead of its iterators. However, I couldn't find anything on that right away. I would immediately switch to this approach if it were slightly less verbose. Does anyone have information if we can now write this?

                              std::copy_if(buttons, std::back_inserter(checkedButtons), std::mem_fn(&QAbstractButton::isChecked));
                              

                              I thought this was already introduced with std::span, but couldn't find it on cppreference.com.

                              1 Reply Last reply
                              1

                              • Login

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