PDA

View Full Version : qobject_cast fails in slot



tuli
25th July 2018, 08:51
I have derived a custom widget my_widget from QWidget. There is a factory function in my MainWindow that creates those widgets, and their destroy-signal is connected to a slot in the MainWindow to do some cleanup.


Problem: qobject_cast returns zero when I try to cast the QOjbect* the slot received to my_widget*.

If I try the cast directly in the factory function, that works.
If I use plain-cast in the slot, it works too (the pointer is the same as returned by tthe factory function).
If I try to qobject_cast to a QWidget*, that works too.



my_widget* create_my_widget(QWidget* parent = nullptr)
{
auto w = new my_widget(parent);

QObject* ppp = (QObject*)w;
auto sss = qobject_cast<my_widget*>(ppp); //works!

connect(w, &my_widget::destroyed, this, &MainWindow::remove_mywidget_widget);
return w;
}


slot:


void MainWindow::remove_mywidget_widget(QObject* removed)
{
auto mywid = qobject_cast<my_widget*>(removed); //Fails!
auto mywid2 = my_widget::staticMetaObject.cast(removed); //Fails!
auto mywid3 = qobject_cast<QWidget*>(removed); //Works!
auto mywid4 = (my_widget*)removed; //Works!

...

}


Where am I going wrong?

d_stranz
25th July 2018, 23:54
I wonder if the problem is that the C++ object type system is failing to correctly deduce the return type of the qobject_cast<>() template when you assign the result to a variable defined as auto? What happens if you replace auto with my_widget * ?

Do you have the Q_OBJECT macro included in your definition of the my_widget class?

tuli
26th July 2018, 00:39
Sadly that isnt the problem. Yes, the Q_OBJECT macro is there.

I have created a minimal demo to reproduce the issue:



class my_wid : public QWidget
{
Q_OBJECT
public:
my_wid(QWidget* p=0) : QWidget(p)
{}

int a,b,c;
};

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
explicit MainWindow(QWidget *parent = 0)
{
my_wid* mm = create_mywid();
delete mm;
}

my_wid* create_mywid(QWidget* parent=0)
{
my_wid* w = new my_wid(parent);
connect(w, &QObject::destroyed, this, &MainWindow::remove_mywid);
return w;
}


protected slots:

void remove_mywid(QObject* removed)
{
my_wid* test = qobject_cast<my_wid*>(removed);
qDebug() << test; // is 0 :(
}
};

d_stranz
26th July 2018, 02:50
Strange. Here's another experiment: Instead of destroying the widget in the MainWindow constructor, set up a one-shot QTimer and connect the timeout() signal to my_wid's deleteLater() slot.

This may possibly be some quirk of Qt that because the MainWindow constructor hasn't finished before you delete the my_wid instance, all of the necessary internal wiring isn't set up completely.

tuli
26th July 2018, 08:15
No change. :(
In my real-world use case the destruction of the my_widget is also user-initiated and that doesnt work either. Really strange.


QTimer::singleShot(2000, mm, &my_wid::deleteLater);

d_stranz
26th July 2018, 16:38
No idea. Do you have the header file for my_widget #included so that MainWindow sees the full class definition (and not just a forward declaration of "class my_widget")? You must, otherwise your factory method wouldn't work. I don't think the qobject_cast<> template would compile without the full definition either. The static cast would work, though, because it just forces the compiler to treat a pointer to "x" as a pointer to "y" and won't complain until you try to call some member function of "y".