Qt creator: c++: undefined reference to Class::Function

41,390

Solution 1

Your unit tests will need to compile the classes in your project that you want to unit-test. So you need to add the include in both project (otherwise the test project will not know the classes you are trying to test). And the linker needs to link to the project's code as well, as your tests will use the classes.

One way is to add the classes you want to test in your test project as well and compile them again when you compile your unit test project but this is tedious and not really handy as every time you want to add a class, you need to add it to both .pro files (hint, you can use wildcards in the .pro files, like *.cpp to add all source files in a folder to a project).

A better approach in my opinion is to setup the project you want to test as a static library, separating it from the application: you have another project which is an application, containing only the main.cpp linking to that static library.

Here is a representation of the folder containing the project:

Project.pro #subdir project
UIProject/ #static lib
    UIProject.pro
    #all your classes here
MainProject/ #application
    MainProject.pro
    main.cpp
UITestProject/ #unit tests of UIProject (linking to it)
    UITestProject.pro
    #all your UI test classes here

Project.pro:

TEMPLATE = subdirs
SUBDIRS += UIProject
SUBDIRS += MainProject
SUBDIRS += UITestProject

UIProject.pro:

# project type
TEMPLATE = lib

# library type
CONFIG += staticlib

HEADERS += *.h
SOURCES += *.cpp

MainProject.pro:

#[...]
TEMPLATE = app
SOURCES += main.cpp
INCLUDEPATH += ../UIProject/
DEPENDPATH += $${INCLUDEPATH} # force rebuild if the headers change

# link against UILib
_UI_LIB = ../UIProject/
CONFIG(debug, debug|release) {
    win32: _UI_LIB = $$join(_UI_LIB,,,debug/UIProject.lib)
} else {
    win32: _UI_LIB = $$join(_UI_LIB,,,release/UIProject.lib)
}
LIBS += $${_UI_LIB}
PRE_TARGETDEPS += $${_UI_LIB}

UITestProject.pro:

#[...]
TEMPLATE = app
HEADERS += *.h
SOURCES += *.cpp

INCLUDEPATH += ../UIProject/
DEPENDPATH += $${INCLUDEPATH} # force rebuild if the headers change

# link against UILib
_UI_LIB = ../UIProject/
CONFIG(debug, debug|release) {
    win32: _UI_LIB = $$join(_UI_LIB,,,debug/UIProject.lib)
} else {
    win32: _UI_LIB = $$join(_UI_LIB,,,release/UIProject.lib)
}
LIBS += $${_UI_LIB}
PRE_TARGETDEPS += $${_UI_LIB}

You would have to edit that to match your project but the main things are here. It should work as I copied it from one of my project, providing I didn't add any errors.

Solution 2

you should include your cpp file to the project to tell build system (qmake) to compile the given file.

When you add myClass.cpp to test project, it gets compiled, otherwise does not.

So, when you will not add cpp file to the project, you'll get linker error.

Includes are just handled separately

When you add new path to INCLUDEPATH, compiler will add that pass to headers search list

When you add applicationProjectPath to the include path, compiler will search headers there. It is important, as allows compiler to find myClass.h

If you will not add path to headers search list, compiler will not be able to compile your main.cpp and you'll get compilation error.

You, however, can just specify (relative) path to your myClass.h in your test-project's main:

#include "../applicationProjectPath/myClass.h"

Summing up

You need add your source files to test project and add your headers path to test project's include search list.

Further reading

What is an undefined reference/unresolved external symbol error and how do I fix it?

Share:
41,390
hashDefine
Author by

hashDefine

Updated on July 09, 2022

Comments

  • hashDefine
    hashDefine over 1 year

    I am creating two c++ projects in my Qt creator. The first is an Application project and the other is unit-test project. the two projects, separately, work fine. However, when linking the two together I face a little problem.

    I am including #INCLUDEPATH applicationProjectPath in .pro file in the unit-test project. then #include myClass from the application project in the main.cpp of the unit-test project. then, creating a myObject from myClass and calling a function within that object.

    when compiling, this error appears:

    undefined reference to `myObject::function' 
    

    however, when adding #SOURCES applicationProjectPath/myClass.cpp to the .pro file of the unit-test project (while keeping the #INCLUDEPATH applicationProjectPath), everything is working (i.e.: the test units are executed)

    again when removing the #INCLUDEPATH from the .pro, it again crashes .

    I thought if I included #SOURCES, then I don't need to include the #INCLUDEPATH. and if I included the #INCLUDEPATH, I should not include #SOURCES (at least not with the full path, just the .cpp file and then the compiler should look for both directories, the default one and the added one).

    so, my question is: why is this happening

  • hashDefine
    hashDefine over 10 years
    >A better approach in my opinion is to setup the project you want to test as a static library. this exactly what I am looking for as an answer. so what you mean is: add my Application-project to the unit-test-project as a library instead of adding its path and source/header files. but my problem now is how to add my Application-project as a library (sorry, could not understand your code) .. but thank you very much for your explanation.
  • hashDefine
    hashDefine over 10 years
    thanks for your answer .. and thank you for the link as it gives me what I want to understand the compiling process in more details ..
  • Uflex
    Uflex over 10 years
    It wasn't code but the representation of the folders on disk, with some comments starting with #. I'll try editing my post to be more understandable
  • Uflex
    Uflex over 10 years
    If you need more explanations just tell me, I'll add some more stuff
  • hashDefine
    hashDefine over 10 years
    Thanks .. you mentioned that: (And the linker needs to link to the project's code as well, as your tests will use the classes.) by this you mean include both (path) and (classes) of the (unit-test project) in the (.pro file) of the (Application-project). But since I am already specifying the (Application project classes) I need to test in my (uni-test project) in its (.pro file) and the unit-test is working, do you really think adding the (unit-test project) to the (.pro file) of the (Application project) is necessary? (having asked that, I am still going to add the library)
  • Uflex
    Uflex over 10 years
    I'm not sure to understand what you mean but linking to the library will resolve that, without having to add anything else. All the objects needed will be contained in the library. If you use the code I wrote in my answer above (starting with "A better approach"), you will have a global project file, "Project.pro", containing no source code, only telling qmake to compile the subdirectories. UIProject will be a static library, used by both your MainProject AND UITestProject. Those last two projects are completely separated, the only thing in common in that they both use UIProject.
  • Uflex
    Uflex over 10 years
    I think I'll write a blog post this week end and explain all this a bit better. All that is not really well documented anywhere and can be a bit hard to understand at first.
  • DBedrenko
    DBedrenko over 7 years
    @Uflex Thanks for this answer, it helped me finally get tests set up correctly after trying for literally 7 hours. Did you end up writing the blog post? Could you link your blog please? I can't see it in your profile.
  • A. Vieira
    A. Vieira over 5 years
    I see. So, adding applicationProjectPath to INCLUDEPATH makes the compiler aware of headers. Then you also need to add all .cpp's relevant to the project to the SOURCES variable. I.e. you don't need to add the .h's to HEADERS.