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!
Forum Updated to NodeBB v4.3 + New Features

C++ terseness challenge!

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