LINUX.ORG.RU

Сигналы и слоты, позднее связывание

 , ,


0

1

Пишу так:

class Parent {
public:
	virtual void someSignal() = 0;
};

class Child: public QObject, public Parent {
	Q_OBJECT
public signals:
	void someSignal() {}
};
Parent *pointer = getPointer();

connect(pointer, SIGNAL(someSignal), this, SLOT(someSlot())); // первый
connect(qobject_cast<QObject*>(pointer), SIGNAL(someSignal), this, SLOT(someSlot())); // второй

Первый вариант падает с

error: no matching function for call to 'Shooter::connect(Parent*&, const char*, Shooter* const, const char*)'
note:  no known conversion for argument 1 from 'Parent*' to 'const QObject*'
Подумав и погуглив понял что надо приводить к QObject, но не помогает, второй вариант тоже не работает:
error: no matching function for call to 'qobject_cast(Parent*&)'
note:  candidates are: template<class T> T qobject_cast(QObject*)

А здесь мне уже не понятно, указатель ведь указывает на потомка, а его можно легко привести к QObject. Почему тогда так?

А здесь мне уже не понятно, указатель ведь указывает на потомка, а его можно легко привести к QObject

Parent *pointer — это же предок, а не потомок. Если там реально объект класса Child, то сначала надо скастовать Parent в Child, потом Child в QObject, иначе сегфолтить будет.

public signals:

#define signals protected

void someSignal() {}

Тело сигнала здесь не нужно, оно будет в сгенерированном moc исходнике.

gentoo_root ★★★★★
()
Последнее исправление: gentoo_root (всего исправлений: 1)

А здесь мне уже не понятно, указатель ведь указывает на потомка, а его можно легко привести к QObject. Почему тогда так?

Ты все еще на этапе конпеляции. Для конпелятора pointer имеет типа Parent *, он понятия не имеет, что там будет в рантайме.
Хочешь трахаться с типами - для этого есть куча кастов, dynamic_cast'а вполне хватит, но reinterpret_cast тоже к твоим услугам. Что же касается qobject_cast, то сообщение об ошибке ясно дает понять, что он хочет QObject*, к которому Parent имеет весьма посредственное отношение (привет множественное наследование!).

Если не хочешь трахаться с кастами и типами, отнаследуйся как белый человек:

QObject < Parent < Child

anonymous
()
Ответ на: комментарий от gentoo_root

надо скастовать Parent в Child

Наследников много может быть, я даже не знаю в рантайме какой именно там класс, разве можно скастовать?

Тело сигнала здесь не нужно

Это я случайно в примере написал.

Kalashnikov ★★★
() автор топика
Ответ на: комментарий от anonymous

Нельзя, Parent - это интерфейс для плагинов, а Child - и есть один из плагинов. Интерфейс должен быть абстрактным, если унаследовать от QObject, оно туда методов с реализациями натолкает.

Kalashnikov ★★★
() автор топика
Ответ на: комментарий от Kalashnikov

Наследников много может быть, я даже не знаю в рантайме какой именно там класс, разве можно скастовать?

Тогда надо как-то узнать. Потому что просто так напрямую скастовать от одного базового класса к другому нельзя — неправильно рассчитаются смещения и будет сегфолт или повреждение данных (это из личного опыта).

Это случайно не попытка сделать сигналы в интерфейсе?

gentoo_root ★★★★★
()
Ответ на: комментарий от Kalashnikov

Нельзя, Parent - это интерфейс для плагинов, а Child - и есть один из плагинов. Интерфейс должен быть абстрактным, если унаследовать от QObject, оно туда методов с реализациями натолкает.

Сделай интерфейс к фабрике, которая будет делать нормальные QObject'ы.

gentoo_root ★★★★★
()
Ответ на: комментарий от gentoo_root

Если там реально объект класса Child, то сначала надо скастовать Parent в Child, потом Child в QObject, иначе сегфолтить будет.

Что? Ты хочешь сказать, что я не могу сделать

Parent *pointer = new Child;
QObject *p = dynamic_cast<QObject*>(pointer);
?

anonymous
()
Ответ на: комментарий от anonymous

но reinterpret_cast тоже к твоим услугам

два чая этому господину

Begemoth ★★★★★
()
Ответ на: комментарий от Kalashnikov

Плагины могут быть не QObject'ами, так получается? Но тогда какого черта ты пихаешь сигналы в интерфейс Parent'а?

anonymous
()
Ответ на: комментарий от anonymous

Что? Ты хочешь сказать, что я не могу сделать

Да, не можешь, надо сначала привести к типу, наследующему и от Parent, и от QObject, а затем привести к QObject (что вобщем будет необязательно).

Begemoth ★★★★★
()
Ответ на: комментарий от anonymous

Плагины могут быть не QObject'ами, так получается

Да и мне это не нравится. Но судя по всему нельзя заставить их быть объектами.

Но тогда какого черта ты пихаешь сигналы в интерфейс Parent'а?

Объявляю их виртуальными чтобы пейсатель плагина не смог забыть их добавить, надеясь что он не забудет и сигналами их объявить.

Kalashnikov ★★★
() автор топика
Ответ на: комментарий от Kalashnikov

Объявляю их виртуальными чтобы пейсатель плагина не смог забыть их добавить, надеясь что он не забудет и сигналами их объявить.

Документация для этих целей подошла бы больше, учитывая, что сделать то, что ты хочешь без костылей вряд ли выйдет и в конечном итоге все равно loose ends будут.

anonymous
()
Ответ на: комментарий от anonymous

В каком месте сегфолт будет? При каких условиях?

