I am attempting to test a class containing a member of third party library.
I have created a fake library to mimic the aforementioned, and am using preprocessor directives to chose between the "real" and fake libraries at compile time - real for the production compilation unit and fake for the test.
The problem is that I see some discrepancies between what comes out of the preprocessor and what the linker produces.
Here's an extremely cut-down version of the setup I have:
Third-party library - lib.hpp
This is a header-only library, so all functions are inlined.
namespace lib {
class Foo {
public:
int returnSomething() const { return 42; }
};
}
Fake library
In the actual case it's fairly chunky so I've split it in header and source files:
Header - fake_lib.hpp
namespace fake {
class FooBar {
public:
int returnSomething() const;
};
}
Source - fake_lib.cpp
#include "fake_lib.hpp"
namespace fake {
int FooBar::returnSomething() const {
return 13;
}
}
Class under test - bar.hpp and bar.cpp
#ifdef SWITCH
#include "fake_lib.hpp"
#endif
// There is no #else here for the preprocessor because in my actual use case we
// need some times from the third_party libs whether we test or are in production
#include "lib.hpp"
class Bar {
public:
int get();
private:
#ifndef SWITCH
#warning "real"
lib::Foo m_foo;
#else
#warning "fake"
fake::FooBar m_foo;
#endif
};
#include "bar.hpp"
int Bar::get() {
return m_foo.returnSomething();
}
Production exe
#include "bar.hpp"
#include <iostream>
int main() {
Bar bar;
std::cout << bar.get() << std::endl;
return 0;
}
Test exe
#include "bar.hpp"
#include <iostream>
int main() {
Bar bar;
std::cout << bar.get() << std::endl;
return 0;
}
This is all built via CMake, here's my CMakeLists.txt:
cmake_minimum_required(VERSION 3.12)
project(fake_failure LANGUAGES CXX)
add_library(lib INTERFACE)
target_include_directories(lib INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib)
add_library(fake OBJECT fake/fake_lib.cpp)
target_include_directories(fake PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/fake)
add_library(bar OBJECT bar.cpp)
target_include_directories(bar PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(bar PUBLIC lib)
add_executable(main_exe main.cpp)
target_link_libraries(main_exe PRIVATE bar)
add_executable(fake_exe fake.cpp)
target_compile_definitions(fake_exe PRIVATE SWITCH)
target_link_libraries(fake_exe PRIVATE bar fake)
Desired outcome
When main_exe
is run, it should print 42. When fake_exe
is run, it should print 13.
Actual outcome
fake_exe
prints out 42 as well.
Now, just to get this out of the way - I can fix this and get the desired outcome for now by defining a static fake_bar
library which adds the SWITCH
macro.
What I'd like to find out
When I run the preprocessor manually I see that Bar
's m_foo
member if of the fake type. I assume since the bar
library is compiled without the SWITCH
macro it uses the inline definitions, meaning that the produced object file has the inlined functions as symbols. Why doesn't the linker error out if it can't see symbols for the fake type?
And why is it that when I run gdb, break in Bar
and do print typeid(m_foo)
I see it is of the real type, even though the preprocessor output I mentioned above said otherwise.
The above would explain why the linker doesn't produce errors - it doesn't even attempt to use the fake symbols. But I can't understand why.
Thanks in advance!