LINUX.ORG.RU

[Исходники Qt][ООП] Что-то я не пойму...

 


0

2

Привожу кусок кода из файла $QTDIR/src/corelib/codecs/qtextcodec.h

class Q_CORE_EXPORT QTextCodec
{
    Q_DISABLE_COPY(QTextCodec)
public:
...
    QString toUnicode(const char *in, int length, ConverterState *state = 0) const
        { return convertToUnicode(in, length, state); }
    QByteArray fromUnicode(const QChar *in, int length, ConverterState *state = 0) const
        { return convertFromUnicode(in, length, state); }

    // ### Qt 5: merge these functions.
    QTextDecoder* makeDecoder() const;
    QTextDecoder* makeDecoder(ConversionFlags flags) const;
    QTextEncoder* makeEncoder() const;
    QTextEncoder* makeEncoder(ConversionFlags flags) const;

    virtual QByteArray name() const = 0;
    virtual QList<QByteArray> aliases() const;
    virtual int mibEnum() const = 0;

protected:
    virtual QString convertToUnicode(const char *in, int length, ConverterState *state) const = 0;
    virtual QByteArray convertFromUnicode(const QChar *in, int length, ConverterState *state) const = 0;

Смотрим на метод toUnicode(всё аналогично для fromUnicode). Он вызывает convertToUnicode(это метод из секции protected?). Он виртуальный и абстрактный? Но класс-то не виртуальный, я могу создавать его экземпляры и вызывать этот метод toUnicode свободно. WTF? Как это вообще скомпилировалось? Я забыл азы ООП или чего не знаю?

Собственно как я вышел на этот файл. Ковырял свою программу(точнее один тест для неё), в тесте случился segmentation fault. Повторил - не повторяется. Повторил ещё раз - не падает. Хорошо, у меня настроено делать core dump. Беру gdb, смотрю core dump. И вижу это место в backtrace. Нутром чую, что здесь какое-то нехорошее место и должно падать при вызове абстрактного метода. Один раз собственно и упало.



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

>Но класс-то не виртуальный, я могу создавать его экземпляры и вызывать этот метод toUnicode свободно

Серьёзно? Ну-ка создай и вызови.

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

Вот это дело вставил к себе в проект
[code=cpp]
char c[10];
QString s;

s=UTF8Codec->toUnicode(c, 5);
[/code]
компилируется без проблем.

gogi
() автор топика

Да, забыл написать что Qt 4.7.0

gogi
() автор топика

>я могу создавать его экземпляры

ну и как ты его создаешь?

у него мало того что абстрактные методы, у него еще конструктор - protected

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

Сходи сюда: qthelp://com.trolltech.qt.470/qdoc/qtextcodec.html
Где тут написано, что класс абстрактный?
И нету всяких «Inherited by», «Inherits»

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

Почитай любую книжку по основам С++, ты не понимаешь, как работает полиморфизм.

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

>Где тут написано, что класс абстрактный? И нету всяких «Inherited by», «Inherits»

иди почитай, что такое абстрактный класс :)

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

Указатель на объект QTextCodec. Так легче?

нет. это и не указатель на объект QTextCodec

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

>Указатель на объект QTextCodec

Уже теплее, но придётся ещё немного подумать :)

И, да, классы в плюсах ещё не стали объектами

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

>иди почитай, что такое абстрактный класс :)

Ты Qt Assistant видел хоть? Так всё хорошо продокументировано. И «Inherits», «Inherited by» - это признаки наследования вверх/вниз. Нахрена заводить абстрактный класс без идеи потом отнаследоваться от него? Методы судя по описанию абстрактные, это признаю. Но Qt я сам компилировал, вот она лежит у меня libQtCore.so.4.7.0. И в ней есть этот класс и класс работает.

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

>И в ней есть этот класс и класс работает.

Ну так ты объект-то создай этого бедного QTextCodec'а. Неужели это так сложно, написать main с одной автоматической переменной внутри?

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

Мня, смотри что я нашел! (менее чем за 5 минут)

class QUtf8Codec : public QTextCodec {
public:
    ~QUtf8Codec();

    QByteArray name() const;
    int mibEnum() const;

    QString convertToUnicode(const char *, int, ConverterState *) const;
    QByteArray convertFromUnicode(const QChar *, int, ConverterState *) const;
    void convertToUnicode(QString *target, const char *, int, ConverterState *) const;
};
rival ★★
()
Ответ на: комментарий от yoghurt

