I'm trying to start a project where I need to implement some genetic algorithms. I am just starting out and trying to figure out a good project structure and how to make the builds easier with cmake. I am also trying to test things as I go along, so I took inpiration from this repository https://github.com/kigster/cmake-project-template.
My problem is that I cannot seem to link a class that I already wrote (genetic_solution
) to the tests (I'm using gtest
).
I now have the following project structure:
├── bin
├── build
├── CMakeLists.txt
├── include
├── lib
├── src
│ ├── CMakeLists.txt
│ ├── genetic
│ │ ├── CMakeLists.txt
│ │ ├── genetic_solution.cpp
│ │ └── genetic_solution.hpp
│ └── main.cpp
└── tests
├── CMakeLists.txt
└── genetic
└── genetic_solution_test.cpp
genetic_solution.cpp
and genetic_solution.hpp
define a genetic_solution
templated class. For now, I am just trying to use gtest
to test the constructer, the getters and setters. The class is very simple, I'll leave the genetic_solution.hpp
here:
#ifndef __GENETIC_SOLUTION_HPP__
#define __GENETIC_SOLUTION_HPP__
#include <vector>
#include <iostream>
namespace genetic {
template<typename T>
class genetic_solution {
private:
float _total_cost;
float _fitness;
std::vector<float> _costs;
T _item;
public:
genetic_solution(const T item);
//getters
float total_cost() const;
float fitness() const;
const std::vector<float>& costs() const;
const T& item() const;
//setters
void total_cost(float new_total);
void fitness(float new_fitness);
void costs(const std::vector<float>& new_costs);
void item(const T& new_item);
virtual void dump_to(std::ostream& os) const = 0;
};
template<typename T>
std::ostream& operator<<(std::ostream& os, const genetic_solution<T>& a);
} // namespace genetic
#endif
My genetic_solution_test.cpp
looks like this:
#include <genetic_solution.hpp>
#include <gtest/gtest.h>
class numeric_solution: public genetic::genetic_solution<int> {
public:
numeric_solution(int item): genetic::genetic_solution<int>(item) {
}
void dump_to(std::ostream& os) const override {
os << "Solution: " << item();
}
};
class genetic_solution_test: public testing::Test {
protected:
numeric_solution _solution = numeric_solution(10);
};
TEST_F(genetic_solution_test, genetic_solution_constructor_test) {
ASSERT_FLOAT_EQ(_solution.total_cost(), -1);
ASSERT_FLOAT_EQ(_solution.fitness(), -1);
ASSERT_EQ(_solution.item(), 10);
ASSERT_EQ(_solution.costs(), std::vector<float>());
}
The ./CMakeLists.txt
file looks like this:
cmake_minimum_required(VERSION 3.16.3)
project(bus_network_optimization LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O3")
set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR})
set(GENETIC_INSTALL_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include)
set(GENETIC_INSTALL_BIN_DIR ${PROJECT_SOURCE_DIR}/bin)
set(GENETIC_INSTALL_LIB_DIR ${PROJECT_SOURCE_DIR}/lib)
set(GENETIC_HEADERS_DIR ${PROJECT_SOURCE_DIR}/src/genetic)
include_directories(${GENETIC_INSTALL_INCLUDE_DIR})
include_directories(${GENETIC_HEADERS_DIR})
add_subdirectory(src)
add_subdirectory(tests)
The ./src/CMakeLists.txt
file looks like this:
cmake_minimum_required(VERSION 3.16.3)
project(main LANGUAGES CXX)
add_subdirectory(genetic)
add_executable(main main.cpp)
target_link_libraries(main genetic)
install(TARGETS main DESTINATION ${GENETIC_INSTALL_BIN_DIR})
The ./src/genetic/CMakeLists.txt
file looks like this:
cmake_minimum_required(VERSION 3.16.3)
project(genetic LANGUAGES CXX)
add_library(genetic SHARED STATIC
genetic_solution.hpp
genetic_solution.cpp)
install(TARGETS genetic DESTINATION ${GENETIC_INSTALL_LIB_DIR})
install(FILES genetic_solution.hpp DESTINATION ${GENETIC_INSTALL_INCLUDE_DIR})
And finally, ./tests/CMakeLists.txt
looks like this:
cmake_minimum_required(VERSION 3.16.3)
project(main_tests LANGUAGES C CXX)
include_directories(${GENETIC_HEADERS_DIR})
find_package(GTest REQUIRED)
add_executable(main_tests genetic/genetic_solution_test.cpp)
target_link_libraries(main_tests genetic GTest::Main)
install(TARGETS main_tests DESTINATION bin)
If I go to the ./build
directory and run: cmake .. && make && make install
I get the following output:
-- The CXX compiler identification is GNU 9.3.0
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- The C compiler identification is GNU 9.3.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Found GTest: /usr/lib/x86_64-linux-gnu/libgtest.a
-- Configuring done
-- Generating done
-- Build files have been written to: /home/vasco/Documents/Thesis/bus_network_optimization/build
Scanning dependencies of target genetic
[ 16%] Building CXX object src/genetic/CMakeFiles/genetic.dir/genetic_solution.cpp.o
[ 33%] Linking CXX static library libgenetic.a
[ 33%] Built target genetic
Scanning dependencies of target main
[ 50%] Building CXX object src/CMakeFiles/main.dir/main.cpp.o
[ 66%] Linking CXX executable main
[ 66%] Built target main
Scanning dependencies of target main_tests
[ 83%] Building CXX object tests/CMakeFiles/main_tests.dir/genetic/genetic_solution_test.cpp.o
[100%] Linking CXX executable main_tests
/usr/bin/ld: CMakeFiles/main_tests.dir/genetic/genetic_solution_test.cpp.o: in function `genetic_solution_test_genetic_solution_constructor_test_Test::TestBody()':
genetic_solution_test.cpp:(.text+0x33): undefined reference to `genetic::genetic_solution<int>::total_cost() const'
/usr/bin/ld: genetic_solution_test.cpp:(.text+0x88): undefined reference to `genetic::genetic_solution<int>::fitness() const'
/usr/bin/ld: genetic_solution_test.cpp:(.text+0xe5): undefined reference to `genetic::genetic_solution<int>::item() const'
/usr/bin/ld: genetic_solution_test.cpp:(.text+0x1a8): undefined reference to `genetic::genetic_solution<int>::costs() const'
/usr/bin/ld: CMakeFiles/main_tests.dir/genetic/genetic_solution_test.cpp.o: in function `numeric_solution::dump_to(std::ostream&) const':
genetic_solution_test.cpp:(.text._ZNK16numeric_solution7dump_toERSo[_ZNK16numeric_solution7dump_toERSo]+0x29): undefined reference to `genetic::genetic_solution<int>::item() const'
/usr/bin/ld: CMakeFiles/main_tests.dir/genetic/genetic_solution_test.cpp.o: in function `testing::internal::TestFactoryImpl<genetic_solution_test_genetic_solution_constructor_test_Test>::CreateTest()':
genetic_solution_test.cpp:(.text._ZN7testing8internal15TestFactoryImplI60genetic_solution_test_genetic_solution_constructor_test_TestE10CreateTestEv[_ZN7testing8internal15TestFactoryImplI60genetic_solution_test_genetic_solution_constructor_test_TestE10CreateTestEv]+0x36): undefined reference to `genetic::genetic_solution<int>::genetic_solution(int)'
collect2: error: ld returned 1 exit status
make[2]: *** [tests/CMakeFiles/main_tests.dir/build.make:87: tests/main_tests] Error 1
make[1]: *** [CMakeFiles/Makefile2:189: tests/CMakeFiles/main_tests.dir/all] Error 2
make: *** [Makefile:130: all] Error 2
I am using Ubuntu 20.04
, cmake 3.16.3
and I got gtest
installed with the package libgtest-dev
.
I already read several answers to related questions but i could not find the problem. I would really appreciate some help solving this. Can I also ask for tips on testing templated classes?
Thank you very much!