Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Небольшие проблемы с QSharedPointer и Forward Declaration
Форум на CrossPlatform.RU > Библиотеки > Qt > Qt Общие вопросы
SABROG
Проблема возникает из-за Forward Declaration тут:
namespace Ui {
    class Widget;
}

MOC еще не сгенерил .cpp файлы и размер класса еще не известен. Как я понял размер класса нужен QSharedPointer для удаления указателя, но например в таком случае все великолепно:
main.cpp
#include <QtCore/QCoreApplication>
#include <QtCore/QSharedPointer>

class Data;

class Object : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QSharedPointer<Data> value READ value WRITE setValue)

public:
    QSharedPointer<Data> value() const {return m_value;}
    void setValue(QSharedPointer<Data> value) {m_value = value;}

private:
    QSharedPointer<Data> m_value;
};

struct Data
{
    int m_value;
    float m_value2;
};

int main(int argc, char **argv)
{
    QCoreApplication a(argc, argv);
    Object obj;
    obj.setValue(QSharedPointer<Data>(new Data));
    obj.value()->m_value = 1;
    obj.value()->m_value2 = 2;

    return 0;
}

#include "moc_main.cpp"

Проблемные исходники:
Widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QtGui/QWidget>

namespace Ui {
    class Widget;
}

class Widget : public QWidget {
    Q_OBJECT
    Q_PROPERTY(QSharedPointer<Ui::Widget> ui READ ui WRITE setUi)
public:
    Widget(QWidget* parent = 0);
    QSharedPointer<Ui::Widget> ui() const {return m_ui;}
    void setUi(QSharedPointer<Ui::Widget> ui) {m_ui = ui;}
    ~Widget();

protected:
    void changeEvent(QEvent* e);

private:
    QSharedPointer<Ui::Widget> m_ui;
};

#endif // WIDGET_H
Widget.cpp

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget* parent) :
    QWidget(parent)
{
    setUi(QSharedPointer<Ui::Widget>(new Ui::Widget));
    ui()->setupUi(this);
}

Widget::~Widget()
{
}

void Widget::changeEvent(QEvent* e)
{
    QWidget::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui()->retranslateUi(this);
        break;
    default:
        break;
    }
}

Предупреждения компилятора