Сегфолта не будет, некорректные преобразования будут выловлены при компиляции, если кончено не забыть проверить указатель, полученный от dynamic_cast на nullptr.

Begemoth ★★★★★
()

Гм, заменил qobject_cast на dynamic_cast - всё заработало. Читал это. Особенно:

...you can cast sideways...

Хотя в доках написано:

The qobject_cast() function behaves similarly to the standard C++ dynamic_cast(), with the advantages...

Kalashnikov ★★★
() автор топика
Ответ на: комментарий от Kalashnikov

Да и мне это не нравится. Но судя по всему нельзя заставить их быть объектами.

Не понимаю все-таки, чем тебя не устраивает наследование от QObject'а. Да, Parent не будет интерфейсом, а просто абстрактным классом, чем это плохо в твоем случае?

anonymous
()
Ответ на: комментарий от anonymous

Согласен. Если трезво смотреть на вещи, я пишу велосипед которым никто кроме меня пользоваться не будет, тем более писать плагины. Иначе доки конечно бы были. Но так по-моему самый адекватный вариант.

Kalashnikov ★★★
() автор топика
Ответ на: комментарий от Begemoth
#include <QObject>

class Parent {
public:
    virtual void someSignal() = 0;
};

class Child: public QObject, public Parent {
    Q_OBJECT
public:
signals:
    void someSignal();
};

#include <QDebug>
#include "main.h"

int main ()
{
  Parent *pointer = new Child();
  QObject *p = dynamic_cast<QObject*>(pointer);
  p->setObjectName("test");
  qDebug() << p << p->objectName();

  return 0;
}
$ ./tmp 
Child(0x1981d20, name = "test") "test"

Мне правда интересно, какие тут подводные камни?

anonymous
()
Ответ на: комментарий от anonymous

Ну, совершенно точно это не православно. Сначала я так и попытался сделать, но там всё валилось опять же на сигналах (возможно не из-за такого наследования впрочем). Пока искал решение, наткнулся на кучу доков и обсуждений (например) где советовали сделать именно так, потому и переписал.

Kalashnikov ★★★
() автор топика
Ответ на: комментарий от Kalashnikov

Сначала я так и попытался сделать, но там всё валилось опять же на сигналах

Ну тут уже явно косяк твоей реализации.

Но вообще, может вариант с множественным наследованием и оке, но у меня свои личные предпочтения, в которые это наследование не входит.

anonymous
()
Ответ на: комментарий от anonymous

Ну тут уже явно косяк твоей реализации.

Да, вероятно.

В кути множественное наследование сплошь и рядом, как раз таки с QObject, вроде не так всё плохо.

Kalashnikov ★★★
() автор топика
Ответ на: комментарий от Kalashnikov

В кути множественное наследование сплошь и рядом

Помимо UI класса сходу ничего больше вспомнить не могу, если речь про саму кути, а не приложения, которые ее используют.

anonymous
()

я не понимаю нафига это делать с сигналами. чего ты хочешь добиться? если ты хочешь сделать плагин то кури QPlugin. Нафига этот изврат?

nanoolinux ★★★★
()
Ответ на: комментарий от anonymous

Ну и, сам то читал? Я читал и так как раз и пишу.

Define a set of interfaces (classes with only pure virtual functions) used to talk to the plugins.

Kalashnikov ★★★
() автор топика

// наркомании тред

shty ★★★★★
()
Ответ на: комментарий от Kalashnikov

А ты заметил, что в примерах этих интерфейсов нигде нет сигналов и слотов? Я бы плагины использовал как фабрики.

anonymous
()
Ответ на: комментарий от anonymous

О как тебя развернуло.

Примеры на то и примеры чтобы лишь необходимый минимум содержать. Там и переменных типа int нет, что, с ними тоже не работает?

Kalashnikov ★★★
() автор топика

плохой дизайн

правильно советуют посмотреть на qt-плагины

плюс ко всему вышесказанному добавлю, что делать виртуальными сигналы\слоты - плохая практика, они из-за этого в 10 раз медленней начинают работать

EugeneBas ★★
()
Ответ на: комментарий от EugeneBas

плохой дизайн

Плагин реализует интерфейс и при этом является объектом, как иначе? Да, и в доках фреймворка так. Смотрю доки по плагинам для креатора, там то же самое апи.

они из-за этого в 10 раз медленней начинают работать

А это уже аргумент, можешь подробнее рассказать?

Kalashnikov ★★★
() автор топика
Ответ на: комментарий от Kalashnikov

А это уже аргумент, можешь подробнее рассказать?

кури реализацию виртуальных методов. они всегда медленнее работают, потому-что их дополнительно ищут в vtable перед тем как вызвать.

nanoolinux ★★★★
()
Ответ на: комментарий от nanoolinux

Ты тред читаешь?

http://doc.qt.nokia.com/4.7-snapshot/plugins-howto.html

Анон уже дал это линк и я уже ответил что по этому апи и делаю. «QPlugin» тебе приснился вообще судя по всему.

там то же самое апи.

Q_EXPORT_PLUGIN2(Example, ExamplePlugin)

Не оно разве?

Kalashnikov ★★★
() автор топика
Ответ на: комментарий от Kalashnikov

Q_EXPORT_PLUGIN2(Example, ExamplePlugin)
Не оно разве?

Ты безнадежен.

anonymous
()
Ответ на: комментарий от Kalashnikov

А это уже аргумент, можешь подробнее рассказать?

Если не ошибаюсь, об этом было написано в книге Бланшета и Саммерфилда

EugeneBas ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.