Yoeri Kroon Yoeri Kroon - 1 month ago 15
C++ Question

GTest linking error: undefined reference to 'foo::function'

I want to use GTest and got the example to work. Now I want to test my own code, but I get a linking problem.

I've search on stackoverflow, and found a similar problem but the answers didn't help me to fix my problem.

My files are:

CMakeLists.txt

cmake_minimum_required(VERSION 2.6)

SET(CMAKE_CXX_FLAGS "-std=gnu++11")

# Locate GTest
find_package(GTest REQUIRED)
include_directories(${GTEST_INCLUDE_DIRS})
file(GLOB files
Datahandler.h
Datahandler.cpp
)

# Link runTests with what we want to test and the GTest and pthread library
add_executable(runTests tests.cpp ${files} )
target_link_libraries(runTests ${GTEST_LIBRARIES} pthread)


tests.cpp

#include "DataHandler.h"
#include <gtest/gtest.h>

TEST(checkFilled, All){
DataHandler handler("loc", "prot", 1);
handler.filledElement[1] = 1;
ASSERT_EQ(1, handler.checkFilled(1));
ASSERT_EQ(0, handler.checkFilled(2));
ASSERT_EQ(1, handler.checkFilled(8));
ASSERT_EQ(0, handler.checkFilled(9));
}

int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}


The errors I get are:

CMakeFiles/runTests.dir/tests.cpp.o: In function `checkFilled_All_Test::TestBody()':
tests.cpp:(.text+0x121): undefined reference to `DataHandler::DataHandler(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, unsigned char)'
tests.cpp:(.text+0x172): undefined reference to `DataHandler::checkFilled(unsigned char)'
tests.cpp:(.text+0x271): undefined reference to `DataHandler::checkFilled(unsigned char)'
tests.cpp:(.text+0x382): undefined reference to `DataHandler::checkFilled(unsigned char)'
tests.cpp:(.text+0x48d): undefined reference to `DataHandler::checkFilled(unsigned char)'
collect2: error: ld returned 1 exit status
CMakeFiles/runTests.dir/build.make:95: recipe for target 'runTests' failed
make[2]: *** [runTests] Error 1
CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/runTests.dir/all' failed
make[1]: *** [CMakeFiles/runTests.dir/all] Error 2
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2


I'm almost certain it has something to do with include or link problem, but I cant find the right syntax.

Answer

There are several points in your CMake project:

SET(CMAKE_CXX_FLAGS "-std=gnu++11")

When using CMAKE_CXX_FLAGS, it is a good practice to not erase previous value:

SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")

By default, the value is empty, so that shouldn't change anything here. Anyway, to require C++11, you may consider instead CMAKE_CXX_STANDARD:

SET(CMAKE_CXX_STANDARD 11)
SET(CMAKE_CXX_STANDARD_REQUIRED ON)

Next:

file(GLOB files
    Datahandler.h 
    Datahandler.cpp 
    )

If you use GLOB, you should pass file patterns, not file paths. Anyway, from the doc:

We do not recommend using GLOB to collect a list of source files from your source tree. If no CMakeLists.txt file changes when a source is added or removed then the generated build system cannot know when to ask CMake to regenerate.

Consider set instead:

set(files
    Datahandler.h 
    Datahandler.cpp 
    )

Next:

include_directories(${GTEST_INCLUDE_DIRS})

#...

target_link_libraries(runTests ${GTEST_LIBRARIES} pthread)

Instead of old-fashioned ${GTEST_LIBRARIES}, you may consider imported targets:

target_link_libraries(runTests GTest::Main)

GTest::Main will handle include directories through the imported target, so you don't need to call include_directories. Moreover, it will add a dependency to a thread library, so I am pretty sure you can remove pthread (to be tested). Finally, GTest::Main includes a basic implementation of the main function which does exactly what you did, so you can remove yours.

Note that you will need to upgrade the minimum required version of CMake to make imported targets fully availables:

cmake_minimum_required(VERSION 2.8.11)

So your CMake project in 2016 should look like:

cmake_minimum_required(VERSION 2.8.11)

SET(CMAKE_CXX_STANDARD 11)
SET(CMAKE_CXX_STANDARD_REQUIRED ON)

set(files
    Datahandler.h 
    Datahandler.cpp 
    )

# Locate GTest
find_package(GTest REQUIRED)

# Link runTests with what we want to test and the GTest library
add_executable(runTests tests.cpp ${files})

target_link_libraries(runTests GTest::Main)

And test.cpp:

#include "DataHandler.h"
#include <gtest/gtest.h>

TEST(checkFilled, All){
    DataHandler handler("loc", "prot", 1);
    handler.filledElement[1] = 1;
    ASSERT_EQ(1, handler.checkFilled(1));
    ASSERT_EQ(0, handler.checkFilled(2));
    ASSERT_EQ(1, handler.checkFilled(8));
    ASSERT_EQ(0, handler.checkFilled(9));
}
Comments