Выполняется сборка проекта q_property_widget...
Запускается: /usr/bin/make clean -w
make: Вход в каталог `/home/sabrog/work/q_property_widget'
make: *** Нет правила для сборки цели `clean'. Останов.
make: Выход из каталога `/home/sabrog/work/q_property_widget'
Завершено с кодом 2.
Запускается: /opt/qtsdk-2010.02/qt/bin/qmake /home/sabrog/work/q_property_widget/q_property_widget.pro -spec linux-g++ -r CONFIG+=debug
Завершено с кодом 0.
Запускается: /usr/bin/make -w
make: Вход в каталог `/home/sabrog/work/q_property_widget'
/opt/qtsdk-2010.02/qt/bin/uic widget.ui -o ui_widget.h
g++ -c -pipe -g -Wall -W -D_REENTRANT -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED -I/opt/qtsdk-2010.02/qt/mkspecs/linux-g++ -I. -I/opt/qtsdk-2010.02/qt/include/QtCore -I/opt/qtsdk-2010.02/qt/include/QtGui -I/opt/qtsdk-2010.02/qt/include -I. -I. -o main.o main.cpp
In file included from /opt/qtsdk-2010.02/qt/include/QtCore/qsharedpointer.h:52,
from /opt/qtsdk-2010.02/qt/include/QtGui/qpixmap.h:49,
from /opt/qtsdk-2010.02/qt/include/QtGui/qbrush.h:53,
from /opt/qtsdk-2010.02/qt/include/QtGui/qpalette.h:47,
from /opt/qtsdk-2010.02/qt/include/QtGui/qwidget.h:49,
from /opt/qtsdk-2010.02/qt/include/QtGui/QWidget:1,
from widget.h:4,
from main.cpp:2:
/opt/qtsdk-2010.02/qt/include/QtCore/qsharedpointer_impl.h: In member function ‘void QtSharedPointer::ExternalRefCount<T>::internalDestroy() [with T = Ui::Widget]’:
/opt/qtsdk-2010.02/qt/include/QtCore/qsharedpointer_impl.h:329: instantiated from ‘bool QtSharedPointer::ExternalRefCount<T>::deref() [with T = Ui::Widget]’
/opt/qtsdk-2010.02/qt/include/QtCore/qsharedpointer_impl.h:380: instantiated from ‘QtSharedPointer::ExternalRefCount<T>::~ExternalRefCount() [with T = Ui::Widget]’
/opt/qtsdk-2010.02/qt/include/QtCore/qsharedpointer_impl.h:441: instantiated from here
/opt/qtsdk-2010.02/qt/include/QtCore/qsharedpointer_impl.h:391: warning: possible problem detected in invocation of delete operator:
/opt/qtsdk-2010.02/qt/include/QtCore/qsharedpointer_impl.h:391: warning: invalid use of incomplete type ‘struct Ui::Widget’
widget.h:7: warning: forward declaration of ‘struct Ui::Widget’
/opt/qtsdk-2010.02/qt/include/QtCore/qsharedpointer_impl.h:391: note: neither the destructor nor the class-specific operator delete will be called, even if they are declared when the class is defined.
g++ -c -pipe -g -Wall -W -D_REENTRANT -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED -I/opt/qtsdk-2010.02/qt/mkspecs/linux-g++ -I. -I/opt/qtsdk-2010.02/qt/include/QtCore -I/opt/qtsdk-2010.02/qt/include/QtGui -I/opt/qtsdk-2010.02/qt/include -I. -I. -o widget.o widget.cpp
/opt/qtsdk-2010.02/qt/bin/moc -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED -I/opt/qtsdk-2010.02/qt/mkspecs/linux-g++ -I. -I/opt/qtsdk-2010.02/qt/include/QtCore -I/opt/qtsdk-2010.02/qt/include/QtGui -I/opt/qtsdk-2010.02/qt/include -I. -I. widget.h -o moc_widget.cpp
g++ -c -pipe -g -Wall -W -D_REENTRANT -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED -I/opt/qtsdk-2010.02/qt/mkspecs/linux-g++ -I. -I/opt/qtsdk-2010.02/qt/include/QtCore -I/opt/qtsdk-2010.02/qt/include/QtGui -I/opt/qtsdk-2010.02/qt/include -I. -I. -o moc_widget.o moc_widget.cpp
In file included from /opt/qtsdk-2010.02/qt/include/QtCore/qsharedpointer.h:52,
from /opt/qtsdk-2010.02/qt/include/QtGui/qpixmap.h:49,
from /opt/qtsdk-2010.02/qt/include/QtGui/qbrush.h:53,
from /opt/qtsdk-2010.02/qt/include/QtGui/qpalette.h:47,
from /opt/qtsdk-2010.02/qt/include/QtGui/qwidget.h:49,
from /opt/qtsdk-2010.02/qt/include/QtGui/QWidget:1,
from widget.h:4,
from moc_widget.cpp:10:
/opt/qtsdk-2010.02/qt/include/QtCore/qsharedpointer_impl.h: In member function ‘void QtSharedPointer::ExternalRefCount<T>::internalDestroy() [with T = Ui::Widget]’:
/opt/qtsdk-2010.02/qt/include/QtCore/qsharedpointer_impl.h:329: instantiated from ‘bool QtSharedPointer::ExternalRefCount<T>::deref() [with T = Ui::Widget]’
/opt/qtsdk-2010.02/qt/include/QtCore/qsharedpointer_impl.h:380: instantiated from ‘QtSharedPointer::ExternalRefCount<T>::~ExternalRefCount() [with T = Ui::Widget]’
/opt/qtsdk-2010.02/qt/include/QtCore/qsharedpointer_impl.h:441: instantiated from here
/opt/qtsdk-2010.02/qt/include/QtCore/qsharedpointer_impl.h:391: warning: possible problem detected in invocation of delete operator:
/opt/qtsdk-2010.02/qt/include/QtCore/qsharedpointer_impl.h:391: warning: invalid use of incomplete type ‘struct Ui::Widget’
widget.h:7: warning: forward declaration of ‘struct Ui::Widget’
/opt/qtsdk-2010.02/qt/include/QtCore/qsharedpointer_impl.h:391: note: neither the destructor nor the class-specific operator delete will be called, even if they are declared when the class is defined.
g++ -Wl,-rpath,/opt/qtsdk-2010.02/qt/lib -o q_property_widget main.o widget.o moc_widget.o -L/opt/qtsdk-2010.02/qt/lib -lQtGui -L/opt/qtsdk-2010.02/qt/lib -L/usr/X11R6/lib -lQtCore -lpthread
make: Выход из каталога `/home/sabrog/work/q_property_widget'
Завершено с кодом 0.



DEADHUNT
а чем std::shared_ptr или boost::shared_ptr не устроили?
SABROG
Цитата(DEADHUNT @ 26.5.2010, 16:53) *
а чем std::shared_ptr или boost::shared_ptr не устроили?


Первый тем, что std::tr1::shared_ptr. Второй тем, что boost. Ставить дополнительную библиотеку, когда есть QSharedPointer, нафига?
DIMEDROLL
Цитата(DEADHUNT @ 26.5.2010, 15:53) *
а чем std::shared_ptr или boost::shared_ptr не устроили?

предполагаю что и эти шаблоны не будут работать с forward declaration.

Цитата
Как я понял размер класса нужен QSharedPointer для удаления указателя, но например в таком случае все великолепно:

То что main.cpp компилится скорее глюк, чем правильное поведение. Насколько я понмю - шаблоны нельзя инициализировать неизвестным классом...
DEADHUNT
Цитата(SABROG @ 26.5.2010, 16:16) *
Первый тем, что std::tr1::shared_ptr. Второй тем, что boost. Ставить дополнительную библиотеку, когда есть QSharedPointer, нафига?

tr1 уже входит в C++0x. boost.smart_pointers ничего не требует кроме заголовочных файлов и темболее многие библиотеки из boost добавляются в стандарт.

Цитата(DIMEDROLL @ 26.5.2010, 16:50) *
предполагаю что и эти шаблоны не будут работать с forward declaration.

будут.
Цитата(DIMEDROLL @ 26.5.2010, 16:50) *
Насколько я понмю - шаблоны нельзя инициализировать неизвестным классом...

undefined behavior.
SABROG
Цитата(DEADHUNT @ 27.5.2010, 8:42) *
tr1 уже входит в C++0x.


tr1 - technical report. По сути отчет от разработчиков компилятора о том, что уже готово из будущего стандарта (C++0x), который еще не вышел. Но когда он выйдет я сомневаюсь, что разработчики компиляторов сделают этот стандарт включеным "по умолчанию". Если они так поступят, то огромное количество приложений, поддержка, которых давно закончилась, просто перестанет собираться без какого-нибудь ключа типа "-std=c++98". Кроме того существуют платформы, где используются старые версии gcc (сделали один раз порт под платформу и забросили), на них конечно никакие программы на Qt написанные с использованием нового стандарта собираться не будут.

Но я уже выбрал, что хотел, тема о другом. Если это баг, то надо писать троллям, если нет, то смирюсь и пойду дальше.
---
Сделал так для проверки. Всё собирается без предупреждений. Пойду делать багрепорт.
#include <QtGui/QWidget>

#if 0
#include <tr1/memory>
#define QSharedPointer std::tr1::shared_ptr
#endif
BRE
Цитата(SABROG @ 27.5.2010, 9:48) *
Если это баг, то надо писать троллям, если нет, то смирюсь и пойду дальше.

Компилятор не может развернуть шаблон не зная типов параметров.
То, что нормально собирается пример с одним файлом - не удивительно, компилятор находит описание структуры, даже если она описана позже, но находится в одной единице компиляции (в одном файле).
SABROG
Логически ведь получается такая схема:
- moc генерит .cpp файлы
- компилятор собирает все модули

Стало быть на втором этапе ему должно быть всё известно, moc же вызывается до компиляции, а не после.
DIMEDROLL
Цитата(DEADHUNT @ 27.5.2010, 7:42) *
будут.
...
undefined behavior.

так будут работать или undefined behavior?
DEADHUNT
Цитата(DIMEDROLL @ 27.5.2010, 12:51) *
так будут работать или undefined behavior?

стандарт посмотри там написано неопределённое поведение, но во многих компиляторах это разрешается.
DIMEDROLL
Цитата(DEADHUNT @ 27.5.2010, 11:59) *
стандарт посмотри там написано неопределённое поведение, но во многих компиляторах это разрешается.

посмотрю, а ты смотрел? подскажешь страничку или пункт?

если в стандарте сказано что будет неопределённое поведение при forward declaration, то зачем писать такой код? и зачем советовать tr1 и boost?
DEADHUNT
Цитата(DIMEDROLL @ 27.5.2010, 13:13) *
посмотрю, а ты смотрел? подскажешь страничку или пункт?

если в стандарте сказано что будет неопределённое поведение при forward declaration, то зачем писать такой код? и зачем советовать tr1 и boost?

что трудно самому стандарт открыть? в 14.4.1 [Note: a template type argument may be an incomplete type (3.9). — end note ], значит всё предсказуемо, просто на исходниках кто-то писал про неопределённое поведение, вот я и загнался.
BRE
Цитата(SABROG @ 27.5.2010, 12:21) *
Логически ведь получается такая схема:
- moc генерит .cpp файлы
- компилятор собирает все модули

Стало быть на втором этапе ему должно быть всё известно, moc же вызывается до компиляции, а не после.

И moc и uic уже отработали, файл ui_widget.h уже сгенерирован.
Если ты добавишь:
#include "ui_widget.h"
в widget.h, то все неоднозначности разрешаться и все скомпилируется.

Forward declaration просто говорит компилятору, что такой класс/тип есть. Если использовать обычный указатель, то это будет работать, потому что компилятор знает размер указателя для любого типа (размер указателя на объект любого класса равен размеру указателя на void). А вот если для шаблона нужен размер объекта, то без полного описания класса компилятор определить его не может.
SABROG
Цитата(BRE @ 27.5.2010, 17:32) *
Если ты добавишь:
#include "ui_widget.h"


И как побочный эффект я потеряю все преимущества связанные с forward declaration, фиксированным размером указателя, скоростью компиляции и перекомпиляции всего приложения при изменении интерфейса.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2021 IPS, Inc.