>Уже теплее, но придётся ещё немного подумать :)

И, да, классы в плюсах ещё не стали объектами


Ну что ты от меня хочешь? Извини, каждый день в словесных формулировках по теме не упражняюсь, что-то и забывается. Я в последнее время занимаюсь в основном практикой, а не штудированием страуструпов. Есть тип данных - класс QTextCodec. Есть динамически-созданная переменная этого класса(объект). Указатель на него возвращает статическая функция этого же класса QTextCodec::codecForName().

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

> Есть тип данных - класс QTextCodec.

Да.

Есть динамически - созданная переменная этого класса.


Нет. Создается переменная класса-потомка.

Указатель на него возвращает статическая функция этого же класса QTextCodec::codecForName().


Да.

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

QUtf8Codec

Вот теперь мне стало ясно, экземпляр какого класса у меня в наличии. C терминалогией вроде разобрались :)

Но мне гораздо интереснее откуда у меня segmentation fault. Вот кусок backtrac'а

#5  <signal handler called>
#6  0x0000000003b088a0 in ?? ()
#7  0x00007f76f0df7d8a in QTextCodec::toUnicode (this=<value optimized out>, cformat=0x7f76f0f46c30 "QEventLoop: Cannot be used without QApplication", ap=0x7f76ed647cf0)
    at ../../include/QtCore/../../src/corelib/codecs/qtextcodec.h:116
#8  QString::vsprintf (this=<value optimized out>, cformat=0x7f76f0f46c30 "QEventLoop: Cannot be used without QApplication", ap=0x7f76ed647cf0) at tools/qstring.cpp:5119
#9  0x00007f76f0da97ab in qt_message (msgType=QtWarningMsg, msg=0x3b04bc0 "\360;\260\003", ap=0x7f76f0f46c30) at global/qglobal.cpp:2296

Как раз на фрейме 6 стек поломался. Я уж и подумал, что здесь был вызов абстрактного метода.

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

:D

да, mv надо памятник ставить, золотые слова :)

я тебе последний раз говорю - ты не знаешь что такое абстрактный класс! иди и учи!

если в документации нету InheritedBy, это не значит его не могут наследовать внутренние, приватные по отношению к юзерспейсу классы.

смотри:
http://qt.gitorious.org/qt/qt/blobs/4.7-stable/src/corelib/codecs/qlatincodec...

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

>Есть динамически-созданная переменная этого класса(объект)

Нет, в том то и дело что не этого класса

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

>Ну очевидно же.
Ну это на более «высоком» уровне произошло, с этим тоже надо разобраться. Но в коде кодеков вроде-бы(ещё пока смотрю) нету никаких событий, чего там-то вылетает? Или тут по принципу, если где-то произошла серьёзная ошибка, значит где-то должно вылететь?

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

Кода очень много. Боюсь что баг трудноуловимый будет и не совсем с моим кодом связанный. Ну если вкратце: Есть главный поток и ещё один. Понимаю, что звучит уже опасно. Но на этом этапе никакой межпоточной связи ещё нету. Когда он(не главный) начинает выполняться, внутри его стека вызовов создаётся объект типа QEventLoop. Он-то шлёт какой-то внутренний варнинг, и получаем, то что получаем.

QEventLoop::QEventLoop(QObject *parent)
    : QObject(*new QEventLoopPrivate, parent)
{
    Q_D(QEventLoop);
    if (!QCoreApplication::instance()) {
        qWarning("QEventLoop: Cannot be used without QApplication");
    } else if (!d->threadData->eventDispatcher) {
        QThreadPrivate::createEventDispatcher(d->threadData);
    }
} 

То есть получается, что к этому моменту QCoreApplication::instance()==NULL. А это странно, т.к. в коде моего теста есть строки, как рекомендуют в документации

QTEST_MAIN(TestUMath)
#include "testumath.moc"

Макрос QTEST_MAIN объявляет функцию main и создаёт объект QApplication, а потом уже вызывает мои слоты.

Если метод QCoreApplication::instance() возвращает значение поля, то по-видимому оно переписалось левым NULL.

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

Кстати, вот полный backtrace

