Question

This code compiles, links and works as intended:

#include <QApplication>
#include <QListView>
#include "File_List_Model.h"

int main(int c,char**v)
{
    QApplication app(c,v);
    QStringList list;

    list << "a" << "b" << "c";

    File_List_Model* model = new File_List_Model;
    model->set_entries(list);

    QListView* view = new QListView;
    view->setModel(model);
    view->show();

    return app.exec();
} 

But when I put the class definition in .cpp file instead of header files, I get linker errors stating that vtable was not properly defined.

#include <QApplication>
#include <QListView>
//#include "File_List_Model.h"
#include "File_List_Proxy.h"
#include <QAbstractItemModel>
#include <QStringList>

class File_List_Model : public QAbstractItemModel
{
    Q_OBJECT
private:
    QStringList data_;
public:
    File_List_Model(QObject *parent = 0) :
        QAbstractItemModel(parent)
    {
    }

    int columnCount(const QModelIndex& parent) const
    {
        return 1;
    }

    QVariant data(const QModelIndex & index, int role) const
    {
        switch(role)
        {
            case Qt::DisplayRole:
            return data_[index.row()];
        default:
            return QVariant();
        }
    }

     QModelIndex index(int row, int column, const QModelIndex & parent) const
    {
        return createIndex(row,column);
    }

    QModelIndex parent(const QModelIndex & index) const
    {
        return QModelIndex();
    }

     bool set_entries(const QStringList& entries)
     {
         if (entries.size())
         {
         beginInsertRows(createIndex(0,0),0,entries.size());
         data_ = entries;
         endInsertRows();
         emit dataChanged(createIndex(0,0),createIndex(0,entries.size()));
         return true;
         }
         else
         {
             return false;
         }
     }

     int rowCount(const QModelIndex & parent) const
     {
         return data_.size();
     }


};

int main(int c,char**v)
{
    QApplication app(c,v);
    QStringList list;

    list << "a" << "b" << "c";

    File_List_Model* model = new File_List_Model;
    model->set_entries(list);

    File_List_Proxy* proxy = new File_List_Proxy;
    proxy->setSourceModel(model);

    QListView* view = new QListView;
    view->setModel(proxy);
    view->show();

    return app.exec();
}
//error:   
debug/moc_File_List_Model.o:moc_File_List_Model.cpp:(.rdata$_ZTV15File_List_Model[vtable for File_List_Model]+0x44): undefined reference to `File_List_Model::columnCount(QModelIndex const&) const'
debug/moc_File_List_Model.o:moc_File_List_Model.cpp:(.rdata$_ZTV15File_List_Model[vtable for File_List_Model]+0x4c): undefined reference to `File_List_Model::data(QModelIndex const&, int) const'

This seems to be exactly the same code. Why does it link when the code is in headers and it doesn't link otherwise?

Was it helpful?

Solution

Qt uses moc tool to handle C++ extensions that a required, for example, for signals-slots mechanism. This tool processes all header (!) files in the project and generates new source files that contain meta-object code for those classes that contain Q_OBJECT macro.

When you have your class defined in .cpp file instead of .h file moc fails to process it properly.

Have a look at this article for more information about Qt Meta-Object Compiler.

OTHER TIPS

Because Qt runs moc on header files and don't run it on sources.

The linker is complaining about missing the object code that comes from compiling moc output. That's because although moc has processed the source file, its output was not compiled into an object file.

For header files, the build system assumes that they are meant for inclusion in multiple translation units and won't violate the one definition rule. Thus moc output can include the header file, and get compiled as a stand-alone translation unit.

But, if you have any Q_OBJECT macros within a .cpp file, the moc output cannot be compiled in isolation: it won't have access to the declarations from your .cpp file and thus can't compile! It also can't include your .cpp file, since that would violate the one definition rule: two translation units - moc output and your .cpp file - would define the same stuff.

Instead, you need to append moc's output to the end of the .cpp file. E.g., if you have O_OBJECT in main.cpp, add #include "main.moc" at the end of the file:

// main.cpp
#include <QtCore>

struct Object : QObject {
  Q_OBJECT
};

int main() {
  Object o;
  qDebug() << o.metaObject()->className();
}

#include "main.moc"
// "main.moc" depends on the declaration of Object above!

The above is an SSCCE.

One could argue that perhaps qmake/cmake should set up the build so that moc output is automatically appended to the .cpp file before it's sent to the compiler. So far, that feature is not implemented.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top