LINUX.ORG.RU

Qt Pimpl

 ,


0

2

Пришлось тут вспомнить что такое Qt Pimpl. В документации об этом начколько мне известно ничего нет. Поэтому в очередной раз достал вот этот хабро-пост. Всё хорошо, но никак не соображу зачем нужны макросы Q_Q и Q_D, когда сами объекты содержат указатели на публичный интерфейс и на приватную реализацию? Кроме того я так и не понял для чего нужен защищённый конструтор. Можно как-нибудь разъяснить этот момент?

★★★★★

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

для чего нужен защишённый конструтор

Как бы синглетон?

// По ссылке не ходил.

anonymous
()

Макросы Q_Q, Q_D используются для единообразия вместе с макросами Q_DECLARE_PUBLIC, Q_DECLARE_PRIVATE. Конечно можно использовать(и определять) d_func(), q_func() руками, но макросами удобнее и наглядней: например если видишь в начале метода Q_D - то сразу ясно что где-то внутри метода используется приватный класс.

Защищенный конструктор нужен для того, чтобы наследник публичного класса мог установить собственную реализацию приватного класса.

bjorn
()

И самые главные вопросы: тебе действительно нужна бинарная совместимость? У твоей библиотеки гарантированно не будет меняться интерфейсы? Если нет - то не заморачивайся на Qt Pimpl т.к. 1) Он завязан на приватные реализации самого Qt - никто не гарантируют что они не будут меняться(привет Qt5!) 2) За счет еще одной прослойки будет увеличение и усложнение кода.

bjorn
()

не заморачивайся с этим, всё только усложнишь. старое доброе

#ifdef __cplusplus
extern "C" {
#endif

int func1();
int func2();
int funcN();

#ifdef __cplusplus
}
#endif
отличное решение.

anonymous
()

Можно.

Пока у тебя есть один публичный интерфейс и приватная его реализация - всё хорошо.

Гемор (ну или не очень) возникает когда у твоего класса появляются наследники. Вся эта Q_ ерунда нужна для того чтобы с помощью неё кастовать интерфейс и реализацию к нужным (реализуемым в данный момент) классам и не запутаться во всём этом бардаке.

com
()

для чего нужен защишённый конструтор

Опять же для наследования, я так пологаю. Только наследник твоего класса имеет право инициализировать указатель на реализацию приватной части.

По сути при наследовании указатель на приватную реализацию остаётся один, в родителе. А Класс-наследник с помощью приватного конструктора инициализирует его своей приватной реализацией, отнаследованной, в свою очередь, от приватной реализации класса-родителя.

com
()

Да и вообще мудро выше советовали - не пользоваться этим без надобности.

Вся фича в том, что внутри Qt указатель на приватные данные всего pimpl хранится в QObject. Все классы внутри Qt, реализующие этот механизм, наследуются от QObjectPrivate. А ты, не нарушив бинарной совместимости, так сделать не сможешь (придётся тащить в свой проект по крайней мере qobject_p.h из Qt, а он в свою очередь, может меняться от версии к версии, что рушит бинарную совместимость).

А то, что описано в статье по ссылке - костыль, использующий внутренние Qt-шные макросы для упрощения его реализации.

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

Меня смущает только один момент: за каким дьяволом наследнику лезть в приватную реализацию предка? Или это попытка сделать приватную реализацию иерархии худее?

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

Плагины. Есть приложение с плагинами. Плагины дёргают часть API приложения (функции доступные плагинам вынесены в маленькую библиотеку). Очень хочется сделать плагины нечувствительными к изменениям ABI. Кстати, я пишу на Qt5 и там ничего с перечисленными макросами не сделалось.

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

про моменты уже разъяснили

по посту в целом - Qt написан немного странно местами, и вот это одно из таких реально странных мест, ориентироваться на такие подходы я бы не стал (не вообще Pimpl, а именно конкретный случай), короче - не трать время на это

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

Ну… Надобнасть в Pimpl в том или ином виде у меня есть (описал ситуацию выше). Вопрос скорее в том как сигналить сигналами объекта не из самого объекта или его наследников.

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