(gdb) bt
#0  0x0000000003b088a0 in ?? ()
#1  0x00007f76f0df797c in QTextCodec::toUnicode (str=0x54a32a "SIGsFAULT_handler_wrapper", size=<value optimized out>) at ../../include/QtCore/../../src/corelib/codecs/qtextcodec.h:116
#2  QString::fromAscii_helper (str=0x54a32a "SIGsFAULT_handler_wrapper", size=<value optimized out>) at tools/qstring.cpp:3767
#3  0x00000000004445f3 in QString (this=0x7f76ed647290, ch=0x54a32a "SIGsFAULT_handler_wrapper") at /home/sergey/code/QtEmbedded-4.7.0-x86/include/QtCore/qstring.h:426
#4  0x00000000004fc093 in SIGsFAULT_handler_wrapper (p=11, info=0x7f76ed647770) at /home/sergey/code/r505/src/fmain.cpp:43
#5  <signal handler called>
#6  0x0000000003b088a0 in ?? ()
#7  0x00007f76f0df7d8a in QTextCodec::toUnicode (this=<value optimized out>, cformat=0x7f76f0f46c30 "QEventLoop: Cannot be used without QApplication", ap=0x7f76ed647cf0)
    at ../../include/QtCore/../../src/corelib/codecs/qtextcodec.h:116
#8  QString::vsprintf (this=<value optimized out>, cformat=0x7f76f0f46c30 "QEventLoop: Cannot be used without QApplication", ap=0x7f76ed647cf0) at tools/qstring.cpp:5119
#9  0x00007f76f0da97ab in qt_message (msgType=QtWarningMsg, msg=0x3b04bc0 "\360;\260\003", ap=0x7f76f0f46c30) at global/qglobal.cpp:2296
#10 0x00007f76f0da9d35 in qWarning (msg=0x3b04bc0 "\360;\260\003") at global/qglobal.cpp:2387
#11 0x00007f76f0dae8ff in QThread::exec (this=<value optimized out>) at thread/qthread.cpp:489
#12 0x0000000000487908 in IOManagerThread::run (this=0x3ba7720) at /home/sergey/code/r505/src/iomanagerthread.cpp:196
#13 0x00007f76f0db0979 in QThreadPrivate::start (arg=<value optimized out>) at thread/qthread_unix.cpp:266
#14 0x00007f76f0b158ba in start_thread (arg=<value optimized out>) at pthread_create.c:300
#15 0x00007f76f00d102d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112
#16 0x0000000000000000 in ?? ()

frame 16 и тут поломан? Это мне не нравиться.

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

hint: Осторожно, быдлокод

#include "testcommon.h"
#include "umath.h"

class TestUMath: public QObject
{
    Q_OBJECT
private slots:
    void initTestCase();

    void tCommon();
    void tLSA();
    void tNumericIntegration();
};

void TestUMath::initTestCase()
{
	DoTestsCommonInit();
}

void TestUMath::tCommon()
{
    my_compare(iLog2(0), 0);
    my_compare(iLog2(1), 0);
    my_compare(iLog2(2), 1);
    my_compare(iLog2(3), 1);
    my_compare(iLog2(4), 2);
    my_compare(iLog2(7), 2);
    my_compare(iLog2(8), 3);
    my_compare(iLog2(15), 3);

    my_compare(my_lround<int>(1.09), 1);
    my_compare(my_lround<int>(1.9), 2);
    my_compare(my_lround<int>(1.49), 1);
    my_compare(my_lround<int>(-1.49), -1);

    my_compare(my_lroundf<int>(1.09f), 1);
    my_compare(my_lroundf<int>(1.9f), 2);
    my_compare(my_lroundf<int>(1.49f), 1);
    my_compare(my_lroundf<int>(-1.49f), -1);

    my_compare(my_round(1.09), 1.0);
    my_compare(my_round(1.9), 2.0);
    my_compare(my_round(1.49), 1.0);
    my_compare(my_round(-1.49), -1.0);

    my_compare(my_roundf(1.09f), 1.0);
    my_compare(my_roundf(1.9f), 2.0);
    my_compare(my_roundf(1.49f), 1.0);
    my_compare(my_roundf(-1.49f), -1.0);
}

