Virtual functions causing link errors

1.4k views Asked by At

I am struggling with an issue while I am using external .h files in my project.

I am using Qt5 with MSVC2012. In my main file, I am including a header file from a KDE application. I put some INCLUDEPATH in the .pro file to link correctly.

The piece of code I am in trouble with is the following:

main.cpp
...
#include <Doc.h>

int main(int argc, char *argv[])
...

The Doc.h belongs to the KDE application source code. Including that file includes many other files in several directories in the KDE application source code.

Currently, the KUndo2Stack.h and the KUndo2Stack.cpp files are the files I am struggling with since three days now.

When I run QMake and compile, I get a link 2001 errors due to unresolved externals with virtual functions in the KUndo2QStack class defined and implemented in those files. Here is an extract:

#ifndef QT_NO_UNDOSTACK

class KUNDO2_EXPORT KUndo2QStack : public QObject
{
    Q_OBJECT
//    Q_DECLARE_PRIVATE(KUndo2QStack)
    Q_PROPERTY(bool active READ isActive WRITE setActive)
    Q_PROPERTY(int undoLimit READ undoLimit WRITE setUndoLimit)

public:
    explicit KUndo2QStack(QObject *parent = 0);
    virtual ~KUndo2QStack();
    void clear();

    void push(KUndo2Command *cmd);

    bool canUndo() const;
    bool canRedo() const;
    QString undoText() const;
    QString redoText() const;

    int count() const;
    int index() const;
    QString actionText(int idx) const;
    QString text(int idx) const;

#ifndef QT_NO_ACTION
    QAction *createUndoAction(QObject *parent) const;
    QAction *createRedoAction(QObject *parent) const;
#endif // QT_NO_ACTION

    bool isActive() const;
    bool isClean() const;
    int cleanIndex() const;

    void beginMacro(const QString &text);
    void endMacro();

    void setUndoLimit(int limit);
    int undoLimit() const;

    const KUndo2Command *command(int index) const;

public Q_SLOTS:
    void setClean();
    virtual void setIndex(int idx);
    virtual void undo();
    virtual void redo();
    void setActive(bool active = true);

Q_SIGNALS:
    void indexChanged(int idx);
    void cleanChanged(bool clean);
    void canUndoChanged(bool canUndo);
    void canRedoChanged(bool canRedo);
    void undoTextChanged(const QString &undoActionText);
    void redoTextChanged(const QString &redoActionText);

private:
    // from QUndoStackPrivate
    QList<KUndo2Command*> m_command_list;
    QList<KUndo2Command*> m_macro_stack;
    int m_index;
    int m_clean_index;
    KUndo2Group *m_group;
    int m_undo_limit;

    // also from QUndoStackPrivate
    void setIndex(int idx, bool clean);
    bool checkUndoLimit();

    Q_DISABLE_COPY(KUndo2QStack)
    friend class KUndo2Group;
};

class KUNDO2_EXPORT KUndo2Stack : public KUndo2QStack
{
public:
    explicit KUndo2Stack(QObject *parent = 0);

    // functions from KUndoStack
    QAction* createRedoAction(KActionCollection* actionCollection, const QString& actionName = QString());
    QAction* createUndoAction(KActionCollection* actionCollection, const QString& actionName = QString());
};

#endif // QT_NO_UNDOSTACK

Note that the unresolved links occur only for virtual functions. Those functions are implemented as it follows:

KUndo2QStack::~KUndo2QStack()
{
#ifndef QT_NO_UNDOGROUP
    if (m_group != 0)
        m_group->removeStack(this);
#endif
    clear();
}

void KUndo2QStack::undo()
{
    if (m_index == 0)
        return;

    if (!m_macro_stack.isEmpty()) {
        qWarning("KUndo2QStack::undo(): cannot undo in the middle of a macro");
        return;
    }

    int idx = m_index - 1;
    m_command_list.at(idx)->undo();
    setIndex(idx, false);
}

