Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Proper use of QPrivateSignal

Proper use of QPrivateSignal

Scheduled Pinned Locked Moved Unsolved General and Desktop
8 Posts 3 Posters 4.9k 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.
  • NathanielN Offline
    NathanielN Offline
    Nathaniel
    wrote on last edited by
    #1

    Recently I encountered a situation where I wanted to encapsulate emitting a signal with a (non signal/slot) method, and I decided I should prevent any code from emitting that signal except via the wrapper method. This led me to examine the QPrivateSignal mechanism (I hadn't really paid attention to it before). I found documentation about QPrivateSignal to be misleading, because there was a general implication that QPrivateSignal being a "private" struct would break client code trying to emit (i.e., call) a signal because it would need to supply a QPrivateSignal argument, and the constructor QPrivateSignal() wouldn't compile. That's true enough, but I got no compiler error with an empty initializer list: i.e. Q_EMIT done({}) (for signal "done()") would compile (in this context of course I want a compiler error here!). This seems like a trivial way to "break" the encapsulation supposedly available via QPrivateSignal. I found that I could get something like how I expected QPrivateSignal to work by defining the following macro:

    #define  USE_PRIVATE_SIGNALS \
    class _QPrivateSignal_ { \
    friend struct QPrivateSignal; \
    public: explicit _QPrivateSignal_\
    (QPrivateSignal = {}){} };
    

    Then I used this _QPrivateSignal_ class instead of QPrivateSignal in my signal declaration: void done(_QPrivateSignal_); and the "wrapper" was akin to: void emit_done(){ Q_EMIT done(_QPrivateSignal_{}); }. This seemed to do the trick in that I couldn't get any call to the signal to compile outside a class member of the relevant QObject subclass. But I'm wondering: am I missing something? Is there a reason the Q_OBJECT macro itself doesn't use this "extra layer" to hide the empty-initializer possibility? Or does my solution have some downside I'm not seeing (there seems to be no problem connecting the signal to lambda handlers)?

    KroMignonK 1 Reply Last reply
    1
    • jeremy_kJ Offline
      jeremy_kJ Offline
      jeremy_k
      wrote on last edited by jeremy_k
      #2

      That's interesting. I would have guessed that out of class aggregate initialization of a private class would have broken, but can't find anything to support or deny the suspicion. I also don't see a matching bug report at https://bugreports.qt.io/. Filing a report might net an explanation.

      Changing the QPrivateSignal definition in qobjectdefs.h to make the constructor explicit works for me. There's no need to introduce a level of indirection.

      Asking a question about code? http://eel.is/iso-c++/testcase/

      1 Reply Last reply
      0
      • NathanielN Nathaniel

        Recently I encountered a situation where I wanted to encapsulate emitting a signal with a (non signal/slot) method, and I decided I should prevent any code from emitting that signal except via the wrapper method. This led me to examine the QPrivateSignal mechanism (I hadn't really paid attention to it before). I found documentation about QPrivateSignal to be misleading, because there was a general implication that QPrivateSignal being a "private" struct would break client code trying to emit (i.e., call) a signal because it would need to supply a QPrivateSignal argument, and the constructor QPrivateSignal() wouldn't compile. That's true enough, but I got no compiler error with an empty initializer list: i.e. Q_EMIT done({}) (for signal "done()") would compile (in this context of course I want a compiler error here!). This seems like a trivial way to "break" the encapsulation supposedly available via QPrivateSignal. I found that I could get something like how I expected QPrivateSignal to work by defining the following macro:

        #define  USE_PRIVATE_SIGNALS \
        class _QPrivateSignal_ { \
        friend struct QPrivateSignal; \
        public: explicit _QPrivateSignal_\
        (QPrivateSignal = {}){} };
        

        Then I used this _QPrivateSignal_ class instead of QPrivateSignal in my signal declaration: void done(_QPrivateSignal_); and the "wrapper" was akin to: void emit_done(){ Q_EMIT done(_QPrivateSignal_{}); }. This seemed to do the trick in that I couldn't get any call to the signal to compile outside a class member of the relevant QObject subclass. But I'm wondering: am I missing something? Is there a reason the Q_OBJECT macro itself doesn't use this "extra layer" to hide the empty-initializer possibility? Or does my solution have some downside I'm not seeing (there seems to be no problem connecting the signal to lambda handlers)?

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

        @Nathaniel Maybe I am wrong, what I understand is that you have to create a private signal for your class which can only be emitted from the class instance itself.

        This is pretty simple:

        class MyClass: public QObject
        {
            Q_OBJECT
        public:
            explicit MyClass(QObject *parent = nullptr): QObject(parent) {}
            
        public slots:
            void doWork()
           {
                // emit the public signal
                emit publicSignal(25);
                // emit the private signal
                emit privateSignal(25, QPrivateSignal());
           }
        signals:
             void publicSignal(int data);
             void privateSignal(int data, QPrivateSignal);
        };
        

        This way, anyone can call MyClass::publicSignal() but only MyClass instance can call privateSignal(), and each signal can be connected to the same slot:

        auto k = new MyClass();
        
        connect(k, &MyClass::publicSignal, this, [](int data) { qDebug() << "received Public:" << data; });
        connect(k, &MyClass::privateSignal, this, [](int data) { qDebug() << "received Private:" << data; });
        

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

        jeremy_kJ 1 Reply Last reply
        0
        • KroMignonK KroMignon

          @Nathaniel Maybe I am wrong, what I understand is that you have to create a private signal for your class which can only be emitted from the class instance itself.

          This is pretty simple:

          class MyClass: public QObject
          {
              Q_OBJECT
          public:
              explicit MyClass(QObject *parent = nullptr): QObject(parent) {}
              
          public slots:
              void doWork()
             {
                  // emit the public signal
                  emit publicSignal(25);
                  // emit the private signal
                  emit privateSignal(25, QPrivateSignal());
             }
          signals:
               void publicSignal(int data);
               void privateSignal(int data, QPrivateSignal);
          };
          

          This way, anyone can call MyClass::publicSignal() but only MyClass instance can call privateSignal(), and each signal can be connected to the same slot:

          auto k = new MyClass();
          
          connect(k, &MyClass::publicSignal, this, [](int data) { qDebug() << "received Public:" << data; });
          connect(k, &MyClass::privateSignal, this, [](int data) { qDebug() << "received Private:" << data; });
          
          jeremy_kJ Offline
          jeremy_kJ Offline
          jeremy_k
          wrote on last edited by jeremy_k
          #4

          @KroMignon I believe OP is referring to being able to call a function taking a QPrivateSignal using the aggregate initializer for the corresponding QPrivateSignal.

          #include <QTimer>
          
          int main(int argc, char *argv[])
          {
              QTimer timer;
              emit timer.timeout({});
              return 0;
          }
          

          Asking a question about code? http://eel.is/iso-c++/testcase/

          KroMignonK 1 Reply Last reply
          0
          • jeremy_kJ jeremy_k

            @KroMignon I believe OP is referring to being able to call a function taking a QPrivateSignal using the aggregate initializer for the corresponding QPrivateSignal.

            #include <QTimer>
            
            int main(int argc, char *argv[])
            {
                QTimer timer;
                emit timer.timeout({});
                return 0;
            }
            
            KroMignonK Offline
            KroMignonK Offline
            KroMignon
            wrote on last edited by
            #5

            @jeremy_k said in Proper use of QPrivateSignal:

            I believe OP is referring to being able to call a function taking a QPrivateSignal using the aggregate initializer for the corresponding QPrivateSignal.

            Sorry, perhaps my english to not good enough, but I don't understand what your are meaning here.

            QPrivateSignal() struct is defined with Q_OBJECT macro.
            This will allow you to mark as signal as 'private', which means it can only be emitted from the class itself, not from outside.
            The private signal must use as last parameter QPrivateSignal, so it can only be called from class itself because no other class can create an instance of QPrivateSignal.
            This signal is still accessible from outside, but only to be connected with any slot which have same signature (except last parameter of course).

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

            jeremy_kJ 1 Reply Last reply
            0
            • KroMignonK KroMignon

              @jeremy_k said in Proper use of QPrivateSignal:

              I believe OP is referring to being able to call a function taking a QPrivateSignal using the aggregate initializer for the corresponding QPrivateSignal.

              Sorry, perhaps my english to not good enough, but I don't understand what your are meaning here.

              QPrivateSignal() struct is defined with Q_OBJECT macro.
              This will allow you to mark as signal as 'private', which means it can only be emitted from the class itself, not from outside.
              The private signal must use as last parameter QPrivateSignal, so it can only be called from class itself because no other class can create an instance of QPrivateSignal.
              This signal is still accessible from outside, but only to be connected with any slot which have same signature (except last parameter of course).

              jeremy_kJ Offline
              jeremy_kJ Offline
              jeremy_k
              wrote on last edited by jeremy_k
              #6

              @KroMignon said in Proper use of QPrivateSignal:

              @jeremy_k said in Proper use of QPrivateSignal:

              I believe OP is referring to being able to call a function taking a QPrivateSignal using the aggregate initializer for the corresponding QPrivateSignal.

              Sorry, perhaps my english to not good enough, but I don't understand what your are meaning here.

              QPrivateSignal() struct is defined with Q_OBJECT macro.
              This will allow you to mark as signal as 'private', which means it can only be emitted from the class itself, not from outside.
              The private signal must use as last parameter QPrivateSignal, so it can only be called from class itself because no other class can create an instance of QPrivateSignal.
              This signal is still accessible from outside, but only to be connected with any slot which have same signature (except last parameter of course).

              Did you try the code above? QTimer::timeout is a private signal, and yet still callable using a clang 13 in C++11 and C++17(+1z) modes.

              Asking a question about code? http://eel.is/iso-c++/testcase/

              KroMignonK 1 Reply Last reply
              0
              • jeremy_kJ jeremy_k

                @KroMignon said in Proper use of QPrivateSignal:

                @jeremy_k said in Proper use of QPrivateSignal:

                I believe OP is referring to being able to call a function taking a QPrivateSignal using the aggregate initializer for the corresponding QPrivateSignal.

                Sorry, perhaps my english to not good enough, but I don't understand what your are meaning here.

                QPrivateSignal() struct is defined with Q_OBJECT macro.
                This will allow you to mark as signal as 'private', which means it can only be emitted from the class itself, not from outside.
                The private signal must use as last parameter QPrivateSignal, so it can only be called from class itself because no other class can create an instance of QPrivateSignal.
                This signal is still accessible from outside, but only to be connected with any slot which have same signature (except last parameter of course).

                Did you try the code above? QTimer::timeout is a private signal, and yet still callable using a clang 13 in C++11 and C++17(+1z) modes.

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

                @jeremy_k said in Proper use of QPrivateSignal:

                Did you try the code above? QTimer::timeout is a private signal, and yet still callable using a clang 13 C++.

                Sorry, now I understand what you are meaning... sad situation :(

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

                jeremy_kJ 1 Reply Last reply
                0
                • KroMignonK KroMignon

                  @jeremy_k said in Proper use of QPrivateSignal:

                  Did you try the code above? QTimer::timeout is a private signal, and yet still callable using a clang 13 C++.

                  Sorry, now I understand what you are meaning... sad situation :(

                  jeremy_kJ Offline
                  jeremy_kJ Offline
                  jeremy_k
                  wrote on last edited by jeremy_k
                  #8

                  @KroMignon said in Proper use of QPrivateSignal:

                  @jeremy_k said in Proper use of QPrivateSignal:

                  Did you try the code above? QTimer::timeout is a private signal, and yet still callable using a clang 13 C++.

                  Sorry, now I understand what you are meaning... sad situation :(

                  Or curious. I don't know if this is a failure with Qt, C++, or my understanding.

                  Asking a question about code? http://eel.is/iso-c++/testcase/

                  1 Reply Last reply
                  1
                  • S Sakars referenced this topic on

                  • Login

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