void TestUMath::tLSA()
{
    const int buf_size=99; // Надо чтобы делился на 3.
    const uint test_val=buf_size;
    uint16 buf[buf_size];
    uint16 new_y1, new_y2;

    // На входе - горизонтальная линия, она же должна быть на выходе.
    for (int i=0; i<buf_size; i++)
        buf[i]=test_val;
    CalcLSA(buf, 0, buf_size-1, new_y1, new_y2);
    my_compare(new_y1, test_val);
    my_compare(new_y2, test_val);

    // На входе наклонная линия, на выходе должна быть она же.
    for (int i=0; i<buf_size; i++)
        buf[i]=test_val-i-1;
    CalcLSA(buf, 0, buf_size-1, new_y1, new_y2);
    my_compare(new_y1, test_val-1);
    my_compare(new_y2, 0);

    // Интуитивно чувствую, что условие правильное.
    for (int i=0; i<buf_size/3; i++)
        buf[i]=0;
    for (int i=buf_size/3; i<2*buf_size/3; i++)
        buf[i]=test_val;
    for (int i=2*buf_size/3; i<buf_size; i++)
        buf[i]=0;
    CalcLSA(buf, 0, buf_size-1, new_y1, new_y2);
    my_compare(new_y1, test_val/3);
    my_compare(new_y2, test_val/3);
}

void TestUMath::tNumericIntegration()
{
    const int buf_size=100;
    const uint test_val=buf_size;
    uint16 buf[buf_size];
    float s;

    // На входе - горизонтальная линия, интеграл - площадь прямоугольника.
    for (int i=0; i<buf_size; i++)
        buf[i]=test_val;

    s=CalcNumericIntergation(buf, 1.0, 0, buf_size-1, 1);
    my_compare(s, (buf_size-1)*test_val);

    s=CalcNumericIntergation(buf, 1.0, 0, buf_size-2, 1);
    my_compare(s, (buf_size-2)*test_val);

    // На входе наклонная линия, на выходе площадь треугольника.
    for (int i=0; i<buf_size; i++)
        buf[i]=test_val-i-1;

    s=CalcNumericIntergation(buf, 1.0, 0, buf_size-1, 1);
    my_compare(s, 0.5*(buf_size-1)*(test_val-1));
}

QTEST_MAIN(TestUMath)
#include "testumath.moc"
gogi
() автор топика
Ответ на: комментарий от gogi

frame 16 и тут поломан? Это мне не нравиться.

А может с gdb кто подскажет что можно сделать? В нормальном случае стек вызовов выглядит примерно так:

#4  0x000000000044bc2d in DoInitialization (parameters=...) at /home/sergey/code/r505/src/init.cpp:843
#5  0x000000000043a162 in DoTestsCommonInit () at /home/sergey/code/r505/tests/testcommon.h:77
#6  0x000000000043a1b1 in TestUMath::initTestCase (this=0x7fff21cc3420) at /home/sergey/code/r505/tests/umath/testumath.cpp:17
#7  0x0000000000444356 in TestUMath::qt_metacall (this=0x7fff21cc3420, _c=QMetaObject::InvokeMetaMethod, _id=0, _a=0x7fff21cc2e00) at ../qmake/embedded/linux-x86-g++/debug/moc/testumath.moc:75
#8  0x00007f8b04a887f8 in QMetaMethod::invoke (this=0x7fff21cc3090, object=0x7fff21cc3420, connectionType=<value optimized out>, returnValue=..., val0=..., val1=..., val2=..., val3=..., 
    val4=..., val5=..., val6=..., val7=..., val8=..., val9=...) at kernel/qmetaobject.cpp:1575
#9  0x00007f8b071549d5 in QMetaMethod::invoke (obj=0x7fff21cc3420, methodName=0x7f8b0716592f "initTestCase()") at ../../include/QtCore/../../src/corelib/kernel/qmetaobject.h:119
#10 invokeMethod (obj=0x7fff21cc3420, methodName=0x7f8b0716592f "initTestCase()") at qtestcase.cpp:888
#11 0x00007f8b0715725e in qInvokeTestMethods (testObject=<value optimized out>, argc=<value optimized out>, argv=<value optimized out>) at qtestcase.cpp:1479
#12 QTest::qExec (testObject=<value optimized out>, argc=<value optimized out>, argv=<value optimized out>) at qtestcase.cpp:1708
#13 0x0000000000444234 in main (argc=1, argv=0x7fff21cc3538) at /home/sergey/code/r505/tests/umath/testumath.cpp:110

В функции DoInitialization() и происходит создание потока и последующее падение, как в первом случае.

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