void KUndo2QStack::redo()
{
    if (m_index == m_command_list.size())
        return;

    if (!m_macro_stack.isEmpty()) {
        qWarning("KUndo2QStack::redo(): cannot redo in the middle of a macro");
        return;
    }

    m_command_list.at(m_index)->redo();
    setIndex(m_index + 1, false);
}

void KUndo2QStack::setIndex(int idx)
{
    if (!m_macro_stack.isEmpty()) {
        qWarning("KUndo2QStack::setIndex(): cannot set index in the middle of a macro");
        return;
    }

    if (idx < 0)
        idx = 0;
    else if (idx > m_command_list.size())
        idx = m_command_list.size();

    int i = m_index;
    while (i < idx)
        m_command_list.at(i++)->redo();
    while (i > idx)
        m_command_list.at(--i)->undo();

    setIndex(idx, false);
}

I have turned this question upside down looking for answers on the web and the only time I got something was when I deleted the word virtual off all the related functions. It cancelled the errors but I got an unresolved link2019 error with the destructor of the class KUndo2Stack (see the code above). The destructor I am talking about is the default one, you won't see any definition or implementation of it.

I need some help to understand what is really going on and how to fix it.

Thanks!

Update

The exact error messages are:

main.obj:-1: error: LNK2001: unresolved external symbol "public: virtual struct QMetaObject const * __cdecl KUndo2QStack::metaObject(void)const " (?metaObject@KUndo2QStack@@UEBAPEBUQMetaObject@@XZ)

main.obj:-1: error: LNK2001: unresolved external symbol "public: virtual void * __cdecl KUndo2QStack::qt_metacast(char const *)" (?qt_metacast@KUndo2QStack@@UEAAPEAXPEBD@Z)

main.obj:-1: error: LNK2001: unresolved external symbol "public: virtual int __cdecl KUndo2QStack::qt_metacall(enum QMetaObject::Call,int,void * *)" (?qt_metacall@KUndo2QStack@@UEAAHW4Call@QMetaObject@@HPEAPEAX@Z)

main.obj:-1: error: LNK2001: unresolved external symbol "public: virtual void __cdecl KUndo2QStack::setIndex(int)" (?setIndex@KUndo2QStack@@UEAAXH@Z)

main.obj:-1: error: LNK2001: unresolved external symbol "public: virtual void __cdecl KUndo2QStack::undo(void)" (?undo@KUndo2QStack@@UEAAXXZ)

main.obj:-1: error: LNK2001: unresolved external symbol "public: virtual void __cdecl KUndo2QStack::redo(void)" (?redo@KUndo2QStack@@UEAAXXZ)

main.obj:-1: error: LNK2019: unresolved external symbol "__declspec(dllimport) public: virtual __cdecl KUndo2Stack::~KUndo2Stack(void)" (__imp_??1KUndo2Stack@@UEAA@XZ) referenced in function "public: virtual void * __cdecl KUndo2Stack::`scalar deleting destructor'(unsigned int)" (??_GKUndo2Stack@@UEAAPEAXI@Z)

debug\whitehall_0_2.exe:-1: error: LNK1120: 7 unresolved externals

Update

After I added Q_OBJECT into the KUndo2Stack class, the link errors related to the qmetaobject of KUndo2QStack disappeared. The same errors appeared related to the KUndo2Stack class this time

main.obj:-1: error: LNK2001: unresolved external symbol "public: virtual struct QMetaObject const * __cdecl KUndo2Stack::metaObject(void)const " (?metaObject@KUndo2Stack@@UEBAPEBUQMetaObject@@XZ)

main.obj:-1: error: LNK2001: unresolved external symbol "public: virtual void * __cdecl KUndo2Stack::qt_metacast(char const *)" (?qt_metacast@KUndo2Stack@@UEAAPEAXPEBD@Z)

main.obj:-1: error: LNK2001: unresolved external symbol "public: virtual int __cdecl KUndo2Stack::qt_metacall(enum QMetaObject::Call,int,void * *)" (?qt_metacall@KUndo2Stack@@UEAAHW4Call@QMetaObject@@HPEAPEAX@Z)

The other errors stayed the same.

0

There are 0 answers