Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Undefined reference in abstract class implementation



  • Hello,

    I am working on a Qt Subdirs project and am running into a new issue when attempting to compile the completed project. My subdirs project consists of a main application and various static libraries which can depend on each other. Everything has been working fine, but some code change I made in an abstract class is now preventing the linker from building the complete application.

    The issue is occurring between an abstract class in one library being inherited by a class in another library. Each library compiles successfully, however, when attempting to link them in the final project, I receive the following errors

    "undefined reference to 'AbstractCametra::AbstractCamera()" in sonyviscacamera.cpp
    "undefined reference to 'AbstractCametra::~AbstractCamera()" in sonycamera.cpp
    "undefined reference to 'AbstractCametra::setPort(AbstractPort *)" in sonyviscacamera.cpp
    

    Here are the pertinent files (stripped for brevity):

    abstractcamera.h

    struct CameraPosition {
        CameraPosition(qint8 pan, qint8 tilit) : pan(pan), tilt(tilt) {}
        qint8 pan;
        qint8 tilt;
    };
    
    class AbstractCamera {
    protected:
        AbstractPort *port_;
    
    public:
        AbstractCamera();
        virtual ~AbstractCamera();
    
        AbstractPort *port() const;
        virtual void setPort(AbstractPort *port);
    
        //Position, Zoom, Focus
    
        virtual AbstractCameraInfo *cameraInfo() const = 0;
    
        virtual CameraPosition currentPosition() = 0;
        virtual void setPosition(CameraPosition pos) = 0;
        virtual void positionChanged() = 0;
       
    //...
    };
    

    abstractcamera.cpp

    #include "abstractcamera.h"
    
    
    AbstractCamera::AbstractCamera() :
        port_(nullptr)
    {
    
    }
    
    AbstractCamera::~AbstractCamera()
    {
    
    }
    
    AbstractPort *AbstractCamera::port() const
    {
        return port_;
    }
    
    void AbstractCamera::setPort(AbstractPort *port)
    {
        port_ = port;
    }
    
    

    sonyviscacamera.h

    #include "abstractcamera.h"
    
    class SonyViscaCamera : public QObject, public AbstractCamera
    {
        Q_OBJECT
    
    private:
        qint8 currentPan_;
        qint8 currentTilt_;
        quint8 currentZoom_;
        quint8 currentFocus_;
    
    public:
        explicit SonyViscaCamera(SonyViscaCameraInfo *info, QObject *parent = nullptr);
        ~SonyViscaCamera();
    
        void setPort(AbstractPort *port);
    
    signals:
        void positionChanged();
        void zoomChanged();
        void focusChanged();
    
    public slots:
        void move(qint8 panSpeed, qint8 tiltSpeed);
        void setPosition(CameraPosition pos);
        void stopMove();
    
        //...
    };
    

    I noticed that the three errors are all on member functions declared as virtual (but not pure virtual). I was able to sucessfully build the project by providing implementations of AbstractCamera() virtual ~AbstractCamera() and *virtual void setPort(AbstractPort port) directly within abstractcamera.h and not in abstractcamera.cpp. To me it seems as if the compiler/linker are ignoring the .cpp file?

    To the best of my knowledge, this all started happening after I decided to allow setPort() to be overridden by declaring it virtual.

    Any help would be greatly appreciated!


  • Qt Champions 2019

    Did you really add abstractcamera.cpp to the sources list so it gets compiled? I would guess it's not the case.



  • All the abstract classes are in a project called interface.pro and my camera implementation is in a project called sonyviscacamera.pro. The contents (stripped for brevity) are as follows:

    interface.pro

    SOURCES += \
        abstractcamera.cpp \
       ...
    HEADERS += \
        abstractcamera.h \
        ...
    

    sonyviscacamera.pro

    SOURCES += \
        sonyviscacamera.cpp \
        ...
    HEADERS += \
        sonyviscacamera.h \
        ...
    INCLUDEPATH += $$PWD/../interface
    DEPENDPATH += $$PWD/../interface
    LIBS += -L$$OUT_PWD/../interface/release/ -linterface
    

    Most of this was generated automatically by Qt Creator. I believe that is correct is it not?


  • Qt Champions 2019

    But then you have to link against interface lib in sonyviscacamera.pro since the interface lib containst the implementation of you functions. If you won't create a separate interface lib you should make the functions inline as you did it in your test.



  • Could you clarify what you mean? I thought the LIBS line in sonyviscacamera.pro was what told the compiler to link against the .a library file that was created by interface.pro. Is it bad practice to link against a library when defining abstract classes/interfaces?


  • Qt Champions 2019

    Ah, I did not saw '-linterface' - not using qmake that often... are you sure you're creating a static interface lib?



  • Yep! If I navigate to my build directory I have libinterface.a under /interface/debug/ and libsonyviscacamera.a under /interface/debug. I would assume that the sonyviscacamera library is able to link against the interface project successfully because it builds the library with no problems (but I don't fully understand how the linker works). Only when I try to build the complete project does this issue occur.

    That's why this is such a confusing issue. And, as I said, it was working just fine until I made a small change to the structure of the interface...


  • Qt Champions 2019

    @Patrick-Wright said in Undefined reference in abstract class implementation:

    LIBS += -L$$OUT_PWD/../interface/release/ -linterface

    You're using the release interface lib here, not the debug one.



  • I almost thought that might have been the solution; a dumb mistake! But it may actually just be a typo since the .pro contents I sent were shortened. This is the actual, complete sonyviscacamera.pro file:

    QT       -= gui
    QT += widgets
    
    TARGET = sonyviscacamera
    TEMPLATE = lib
    CONFIG += staticlib
    
    DEFINES += QT_DEPRECATED_WARNINGS
    
    SOURCES += \
            sonyviscacamera.cpp \
        sonyviscacamerainfo.cpp \
        sonyviscacamerainfowidget.cpp
    HEADERS += \
            sonyviscacamera.h \
        sonyviscacamerainfo.h \
        sonyviscacamerainfowidget.h
    
    win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../interface/release/ -linterface
    else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../interface/debug/ -linterface
    else:unix: LIBS += -L$$OUT_PWD/../interface/ -linterface
    
    INCLUDEPATH += $$PWD/../interface
    DEPENDPATH += $$PWD/../interface
    
    win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../interface/release/libinterface.a
    else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../interface/debug/libinterface.a
    else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../interface/release/interface.lib
    else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../interface/debug/interface.lib
    else:unix: PRE_TARGETDEPS += $$OUT_PWD/../interface/libinterface.a
    
    FORMS += \
        sonyviscacamerainfowidget.ui
    

    I think the if/else statement correctly chooses the debug or release version.


  • Qt Champions 2019

    I'm not very familar with qmake (mostly because I don't want to take care for such stuff like here - the buildsystem must know the correct paths). What you can do is to remove the release interface.a file to see if you get a linker error. Or you can take a look at the linker line to see if it pick up the correct interface.a file.



  • I checked the link command line and it is using the correct library paths.

    I am looking more closely at what I am trying to do and am wondering if this is do to the fact that SonyViscaCamera is both a QObject and an AbstractCamera. Maybe someone could explain in more detail how multiple inheritance would work in this case. i want to inherit QObject because my camera is going to make use of signals and slots. I also inherit my interface because that is how I define the standard interface for a camera. What I am wondering is, would it be better to make AbstractCamera inherit from QObject then have SonyViscaCamera inherit from Abstract Camera?

    I ask because I flipped the order of inheritance as such

    class SonyViscaCamera : public AbstractCamera, public QObject
    

    and now I get the following (different) error:

    "'staticmetaObject' is not a member of 'AbstractCamera'"
    


  • Also, the whole problem is solved if I simply make void setPort(AbstractPort *port) in AbstractCamera NOT virtual. This leads me to believe there is definately something wrong with my understanding of multiple inheritance in this case... I know what I want to accomplish, but now am unsure how to go about that. I made setPort() virtual so that sub classes could override it if they needed to.


  • Lifetime Qt Champion

    Hi,

    What code change did trigger these errors ?



  • I made AbstractCamera abstract because I know that every camera will contain a reference to an AbstractPort, so rather than have every subclass implement the same code, I added that as a member function (and variable) to AbstractCamera. The rest of the methods are pure virtual because every subclass will have an independent implementation. The change which caused the error was when I made setPort in AbstractCamera virtual so that sub classes could override this method if needed.

    I am wondering now if it would be more intelligent to just make every method pure virtual and have the sub class implement the setPort method as well... Would that be the best option?


  • Qt Champions 2017

    @Patrick-Wright said in Undefined reference in abstract class implementation:

    I ask because I flipped the order of inheritance as such

    Not allowed. QObject must always go first due to moc not being smart enough.

    My advice: Inspect the symbols in the interface library and make sure everything that is supposed to be there is there. I imagine there was some problem with it if the linker's complaining (the linker is a rather simple program).

    I am wondering now if it would be more intelligent to just make every method pure virtual and have the sub class implement the setPort method as well... Would that be the best option?

    That is up to you. Should work either way, though.



  • @Christian-Ehrlicher This is the solution. Thank you.


Log in to reply