Inherits и Inherited by пишутся только для документированных классов, все же реализации конкретных кодеков скрыты, доступ к ним через статический метод. По поводу падения - очевидно, что поток пытается стартовать до того, как будет создан или после того, как был уничтожен QApplication. Возможно из-за испорченной памяти.

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

По поводу падения - очевидно, что поток пытается стартовать до того, как будет создан или после того, как был уничтожен QApplication. Возможно из-за испорченной памяти.

В моём случае функция main() тривиальна.

#define QTEST_MAIN(TestObject) \
int main(int argc, char *argv[]) \
{ \
    QApplication app(argc, argv); \
    QTEST_DISABLE_KEYPAD_NAVIGATION \
    TestObject tc; \
    return QTest::qExec(&tc, argc, argv); \
}
app нигде не удаляется. Похоже на этот раз что-то системное: глючит linux/libc/Qt.

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

Сразу видно сишника.

template <typename TestObject>
int testObjectMain(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QTEST_DISABLE_KEYPAD_NAVIGATION
    TestObject tc;
    return QTest::qExec(&tc, argc, argv);
}

int main()
{
    return testObjectMain<TestUMath>();
}

Загрузите в отладчике, пройдитесь по стеку вызовов, посмотрите вследствие чего оно ругается на отсутствие QApplication. Вполне может быть, что поток удаляется через delete без предварительного QThread::wait().

QThread::~QThread ()

Destroys the thread.

Note that deleting a QThread object will not stop the execution of the thread it represents. Deleting a running QThread (i.e. isFinished() returns false) will probably result in a program crash. You can wait() on a thread to make sure that it has finished.

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

> Поздно уже, баг не повторяется

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

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

> Замечу, что макрос QTEST_MAIN - не мой, а Qt'шный.

Тогда извините (-:

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

> воистину

Таки да:(.

Но пока что кьютишники с дельфийским складом ума фильтруются двумя-тремя простейшими вопросами на тему использования layout'ов.

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

Но пока что кьютишники с дельфийским складом ума фильтруются двумя-тремя простейшими вопросами на тему использования layout'ов.

Вопросы в студию.

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

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

Да вроде всё тут нормально. К сожалению я не запомнил, успел ли отработать тест с надписями «PASS» в консоли, т.е. вызвались ли мои тестирующие методы. Сейчас всё что есть у меня по этому багу - это core dump. И судя по нему, поток ещё находился в состоянии инициализации-запуска. Может тут ему крышу и снесло: он запускается, а его уже прибивают? Если так, неприятно, конечно, но в реальном приложении эта ситуация невероятна.

Сейчас у меня, в деструкторе главного окна(я думаю что оно всяко раньше объекта приложения удаляется) грубо говоря есть такой код

	ioManagerThread->quit();
	...
	bool res=ioManagerThread->wait(250);
	if (!res)
	{
		ioManagerThread->terminate();
		delete ioManagerThread;
	}

Вроде даю потоку самому завершиться, Жду, не хочет, тогда киляю.

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

1. сам поток не завершиться, если ты не обработал эту ситуация внутри потока - смотри примеры как более правильно это делать
2. если главное окно владеет потоком то удалять самому поток нельзя

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

>1. сам поток не завершиться, если ты не обработал эту ситуация внутри потока - смотри примеры как более правильно это делать

Да тут речь о том, что поток ещё как бы только стартует(QThread::exec()). Я ещё ничего не могу обработать.

2. если главное окно владеет потоком то удалять самому поток нельзя

Нет, не владеет. Главное окно тут притом, что мне нужен какой-нибудь слот к которому можно прицепить сигнал QCoreApplication::aboutToQuit().

gogi
() автор топика

офигенный тред. спасибо нубу-недоучке. поржал)))

А вот это, я пожалуй возьму за антипример.

#define QTEST_MAIN(TestObject) \
int main(int argc, char *argv[]) \
{ \
    QApplication app(argc, argv); \
    QTEST_DISABLE_KEYPAD_NAVIGATION \
    TestObject tc; \
    return QTest::qExec(&tc, argc, argv); \
}

gogi, ты переплюнул всех индусов, которых я когда-либо встречал.

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

>gogi, ты переплюнул всех индусов, которых я когда-либо встречал.

Прежде чем ржать научись читать. Я уже писал, что это не мой код, а код из одного модуля Qt.

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