0

I tried the new Qt5 feature of using a callable object as callback.

#include <QApplication>
#include <QPushButton>
#include <QWidget>
#include <QBoxLayout>

#include <cstdio>

class ButtonEventHandler
    {
    public:
        ButtonEventHandler(const ButtonEventHandler& obj):
            counter(obj.counter),r_button(obj.r_button)
            {
            printf("Copy from %p -> %p\n",&obj,this);
            }

        ButtonEventHandler& operator=(const ButtonEventHandler& obj)
            {
            r_button=obj.r_button;
            counter=obj.counter;
            printf("Assign from %p -> %p\n",&obj,this);
            return *this;
            }

        explicit ButtonEventHandler(QPushButton& button):r_button(&button)
            {
            printf("CTOR %p\n",this);
            counter=0;
            }

        void operator()(bool checked) noexcept
            {
            char buffer[32];
            printf("%p  counter:%zu\n",this,counter);
            sprintf(buffer,"%zu",counter);
            r_button->setText(buffer);
            ++counter;
            }

    private:
        size_t counter;
        QPushButton* r_button;
    };

int main(int argc,char** argv)
    {
    QApplication app(argc,argv);
    QWidget window;
    QHBoxLayout box;
    window.setLayout(&box);
    window.setFixedSize(400,300);

    QPushButton button1("Click me");
    ButtonEventHandler eh1(button1);
    QObject::connect(&button1,&QPushButton::clicked,eh1);
    box.addWidget(&button1);

    window.show();
    return app.exec();
    }

Sample output:

CTOR 0x7ffdd35f4ed0
Copy from 0x7ffdd35f4ed0 -> 0x7ffdd35f4ee0
Copy from 0x7ffdd35f4ee0 -> 0x7ffdd35f4e70
Copy from 0x7ffdd35f4e70 -> 0x21b2c10
Copy from 0x21b2c10 -> 0x7ffdd35f4050
0x7ffdd35f4050  counter:0
Copy from 0x21b2c10 -> 0x7ffdd35f4050
0x7ffdd35f4050  counter:0
Copy from 0x21b2c10 -> 0x7ffdd35f4050
0x7ffdd35f4050  counter:0
Copy from 0x21b2c10 -> 0x7ffdd35f4050
0x7ffdd35f4050  counter:0

However, it does not really behave as expected. It starts with copying the callback object (I understand that: so it does not go out of scope before the object it is associated with). But then, the object is copied before every fired event, so my counter is kept at zero. Why does it keep the object invariant?

Tarod
  • 6,732
  • 5
  • 44
  • 50
user877329
  • 6,717
  • 8
  • 46
  • 88
  • Why would you expect it to keep your object same? Is it said so in specification? I think it does something like: `auto callbacks = callbacks(); for (auto & cb : callbacks) cb();` – V. Kravchenko Jul 12 '16 at 19:30
  • What Qt version are you using? I think what you're seeing has been fixed ~5.1.x, a couple of years ago. Can you try Qt 5.6? (Besides, please, use `button->setText(QString::number(counter))`) – peppe Jul 12 '16 at 22:24
  • @V.Kravchenko Would you expect a copy of the callback object? One major reason to put things in a class rather than a function is that objects of that class can maintain state between calls. – user877329 Jul 13 '16 at 06:00
  • @peppe I am using 5.5.1+dfsg-16ubuntu7.1. I guess that means 5.5.1 with some unspecified features removed since it is marked with dfsg. But is it possible to change such a thing? Wouldn't that break old code relying on the old behavior? – user877329 Jul 13 '16 at 06:03
  • I have no idea. But you can do a quick test -- download a build of Qt 5.6/5.7 from https://www.qt.io/download-open-source/ and give it a try. – peppe Jul 13 '16 at 07:11
  • dfsg means Debian Free Software Guidelines, which means that it's adapted to the Debian distributions policies. – Velkan Jul 14 '16 at 20:35

1 Answers1

-1

It appears that this is a bug in Qt 5.5.1. Compiling against Qt 5.7 gives the following output:

CTOR 0x7ffd7a47e370
Copy from 0x7ffd7a47e370 -> 0x7ffd7a47e380
Copy from 0x7ffd7a47e380 -> 0x7ffd7a47e310
Copy from 0x7ffd7a47e310 -> 0xb65970
0xb65970  counter:0
0xb65970  counter:1
0xb65970  counter:2

Which is more expected

user877329
  • 6,717
  • 8
  • 46
  • 88
  • It's not a bug. You can not rely on that behaviour. It may be copied or use the same object. Don't do that. – Velkan Jul 14 '16 at 20:32
  • @Velkan any reference to the right documentation page? I have only found "Try this new cool feature"-stuff. Not a page saying anything about if it may copy or not. – user877329 Jul 15 '16 at 08:36
  • @Velkan I found that. But those who wrote that did not thought about this issue. I have never seen a framework that copies a listener before calling one of its members so my default is no copy (at least on events). In addition to being contra-intuitive, it may be inefficient if the listener is large. – user877329 Jul 15 '16 at 08:50
  • It's like a C++ Standard Library: functors are sometimes copied. – Velkan Jul 15 '16 at 08:57