Global and Local variable, running into problem with connect()

0

I have this code: mainclass.cpp file:

#include "mainclass.h"
#include <QtDebug>
#include <QApplication>

Domino *domino = new Domino();

int main(int argc, char *argv[])
{
    qDebug()<< __FUNCTION__;
    QApplication a(argc, argv);
    MainClass w;
    w.show();
    return a.exec();
}

MainClass::MainClass(QWidget *parent) : QMainWindow(parent)
{
    qDebug()<< __FUNCTION__;
    //Domino *domino = new Domino();
    connect(domino, SIGNAL(OneImageReceivedSignal()), this, SLOT(OneImageReceivedSlot()));
    domino->ReceiveAnImage();
}

void MainClass::OneImageReceivedSlot(){
    qDebug()<< __FUNCTION__;
}

domino.cpp file:

#include "domino.h"
#include <QtDebug>
Domino::Domino(QObject *parent) : QObject(parent)
{
    qDebug()<< __FUNCTION__;
    cdl_img_acquisition_= new CDLImageAcquisition();
    connect(cdl_img_acquisition_, SIGNAL(OneImageReceivedSignal()), this, SIGNAL(OneImageReceivedSignal()));
}


void Domino::ReceiveAnImage(){
    qDebug()<<__FUNCTION__;
    cdl_img_acquisition_->ReceiveAnImage();
}


CDLImageAcquisition *Domino::get_cdl_img_acquisition(){
    qDebug()<< __FUNCTION__;
    return this->cdl_img_acquisition_;
}

icdlimageacquisition.cpp file:

#include "cdlimageacquisition.h"
#include<QtDebug>

CDLImageAcquisition::CDLImageAcquisition(QObject *parent) : QObject(parent)
{
    qDebug()<< __FUNCTION__;
    icdlcam_=new ICDLCam();
    connect(icdlcam_, SIGNAL(OneImageReceivedSignal()),this, SIGNAL(OneImageReceivedSignal()));
}

void CDLImageAcquisition::ReceiveAnImage(){
    qDebug()<<__FUNCTION__;
    icdlcam_->ReceiveAnImage();
}

ICDLCam *CDLImageAcquisition::get_cdl_cam(){
    qDebug()<< __FUNCTION__;
    return this->icdlcam_;
}

icdlcam.cpp file:

#include "icdlcam.h"
#include <assert.h>
#include <QtDebug>

ICDLCam::ICDLCam(QObject *parent) : QObject(parent)
{
    qDebug()<< __FUNCTION__;
}


void ICDLCam::ReceiveAnImage(){
    qDebug()<<__FUNCTION__;
    emit OneImageReceivedSignal();
}

When I build and run with MSVC 2015, or 2019, the program crashed as below:

15:57:25: Starting C:\Users\User\source\DL01-SOFTWARE\build-DOMINO-TEST2-Desktop_Qt_5_15_0_MSVC2019_64bit-Debug\Lib\debug\Lib.exe ...
Domino::Domino
CDLImageAcquisition::CDLImageAcquisition
ICDLCam::ICDLCam
15:57:27: The program has unexpectedly finished.
15:57:27: The process was ended forcefully.
15:57:27: C:\Users\User\source\DL01-SOFTWARE\build-DOMINO-TEST2-Desktop_Qt_5_15_0_MSVC2019_64bit-Debug\Lib\debug\Lib.exe crashed.

The debugger crashes at connect(icdlcam_, SIGNAL(OneImageReceivedSignal()),this, SIGNAL(OneImageReceivedSignal())); of the CDLImageAcquisition constructor.

It shows this message dialog: "Exception triggered: The inferior stopped because it triggered an exception. Stopped in thread 0 by: Exception at 0x7fff9dd8da2a, code:0xc0000005: read access violation at: 0x0, flags = 0x0 (first chance).

The program does not crash when:

  • I declare and initialize domino variable inside the local scope of the MainClassconstructor
  • I build and run with the MinGW compiler regardless of the scope of the dominovariable.

I have to use Pylon API in Windows that does not support MinGW compiler, and I also need the global scope of the domino variable later. I also tried putting it as a static member of a class, but the result is the same.

c++
qt
visual-c++
global-variables
asked on Stack Overflow Dec 3, 2020 by Phạm Hồng Nhung • edited Dec 3, 2020 by Phạm Hồng Nhung

1 Answer

0

I don't have an explanation on what exactly happens, I can only assume that there is some Qt magic happening here with the Win32 API.

For GUI (non-console) applications, the starting point is not main, but WinMain, which is why Qt redefines main to qMain (https://code.qt.io/cgit/qt/qtbase.git/tree/src/gui/kernel/qwindowdefs.h?h=5.15#n114) and links to a special qtmain static library (https://code.qt.io/cgit/qt/qtbase.git/tree/src/winmain/?h=5.15), the details of which elude me...

Anyhow, a minimal example to reproduce the issue:

main.cpp:

#include <QObject>

class Test : public QObject
{
    Q_OBJECT
public:
    Test() {
        connect(this, &Test::sig, this, &Test::slot);
    }

    void slot() {}
signals:
    void sig();
};

Test *t = new Test;

int main(int argc, char *argv[]) {
//    Test t;
     return 0;
}

#include "main.moc"

test.pro

CONFIG += c++11
SOURCES += main.cpp

The above crashes.

If you create the Test object in main instead of globally, it no longer crashes.

If you build it as a console application, it no longer crashes, i.e. when changing the CONFIG line to CONFIG += c++11 console, rerunning qmake & rebuilding.

Bottom line: You cannot use connect before main.

Options that you have, besides not calling connect in the constructor of your global object (or not using a global object):

Allocate the global object in main

Test *t = nullptr;

int main(int argc, char *argv[]) {
    t = new Test;
    return 0;
}

Use Q_GLOBAL_STATIC

The object created by Q_GLOBAL_STATIC initializes itself on the first use, which means that it will not increase the application or the library's load time. Additionally, the object is initialized in a thread-safe manner on all platforms.

Q_GLOBAL_STATIC(Test, t)

somewhere else access the object either by calling methods with t->someMethod() or access the pointer with t(), e.g. connect(t(), &Test::sig, .....).

answered on Stack Overflow Dec 4, 2020 by E4z9

User contributions licensed under CC BY-SA 3.0