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. Widget from scratch using pImpl, how to Q_PRIVATE_SLOT?
QtWS25 Last Chance

Widget from scratch using pImpl, how to Q_PRIVATE_SLOT?

Scheduled Pinned Locked Moved Solved General and Desktop
widgetpimplprivate apiprivate slot
6 Posts 3 Posters 643 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.
  • P Offline
    P Offline
    Pl45m4
    wrote on 15 Jul 2024, 15:15 last edited by
    #1

    Hi,

    as the title already says, I'm working on a widget, which I'm building from scratch.
    I followed Qt's pImpl design, therefore I have my own WidgetPrivate class (+ d_ptr) and make use of the Q_xxxxx macros as much as I can (Q_D, Q_Q, etc.).

    The way my widget should work requires it to have a QTimer.
    This timer and the to timeout() connected function I want to hide so that they are not visible/accessible from the "public" API (Widget.h). They are both part of my WidgetPrivate class.

    So now my questions:

    How to make this work?
    I've read about Q_PRIVATE_SLOT but at the same time everywhere is written that it is only there because of compatibility to Qt4 and C++ Standard below C++11. In Qt5 and above it's allegedly not needed...

    I also found this pretty old guide:

    • http://coffeeandcode.blogspot.com/2008/01/private-implementation-slots.html

    which works, when built exactly like it's written there.
    I see

    bob!
    bob!
    bob!
    bob!
    bob!

    every second on timeout in my output pane.

    However, adapting it to my project does not seem to work.
    I don't use Qt4 (obviously) and I don't have inline functions.
    My structure looks like:

    • widget.h
    • widget_p.h
    • widget.cpp (Widget and WidgetPrivate both share this code file)

    In addition I don't use the String-based connections...

    This altogether results in a bunch of errors, which make my code not compile :(

    Any help is appreciated :)

    H:/dev/Qt/6.7.0/mingw_64/include/QtCore/qobject.h::215::-1">H:/dev/Qt/6.7.0/mingw_64/include/QtCore/qobject.h</a>:215:9: note:   template argument deduction/substitution failed:
    H:/dev/Qt/6.7.0/mingw_64/include/QtCore/qobject.h: In substitution of 'template&lt;class Func1, class Func2&gt; static QMetaObject::Connection QObject::connect(const typename QtPrivate::FunctionPointer&lt;Prototype&gt;::Object*, Func1, const typename QtPrivate::ContextTypeForFunctor&lt;Func2&gt;::ContextType*, Func2&amp;&amp;, Qt::ConnectionType) [with Func1 = void (QTimer::*)(QTimer::QPrivateSignal); Func2 = void (WidgetPrivate::*)()]':
    H:/dev/Qt/6.7.0/mingw_64/include/QtCore/qobject.h</a>:215:9: error: no type named 'ContextType' in 'struct QtPrivate::ContextTypeForFunctor&lt;void (WidgetPrivate::*)(), void&gt;'
    H:/dev/Qt/6.7.0/mingw_64/include/QtCore/qobject.h</a>:264:9: note: candidate: 'template&lt;class Func1, class Func2&gt; static QMetaObject::Connection QObject::connect(const typename QtPrivate::FunctionPointer&lt;Prototype&gt;::Object*, Func1, Func2&amp;&amp;)'
      264 |         connect(const typename QtPrivate::FunctionPointer&lt;Func1&gt;::Object *sender, Func1 signal, Func2 &amp;&amp;slot)
          |         ^~~~~~~
    

    If debugging is the process of removing software bugs, then programming must be the process of putting them in.

    ~E. W. Dijkstra

    P 1 Reply Last reply 15 Jul 2024, 17:36
    0
    • C Online
      C Online
      Christian Ehrlicher
      Lifetime Qt Champion
      wrote on 15 Jul 2024, 17:31 last edited by
      #3

      You can not use the pmf connect syntax with the pImpl idiom. One way is to derive the private class from QObject or use a lambda.

      Maybe you can also take a look on QObjectPrivate::connect() in qobject_p.h (when someone asks - I don't know this function... 🙂)

      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
      1
      • S Offline
        S Offline
        SGaist
        Lifetime Qt Champion
        wrote on 15 Jul 2024, 17:12 last edited by
        #2

        Hi,

        Can you share a minimal reproducer ?
        That would make things easier to spot the issue.

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

        1 Reply Last reply
        1
        • C Online
          C Online
          Christian Ehrlicher
          Lifetime Qt Champion
          wrote on 15 Jul 2024, 17:31 last edited by
          #3

          You can not use the pmf connect syntax with the pImpl idiom. One way is to derive the private class from QObject or use a lambda.

          Maybe you can also take a look on QObjectPrivate::connect() in qobject_p.h (when someone asks - I don't know this function... 🙂)

          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
          1
          • P Pl45m4
            15 Jul 2024, 15:15

            Hi,

            as the title already says, I'm working on a widget, which I'm building from scratch.
            I followed Qt's pImpl design, therefore I have my own WidgetPrivate class (+ d_ptr) and make use of the Q_xxxxx macros as much as I can (Q_D, Q_Q, etc.).

            The way my widget should work requires it to have a QTimer.
            This timer and the to timeout() connected function I want to hide so that they are not visible/accessible from the "public" API (Widget.h). They are both part of my WidgetPrivate class.

            So now my questions:

            How to make this work?
            I've read about Q_PRIVATE_SLOT but at the same time everywhere is written that it is only there because of compatibility to Qt4 and C++ Standard below C++11. In Qt5 and above it's allegedly not needed...

            I also found this pretty old guide:

            • http://coffeeandcode.blogspot.com/2008/01/private-implementation-slots.html

            which works, when built exactly like it's written there.
            I see

            bob!
            bob!
            bob!
            bob!
            bob!

            every second on timeout in my output pane.

            However, adapting it to my project does not seem to work.
            I don't use Qt4 (obviously) and I don't have inline functions.
            My structure looks like:

            • widget.h
            • widget_p.h
            • widget.cpp (Widget and WidgetPrivate both share this code file)

            In addition I don't use the String-based connections...

            This altogether results in a bunch of errors, which make my code not compile :(

            Any help is appreciated :)

            H:/dev/Qt/6.7.0/mingw_64/include/QtCore/qobject.h::215::-1">H:/dev/Qt/6.7.0/mingw_64/include/QtCore/qobject.h</a>:215:9: note:   template argument deduction/substitution failed:
            H:/dev/Qt/6.7.0/mingw_64/include/QtCore/qobject.h: In substitution of 'template&lt;class Func1, class Func2&gt; static QMetaObject::Connection QObject::connect(const typename QtPrivate::FunctionPointer&lt;Prototype&gt;::Object*, Func1, const typename QtPrivate::ContextTypeForFunctor&lt;Func2&gt;::ContextType*, Func2&amp;&amp;, Qt::ConnectionType) [with Func1 = void (QTimer::*)(QTimer::QPrivateSignal); Func2 = void (WidgetPrivate::*)()]':
            H:/dev/Qt/6.7.0/mingw_64/include/QtCore/qobject.h</a>:215:9: error: no type named 'ContextType' in 'struct QtPrivate::ContextTypeForFunctor&lt;void (WidgetPrivate::*)(), void&gt;'
            H:/dev/Qt/6.7.0/mingw_64/include/QtCore/qobject.h</a>:264:9: note: candidate: 'template&lt;class Func1, class Func2&gt; static QMetaObject::Connection QObject::connect(const typename QtPrivate::FunctionPointer&lt;Prototype&gt;::Object*, Func1, Func2&amp;&amp;)'
              264 |         connect(const typename QtPrivate::FunctionPointer&lt;Func1&gt;::Object *sender, Func1 signal, Func2 &amp;&amp;slot)
                  |         ^~~~~~~
            
            P Offline
            P Offline
            Pl45m4
            wrote on 15 Jul 2024, 17:36 last edited by Pl45m4
            #4

            Here is a stripped-down example to show what I'm doing:

            widget.h
            (Public API)

            #ifndef WIDGET_H
            #define WIDGET_H
            
            #include <QWidget>
            #include <QScopedPointer>
            
            
            class WidgetPrivate;
            class Widget: public QWidget
            {
                Q_OBJECT
            
            public:
            
            
                explicit Widget(QWidget *parent = nullptr);
                ~Widget();
            
            Q_SIGNALS:
             
                // public signal which should fire when background logic
                // and timer timeout occur
                void timespanPassed();
            
            protected:
            
                void mousePressEvent(QMouseEvent *ev);
                void mouseReleaseEvent(QMouseEvent *ev);
            
            protected:
            
                Widget(WidgetPrivate &dd, QWidget *parent = nullptr);
            
            
            protected:
            
                /*const*/ QScopedPointer<WidgetPrivate> /*const*/ d_ptr;
            
            
            private:
            
                Q_DECLARE_PRIVATE(Widget)
                Q_DISABLE_COPY(Widget)
            
                // Is this needed?! If not, how else to do this
                Q_PRIVATE_SLOT(d_func(), void foo())
            
            };
            
            #endif // WIDGET_H
            

            widget_p.h

            #ifndef WIDGET_P_H
            #define WIDGET_P_H
            
            
            #include <QDebug>
            
            class Widget;
            class QTimer;
            
            class WidgetPrivate
            {
                Q_DECLARE_PUBLIC(Widget)
                Q_DISABLE_COPY(WidgetPrivate)
            
            public:
            
                WidgetPrivate(Widget *q);
                virtual ~WidgetPrivate();
            
                void init();
            
                Widget *const q_ptr;
            
                // this is the timer I want to use internally
                QTimer *timer;
            
                // internal function which causes a signal in public API to fire
                // (after some processing) 
                void emitTimespanPassed();
            
            private:
            
                void foo(); // private internal slot?!
            
            
            };
            
            #endif // WIDGET_P_H
            

            widget.cpp

            #include "widget.h"
            #include "widget_p.h"
            
            #include <QPainter>
            #include <QPointer>
            #include <QMouseEvent>
            #include <QTimer>
            
            // needed?!
            #include "moc_widget.cpp"
            
            WidgetPrivate::WidgetPrivate(Widget* q)
                : q_ptr(q)
                , timer(nullptr)
            {
            
            }
            
            
            WidgetPrivate::~WidgetPrivate()
            {
            
            }
            
            void WidgetPrivate::init()
            {
                Q_Q(Widget);
                // init public API settings here
            
                timer = new QTimer(q);
                timer->setSingleShot(true);
            
                // interval is changable in API
                timer->setInterval(3000); // 3s
             
                 // connect here?!
                // if so, how?
                // QObject::connect(timer, &QTimer::timeout, this, [&](){ foo(); });
            
                q->setMouseTracking(true);
            
            }
            
            void WidgetPrivate::emitTimespanPassed()
            {
                Q_Q(Widget);
                QPointer<Widget> guard(q);
            
                // some conditions and logic to emit public signal...
                // [ ... ]
                // emit widget signal
                if (guard)
                    emit q->timespanPassed();
            }
            
            // function connected to QTimer::timeout
            void WidgetPrivate::foo()
            {
            
                // if (xyz) { // some logic
                    // cause emit of public signal in Widget API
                    emitTimespanPassed();
            
                // }
            
            }
            
            // END INTERNAL PRIVATE
            // #######################################################################################
            // #######################################################################################
            // #######################################################################################
            // BEGIN PUBLIC API
            
            Widget::Widget(QWidget *parent)
                : QWidget(parent)
                , d_ptr(new WidgetPrivate(this))
            {
                Q_D(Widget);
                d->init();
            
                 // OR connect here?!?!?
                 // better here or in WidgetPrivate::init()?
                 // no error but signal not emitted
                QObject::connect(d->timer, SIGNAL(timeout()), this, SLOT(foo())); 
                // does NOT work, crashes with mentioned error
                QObject::connect(d->timer, &QTimer::timeout, d_func(), &WidgetPrivate::foo); 
            }
            
            Widget::Widget(WidgetPrivate &dd, QWidget *parent)
                : Widget(parent)
                , d_ptr(&dd)
            {
            
                Q_D(Widget);
                d->init();
            
            }
            
            void Widget::mousePressEvent(QMouseEvent *ev)
            {
                Q_D(Widget);
                d->timer->start();
                QWidget::mousePressEvent(ev);
            }
            
            void Widget::mouseReleaseEvent(QMouseEvent *ev)
            {
                Q_D(Widget);
                d->timer->stop();
                QWidget::mouseReleaseEvent(ev);
            }
            
            // END PUBLIC API
            // #######################################################################################
            // #######################################################################################
            // #######################################################################################
            

            main.cpp

            #include <QApplication>
            #include "widget.h"
            
            int main(int argc, char *argv[])
            {
                QApplication a(argc, argv);
                Widget w;
            
                QObject::connect(&w, &Widget::timespanPassed, &w, [&](){
                    qDebug() << "Timespan passed";
                });
            
                w.show();
                return a.exec();
            }
            

            This is my code... I'm unsure about many things there.
            Where to connect my internal function?
            Is the #include "moc_widget.cpp" needed? Some sources state that it's needed to tell moc to "moc" the WidgetPrivate part?!

            Very much appreciated if any of you can shed a light on this procedure for me :))
            I hope you get what I'm trying to do... but maybe there are even some structural flaws?! Can't tell actually :(


            If debugging is the process of removing software bugs, then programming must be the process of putting them in.

            ~E. W. Dijkstra

            1 Reply Last reply
            0
            • C Online
              C Online
              Christian Ehrlicher
              Lifetime Qt Champion
              wrote on 15 Jul 2024, 17:45 last edited by
              #5

              As I said the pmf syntax needs a QObject as sender and receiver.

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

              P 1 Reply Last reply 15 Jul 2024, 17:54
              1
              • C Christian Ehrlicher
                15 Jul 2024, 17:45

                As I said the pmf syntax needs a QObject as sender and receiver.

                P Offline
                P Offline
                Pl45m4
                wrote on 15 Jul 2024, 17:54 last edited by Pl45m4
                #6

                @Christian-Ehrlicher said in Widget from scratch using pImpl, how to Q_PRIVATE_SLOT?:

                As I said the pmf syntax needs a QObject as sender and receiver.

                Ah sorry, while I was editing my example code I missed your reply :)

                Oh wow didn't know that :0
                So the ( SIGNAL(), SLOT() ) connection stuff is not 100% identical in terms of use-case and compatibility with the Functor-based connections?
                Good to know.

                @Christian-Ehrlicher said in Widget from scratch using pImpl, how to Q_PRIVATE_SLOT?:

                Maybe you can also take a look on QObjectPrivate::connect() in qobject_p.h (when someone asks - I don't know this function... 🙂)

                Yeah, I checked the Qt Private sources here and there while I was building my widget to see how it's done. Haven't seen this exact part, but will visit it again ;-)
                Since I don't want to include the Qt Private source completely (which would make my widget highly dependent on a single Qt version), I made my own WidgetPrivate based on QWidgetPrivate "style" but without inheriting it... so no QObjectData, QObjectPrivate and whatnot... for me ;-)

                Edit:

                @Christian-Ehrlicher said in Widget from scratch using pImpl, how to Q_PRIVATE_SLOT?:

                Maybe you can also take a look on QObjectPrivate::connect() in qobject_p.h (when someone asks - I don't know this function... 🙂)

                This function?
                What is there to see? Other than QObjectPrivate::connect.
                You mean the use of two QObject Functors?

                when someone asks - I don't know this function

                Nobody will know 🤐

                One way is to derive the private class from QObject or use a lambda.

                If one way to make it work is using the SIGNAL / SLOT keywords again, I will probably try it.
                Tried lambda, did not work either :/ Or maybe I was doing something wrong.

                Edit_II:

                I made my actual code work, using String-based connections while I moved the connect statement to the WidgetPrivate::init() function and hid the QTimer also behind the WidgetPrivate data.
                Not 100% pleased, tho :D
                Is it a good idea to make the Impl class a QObject?!
                What's your recommendation?

                @SGaist oh lol even missed your comment too :D Was about to add the code anyway :)


                If debugging is the process of removing software bugs, then programming must be the process of putting them in.

                ~E. W. Dijkstra

                1 Reply Last reply
                0
                • P Pl45m4 has marked this topic as solved on 9 Aug 2024, 17:07
                • P Pl45m4 referenced this topic on 28 Oct 2024, 02:18
                • P Pl45m4 referenced this topic on 1 Nov 2024, 02:10

                1/6

                15 Jul 2024, 15:15

                • Login

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