Reputation: 40395
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?
Upvotes: 5
Views: 8767
Reputation: 9388
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.
Upvotes: 6
Reputation: 517
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.
Upvotes: 2
Reputation: 1007
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
Upvotes: 1