Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linker error with Qt Signal/Slot example

I have a Qt application with multiple classes that use signals and slots and it compiles just fine. However, when I make a custom class inside the main CPP (main.cpp) file, I get a linker error.

Here is the code I use:

class Counter : public QObject
{
    Q_OBJECT

public:
    Counter() { m_value = 0; }

    int value() const { return m_value; }

public slots:
    void setValue(int value)
    {
     if(value!=m_value)
     {
         m_value = value;
         qDebug() << "Value " << value;
         emit valueChanged(value);
     }
    }

signals:
    void valueChanged(int newValue);

private:
    int m_value;
};

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    Counter a, b;
    QObject::connect(&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int)));

    a.setValue(12);     // a.value() == 12, b.value() == 12
    b.setValue(48);     // a.value() == 12, b.value() == 48

    QTimer::singleShot(0, &app, SLOT(quit()));

    return app.exec();
}

Here are the errors:

Error   4   error LNK2001: unresolved external symbol "public: virtual struct QMetaObject const * __thiscall Counter::metaObject(void)const " (?metaObject@Counter@@UBEPBUQMetaObject@@XZ)  
Error   5   error LNK2001: unresolved external symbol "public: virtual void * __thiscall Counter::qt_metacast(char const *)" (?qt_metacast@Counter@@UAEPAXPBD@Z)
Error   6   error LNK2001: unresolved external symbol "public: virtual int __thiscall Counter::qt_metacall(enum QMetaObject::Call,int,void * *)" (?qt_metacall@Counter@@UAEHW4Call@QMetaObject@@HPAPAX@Z)   
Error   7   error LNK2019: unresolved external symbol "protected: void __thiscall Counter::valueChanged(int)" (?valueChanged@Counter@@IAEXH@Z) referenced in function "public: void __thiscall Counter::setValue(int)" (?setValue@Counter@@QAEXH@Z)

This linker error does not occur when I place the counter in a separate header file. What's the reason for this behavior?

like image 862
Kiril Avatar asked Nov 28 '12 21:11

Kiril


People also ask

How signal and slot works in Qt?

In Qt, we have an alternative to the callback technique: We use signals and slots. A signal is emitted when a particular event occurs. Qt's widgets have many predefined signals, but we can always subclass widgets to add our own signals to them. A slot is a function that is called in response to a particular signal.

Are Qt signals and slots thread safe?

It is generally unsafe to provide slots in your QThread subclass, unless you protect the member variables with a mutex. On the other hand, you can safely emit signals from your QThread::run() implementation, because signal emission is thread-safe.

What is private slots in Qt?

This means that a signal emitted from an instance of an arbitrary class can cause a private slot to be invoked in an instance of an unrelated class. What this means: From another class, you can't call a private slot as a function, but if you emit a signal connected to that private slot, you can invoke it."


3 Answers

I'm assuming you're working with qmake.

The moc is made to run on header files automatically by default, because that's where classes are declared in general. Notice that this rule is defined in the makefile, you can manually run moc on a source file.

You have to inform qmake that the file contains a class. To do this, put #include "filename.moc" after the declaration of Counter. You can see more details here (QtCentre) or here (doc).

If you're working with another tool than qmake, say CMake, you have to specify a rule to force the moc to parse the .cpp files (the simplest is to process them all). For files that do not contain a Qt object class, moc will generate an empty file.

However, even if this class is made to be 'private', I advise you to declare it in a header (for example counter_private.h). For example, Qt source is using this trick.

like image 126
Synxis Avatar answered Sep 21 '22 21:09

Synxis


It looks like you have only one code file. If you use the default way to create a Qt project build (qmake && make or QtCreator), the MOC only scans *.h files. If you have all your code in one main.cpp the MOC will not create any code, but that's needed for Signal/Slots to work.

The simplest way to make this specific example working would be adding a line "#include "main.moc"" at the end of your main.cpp. This dependency will be detected by qmake and the needed Makefile targets will be created.

The cutest way would be the clean one: One class - One header and one implementation file.

like image 44
Alexander Nassian Avatar answered Sep 23 '22 21:09

Alexander Nassian


They moc/uic custom build commands are done on the header file, so it compiles when put in a seperate header/source file and not when put in the same source file

like image 37
dmaij Avatar answered Sep 25 '22 21:09

dmaij