Qt: a missing vtable usually means the first non-i

2019-04-23 14:46发布

问题:

There are numerous threads on this all over. None seems to fit my bill. I'm getting the following linker errors in my code:

Undefined symbols for architecture x86_64:
  "vtable for MSFSPlugin::MSFSPluginImpl", referenced from:
      MSFSPlugin::MSFSPluginImpl::MSFSPluginImpl(QObject*) in MSFSPlugin.o
      MSFSPlugin::MSFSPluginImpl::~MSFSPluginImpl() in MSFSPlugin.o
  NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

It should have been something obvious as - a missing vtable usually means the first non-inline virtual member function has no definition. However, I don't see what I'm missing:

I have this class declation in MSFSPlugin.h:

class MSFSPlugin
    :
    public QObject,
    public IMediaSource
{
    Q_OBJECT
    Q_INTERFACES(IMediaSource)
    ...
protected:
    class MSFSPluginImpl;
    MSFSPluginImpl* mImpl;

}

Then in MSFSPlugin.cpp, I have the following:

class MSFSPlugin::MSFSPluginImpl : public QThread
{
    Q_OBJECT
    public:
        MSFSPluginImpl(QObject *parent = 0);
        virtual ~MSFSPluginImpl();

        QString     getSourceDirectory() const;
        void        setSourceDirectory(QString sourceDirectory);

    signals:
        void        loadDirectoryFinished(bool success);

    protected:
        QString     mSourceDirectory;
};

Followed by definitions:

MSFSPlugin::MSFSPluginImpl::MSFSPluginImpl(QObject *parent) : QThread(parent)
{
}

MSFSPlugin::MSFSPluginImpl::~MSFSPluginImpl()
{
}

QString MSFSPlugin::MSFSPluginImpl::getSourceDirectory() const
{
    return mSourceDirectory;
}

void MSFSPlugin::MSFSPluginImpl::setSourceDirectory(QString sourceDirectory)
{
    mSourceDirectory = sourceDirectory;
}

...

In short, I don't think I'm missing any non-inline virtual member function definition. I'm using:

Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
Target: x86_64-apple-darwin13.1.0
Thread model: posix

Additional Info:

In my moc_MSFSPlugin.cpp I don't see the autogenerated Q_OBJECT related code for class MSFSPlugin::MSFSPluginImpl which indirectly (via QThread) derives from QObject. Specifically, I don't see code generated for the signal, declared in that class (loadDirectoryFinished). Could this be the problem?

EDIT 1: The error goes away if I comment out Q_OBJECT from the declation of MSFSPlugin::MSFSPluginImpl, but then I lose the signal functionality.

EDIT 2: I see moc operates on header files only. Could this be related to the fact that my QObject derived class is declared & defined in a .cpp file?

回答1:

Assuming we're dealing with qmake.

The golden rules are:

  1. Make sure the Q_OBJECT macro is present in the definition of all QObject-derived classes.
  2. Make sure you declare your QObject-derived classes in your header files only.
  3. Make sure all of your header files are listed in your .pro file in the HEADERS= list.
  4. Run qmake every time you add Q_OBJECT to one of your classes or modify your .pro file.

Explanation

EDIT 1: The error goes away if I comment out Q_OBJECT from the declation of MSFSPlugin::MSFSPluginImpl, but then I lose the signal functionality.

Yup. Q_OBJECT is needed for declaring signals, slots, invokables, qobject_cast, translations, properties, enums and methods introspection, and so on.

The #1 golden rule comes from the fact that without Q_OBJECT you can't use stuff like qobject_cast on your class; if you use (even indirectly) introspection facilities, for instance to debug an object hierarchy or to dump all active connections to an object, then objects of your class will have the real class name shown (and not the one of the superclass); etc.

Q_OBJECT does two things:

  1. it tells builsystems to add calls to moc, whose job is to generate some extra code for the class. This code will provide all the facilities listed above;
  2. since it's a normal C++ macro, when compiled it expands to a few declarations, including a couple of virtuals: qt_metacall() and metaObject(). moc will generate the implementation for these virtuals.

The error you get is the typical symptom of having declared the virtuals (because the macro was expanded in your code) but moc wasn't run, you had some unimplemented virtuals which will make the linking fail.

With gcc and GNU ld, you get an even more cryptic error, about an undefined reference to vtable for ClassName. Of course, googling such errors will immediately tell you how to solve the issue.

EDIT 2: I see moc operates on header files only. Could this be related to the fact that my QObject derived class is declared & defined in a .cpp file?

So, the question is: why wasn't moc run on a file containing a definition of a class with the Q_OBJECT macro?

When we use qmake to generate your Makefiles, then qmake will scan all the header files listed in the HEADERS variable. When it finds that a header contains a class definition with the Q_OBJECT macro, it will also emit instructions (in the Makefile) to run moc over that header, compile moc's output and link the resulting object in the final target.

And we have rules #2, #3, #4 right here.

#2 tells us to put Q_OBJECT classes in headers; and that's because HEADERS lists headers, not sources.

#3 tells us to indeed put all headers into the HEADERS list. That obviously because if a header containing a Q_OBJECT is not in that list, then qmake won't find it and emit the rules. (While not strictly necessary for headers not containing QObject subclasses, it's good practice to put every header in there in order to forget none.)

#4 tells us to re-run qmake every time we add Q_OBJECT or modify the .pro file. The reason for the first part of the rule is that if qmake already scanned a header and found no Q_OBJECT, then it didn't emit any rules in the Makefile. But adding Q_OBJECT also needs those rules; hence we need qmake to re-scan the headers, and that's precisely what re-running qmake does.

The same reason applies when the .pro is modified (for instance, when adding more headers -- maybe with Q_OBJECT under HEADERS).

Note that if you're using a GNU-like make, then qmake will emit a special rule that tells make to re-run qmake and then restart the Makefile, if the .pro gets modified after the Makefile. That's why usually on UNIX you don't need to manually re-run qmake when you modify your .pro -- just running make will also run qmake again. But this doesn't work everywhere.


So, is it impossible to have classes definition containing Q_OBJECT in a .cpp file?

No, it's perfectly possible, but it requires using a somehow undocumented qmake feature. The trick is adding a line like:

#include "foobar.moc"

at the end of the foobar.cpp file, file that contains one of more class definitions with Q_OBJECT.

qmake will find this special inclusion and generate a rule for moc to create foobar.moc, which will then be included by the .cpp and thus compiled together with it. (Hence, there will be no extra rules for compiling foobar.moc nor to link the result.)



回答2:

EDIT 1: The error goes away if I comment out Q_OBJECT from the declation of MSFSPlugin::MSFSPluginImpl, but then I lose the signal functionality.

Yes, the Q_OBJECT macro is necessary for signals, slots, properties and so on.

EDIT 2: I see moc operates on header files only. Could this be related to the fact that my QObject derived class is declared & defined in a .cpp file?

Yes and no. I will explain..

Usually, it is solved by separation as you write, but it is also possible to include the moc file after your class definition to get it work, but you need to remember not to put more than one in there to avoid strange consequences.

Therefore, in your case, you could establish a MSFSPlugin_p.h or MSFSPluginImpl.h file for the implementation header.

By the way, it is a bad idea to make pimpl protected. The pimpl idiom means private implementation, not protected.



标签: qt clang