Дочитай внимательно статью на хабре, перечитай еще раз насчет Q_PRIVATE_SLOT, посмотри исходники Qt - многие вопросы отпадут насчет pimpl.

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

Поясни, что ты хочешь конкретно? Не нравятся макросы - не используй, пиши все руками. «Эмит» сигнала - это просто вызов protected метода с именем сигнала, макрос emit можно и не писать: emit some() и просто some() делают ровно одно и тоже.

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

Ну и если ты не использовал макрос Q_DECLARE_PUBLIC, то сделай ClassPrivate friend'ом Class, чтоб можно было protected методы(сигналы) вызывать.

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

То есть, если я сделаю свой приватный класс дружественным к моему публичному интерфейсу, то смогу из приватного класса дёргать слоты как функции по указателю на публичный интерфейс?

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

Да слоты ты вообще всегда можешь вызывать из другого класс, если они public, это же просто обычный метод класса, для этого не надо становиться friend'ом. То что я написал это вызов сигнала публичного класс из приватного. Найди время и посмотри что тебе MOC генерирует - все встанет на свои места.

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

Что-то из того разряда. По-сути, как и в обычном наследовании, сын наследует функционал предка. В том числе и его protected методы и структуры данных.

А вдруг ты захочешь в приватной реализации сына модифицировать данные приватной реализации предка?

И вообще - одна из целей, из-за которых затевался этот самый pimpl - скрыть от пользователя лишние (приватные) методы и структуры данных и предоставить ему красивый интерфейс работы с классом.

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

Ну только наоборот, надо в публичном классе сделать френдом приватный. Кстати, если бы ты посмотрел на столь нелюбимые макросы, то сразу бы это увидел:

#define Q_DECLARE_PRIVATE(Class) \
    inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr)); } \
    inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr)); } \
    friend class Class##Private;

#define Q_DECLARE_PUBLIC(Class)                                    \
    inline Class* q_func() { return static_cast<Class *>(q_ptr); } \
    inline const Class* q_func() const { return static_cast<const Class *>(q_ptr); } \
    friend class Class;

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

Смотреть исходники до просветления:

#ifndef QT_NO_EMIT
# define emit
#endif

emit не делает ничего.

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

Пример:

Имеем test.cpp

class Test : public QObject
{
    Q_OBJECT
signals:
    void testSignal(const QString &arg1, int arg2);
};

Получаем moc_test.cpp:

...
// SIGNAL 0
void Test::testSignal(const QString & _t1, int _t2)
{
    // Магия начинается тут!
    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)), const_cast<void*>(reinterpret_cast<const void*>(&_t2)) };
    QMetaObject::activate(this, &staticMetaObject, 0, _a);
}
...

Как видно сигнал это просто метод, а MOC магия уже внутри него.

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

Собственно, о чём я и написал выше. moc пройдётся по исходникам и дорисует всё что надо.

Так как moc - препроцессор, для него emit - просто метка, как, допустим, #define для стандартного плюсового препроцессора.

Т.Е. если ты не напишешь emit, (ну или синоним там какой-то, помнится, был), то moc этой магии для тебя не сгенерирует.

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

moc не пойдёт смотреть приватную реализацию, он сгенерит всё что надо для публичного интерфейса в котором написано Q_OBJECT.

Соответственно можно писать emit, а можно не писать.

Но для улучшения читаемости, всё-же, стоит.

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

Еще раз, MOC вообще не смотрит на emit, он смотрит на signals и создает методы типа того, который я привел в листинге, а потом уже препроцессор C++ заменят макрос signals на protected.

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

Совсем туплю.

moc смотрит на .h файл и генерит нужный функционал для всего что описано как signal. Ну и наверняка для всего что описано как slot. В исходники не полезу уже смореть что там и как. Пойду лучше спать.

P.S. А стоило то всего два месяца плотно писать на Java. Надо-бы заканчивать, но такой вариант отсутствует.

com
()

тред не читай @ сразу отвечай

зачем нужны макросы Q_Q и Q_D

для краткости и конст-корректности доступа к объекту-компаньону

для чего нужен защищённый конструтор

Наверное, чтобы его могли вызывать только друзья

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

Ну и наверняка для всего что описано как slot.

Нет-с, их только нумеруют (вместе с сигналами)

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