CMake - Undefined Reference - c++

I am trying to include gtest to my project.
The problem is that I get a undefined reference error in the GTest.
I am trying to test the Node class in Gtest. Inside the constructor of the Node I am using the Class Logger. Although I have added the library logger to the gtest-target I still the undefined reference error regarding to the Logger....
My guess CMake does no look for nested classes that are used inside Node.
only Node itself.
Temperoy fix
If I use the Logger in the gtest-node.cpp it works
gtest.cpp
/* Pseudo Code */
TEST Node
{
Logger::log("Temp Fix")
Node * n = Node(0,0,0)
}
This way the the Logger is directly used in the gtest this way the logger-library will be add to the target by cmake.
My Setup (pseudo-code because my project is way bigger than this)
(https://github.com/ronsalm/LearningLunch)
├── CMakeLists.txt
├── main.cpp
├── logger
│ ├── CMakeLists.txt
│ ├── logger.cpp
│ └── logger.h
├── Node
│ ├── CMakeLists.txt
│ ├── node.cpp
│ └── node.h
└── Gtest
├── CMakeLists.txt
├── gtest-node.cpp
└── node.h
main.cpp
/* Pseudo Code */
int main()
{
Node * n = Node(0,0,0)
}
logger.h
/* Pseudo Code */
class Logger
{
log(string)
}
logger.cpp
/* Pseudo Code */
Logger::log(string s)
{
//write to string to file
}
node.h
/* Pseudo Code */
class Node
{
Node(int,int,int)
}
node.cpp
/* Pseudo Code */
Node::node(int x, int y , int z)
{
Logger::log("Create Node")
}
gtest.cpp
/* Pseudo Code */
TEST Node
{
Node * n = Node(0,0,0)
}
CMakeLists.txt (Root)
project(applic)
include_directories(
"${CMAKE_SOURE_DIR/node"
"${CMAKE_SOURE_DIR/logger")
add_library(node node.cpp)
add_executable(applic main.cpp)
target_link_libraries(applic logger node)
CMakeLists.txt (Logger)
add_library(logger logger.cpp)
CMakeLists.txt (Node)
add_library(node node.cpp)
CMakeLists.txt (Gtest)
add_executable(gtest-node gtest-node.cpp)
set_target_properties(gtest-node PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR})
target_link_libraries(gtest-logger gtest phtread logger node)
add_test(NAME node COMMAND $<TARGET_FILE:gtest-node>
enable_testing()
The original error:
../../../../lib/libdatabase.a(sql.cpp.o): In function `SQL::Open()':
/home/rsalm/test/src/database/sql/sql.cpp:19: undefined reference to `Logger::TagDebug'
/home/rsalm/test/src/database/sql/sql.cpp:19: undefined reference to `Logger::instance(std::string const&) '
../../../../lib/libdatabase.a(sql.cpp.o): In function `SQL::Close()':
/home/rsalm/test/src/database/sql/sql.cpp:27: undefined reference to `Logger::TagDebug'
/home/rsalm/test/src/database/sql/sql.cpp:27: undefined reference to `Logger::instance(std::string const&) '
../../../../lib/libdatabase.a(sql.cpp.o): In function `Logger& operator<< <char [25]>(Logger&, char const (&) [25]) ':
/home/rsalm/test/inc/logger.h:33: undefined reference to `Logger::pInstance'
../../../../lib/libdatabase.a(sql.cpp.o): In function `Logger& operator<< <char [21]>(Logger&, char const (&) [21]) ':
/home/rsalm/test/inc/logger.h:33: undefined reference to `Logger::pInstance'
../../../../lib/libdatabase.a(sql.cpp.o): In function `Logger& operator<< <char [26]>(Logger&, char const (&) [26]) ':
/home/rsalm/test/inc/logger.h:33: undefined reference to `Logger::pInstance'
../../../../lib/libdatabase.a(sql.cpp.o): In function `Logger& operator<< <char [24]>(Logger&, char const (&) [24]) ':
/home/rsalm/test/inc/logger.h:33: undefined reference to `Logger::pInstance'
collect2: error: ld returned 1 exit status
src/layout/gtest/CMakeFiles/gtest-layout-factory.dir/build.make:98: recipe for target '../bin/gtest-layout-factory' failed
make[2]: *** [../bin/gtest-layout-factory] Error 1
CMakeFiles/Makefile2:1824: recipe for target 'src/layout/gtest/CMakeFiles/gtest-layout-factory.dir/all' failed

Your node library uses the logger library, but you haven't specified that link relationship. You need to add the following:
target_link_library(node PRIVATE logger)
Then, any time you link to node, CMake will append logger to the link libraries if node is built as a static library.
You almost get away with what you did with the linking of the gtest-logger target where you explicitly added the logger library there, assuming you actually meant gtest-node rather than gtest-logger in that call to target_link_library(). If you had listed logger after node, I would have expected the build to work (assuming the target name typo), but that would not be the correct way to do it. You should only need to explicitly list logger as a link dependency of gtest-logger if something in gtest.cpp directly referenced something from the Logger class.

Related

CMake & C++ : linker error : undefined reference to function

I am trying to compile a simple C++ program with CMake, but I am getting a linker error :
[2/2] Linking CXX executable bin/MY_PROGRAM
FAILED: bin/MY_PROGRAM
: && g++ -g CMakeFiles/MY_PROGRAM.dir/src/main.cpp.o -o bin/MY_PROGRAM && :
/usr/bin/ld: CMakeFiles/MY_PROGRAM.dir/src/main.cpp.o: in function `main':
/home/user/Code/root/src/main.cpp:27: undefined reference to `str_toupper(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)'
collect2: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.
I have tried looking at some questions with similar issues but couldn't find what I did wrong. There must be a problem with my directory structure and CMake files. I could change the directory structure to make things easier, but I may be missing something important and I'd like to figure out why. Also, I am new to C++ so I might be doing something wrong in the code itself, but my IDE doesn't find any issue.
My directory structure is :
root
|-- CMakeLists.txt
|-- src
|-- main.cpp
|-- utils
|-- CMakeLists.txt
|-- src
|-- strutils.cpp
|-- include
|-- strutils.h
The top-level CMakeLists.txt is :
// general stuff (standard, etc...)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY bin)
set(MY_PROGRAM_SRC src/main.cpp)
add_executable(MY_PROGRAM ${MY_PROGRAM_SRC})
include_directories(utils/include)
link_libraries(MY_LIBRARY)
and utils/CMakeLists.txt :
set(MY_LIBRARY_SRC include/strutils.h src/strutils.cpp)
add_library(MY_LIBRARY STATIC ${MY_LIBRARY_SRC})
// This is to include strutils.h in strutils.cpp
// (but is it needed ? I could just #include <string> in strutils.cpp, I guess)
target_include_directories(MY_LIBRARY PUBLIC include)
Finally, the source files :
// strutils.h
#include <string>
void str_toupper(const std::string &in, std::string &out);
// strutils.cpp
#include <algorithm>
#include <strutils.h>
void str_toupper(const std::string &in, std::string &out) {
std::transform(in.begin(), in.end(), out.begin(), [](char c){return std::toupper(c);});
}
// main.cpp
#include <strutils.h>
#include <cstdlib>
#include <cstdio>
#include <string>
int main(int argc, char *argv[]) {
// ...
std::string arg_in;
for (int i = 0; i < argc; i++) {
str_toupper(std::string(argv[i]), arg_in);
}
}
Does anyone have an idea of what's going on ? Thanks !
The first sentence in the manual link_libraries
Link libraries to all targets added later.
You use this directive after the target MY_PROGRAM is added - the target MY_PROGRAM is added prior link_libraries.
Prefer use target_link_libraries(MY_PROGRAM MY_LIBRARY) - other targets can require different dependencies and it's better not use a global set of dependencies for all targets.

How to avoid "Undefined Reference" error when using my own library in an executable, building the project with CMake? [duplicate]

This question already has answers here:
Automatically add all files in a folder to a target using CMake?
(5 answers)
Closed 11 months ago.
I'm trying to set up a C++ project using CMake but I think I'm missing something. When I'm trying to use my library in an executable I get the error:
Scanning dependencies of target dynamic-shadows-lib
[ 33%] Linking CXX static library libdynamic-shadows-lib.a
[ 33%] Built target dynamic-shadows-lib
Scanning dependencies of target main
[ 66%] Building CXX object CMakeFiles/main.dir/main.cpp.o
[100%] Linking CXX executable main
/usr/bin/ld: CMakeFiles/main.dir/main.cpp.o: in function `main':
main.cpp:(.text+0x83): undefined reference to `num3()'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/main.dir/build.make:85: main] Error 1
make[1]: *** [CMakeFiles/Makefile2:78: CMakeFiles/main.dir/all] Error 2
make: *** [Makefile:84: all] Error 2
My file structure looks like this:
.
├── build
├── CMakeLists.txt
├── include
│   └── vec2f.hpp
├── main.cpp
└── src
└── vec2f.cpp
3 directories, 4 files
My root (and only) CMakeLists.txt looks like this:
cmake_minimum_required(VERSION 3.16)
project(
dynamic-shadows
VERSION 1.0
LANGUAGES CXX
)
# Set C++ to version 14
set(CMAKE_CXX_STANDARD 14)
# Set a name for the target
set(TARGET_LIB ${CMAKE_PROJECT_NAME}-lib)
# Make library ${TARGET_LIB}
add_library(${TARGET_LIB} STATIC)
# Set linker language to CXX (Gets error without it)
set_target_properties(${TARGET_LIB} PROPERTIES LINKER_LANGUAGE CXX)
# Set include directory for ${TARGET_LIB}
target_include_directories(
${TARGET_LIB}
PUBLIC
${PROJECT_SOURCE_DIR}/include
)
# Set sources for ${TARGET_LIB}
target_sources(
${TARGET_LIB}
PUBLIC
${PROJECT_SOURCE_DIR}/src
)
# Add a simple test executable to test the library
add_executable(main main.cpp)
# Link the ${TARGET_LIB} to main executable
target_link_libraries(
main
PUBLIC
${TARGET_LIB}
)
I suspect the problem lies in my CMakeLists.txt since I'm new to this, but I can't figure out what it is. What am I missing? Could it be something else I'm doing wrong?
The code I'm trying to run is very simple but I'll include it for reference:
./include/vec2.hpp
#ifndef __VEC2F_HPP__
#define __VEC2F_HPP__
#include <iostream>
namespace ds {
class vec2f {
public:
float x;
float y;
vec2f(float x_value, float y_value) : x(x_value), y(y_value) {}
};
} // End of namespace ds
std::ostream & operator<<(std::ostream &out, const ds::vec2f &v);
ds::vec2f operator+(const ds::vec2f &left, const ds::vec2f &right);
float num3();
#endif
./src/vec2f.cpp
#include "../include/vec2f.hpp"
/**
* #brief Overload of << operator for ds::vec2f class to allow for printing it in std::cout.
*
* #param out std::ostream reference (&)
* #param v ds::vec2f reference (&)
* #return std::ostream& out
*/
std::ostream & operator<<(std::ostream &out, const ds::vec2f &v)
{
return out << "[" << v.x << ", " << v.y << "]";
}
/**
* #brief Overload of + operator for ds::vec2f class to allow for vector addition.
*
* #param left ds::vec2f
* #param right ds::vec2f
* #return ds::vec2f sum
*/
ds::vec2f operator+(const ds::vec2f &left, const ds::vec2f &right)
{
return ds::vec2f(
left.x + right.x,
left.y + right.y
);
}
float num3()
{
return 3;
}
./main.cpp
#include "vec2f.hpp"
int main(int argc, char* argv[])
{
std::cout << "Hello world!" << std::endl;
ds::vec2f v1 = ds::vec2f(8, -2);
ds::vec2f v2 = ds::vec2f(2, 5);
float n = num3();
std::cout << "Res: " << n << std::endl;
return 0;
}
I've tried to follow solutions to similair problems which usually seems to have something to do with linking.
Most havn't really helped since I'm required to solve this using CMake. I've tried with a variety of CMakeLists.txt configurations but ended up with this one since it looked the cleanest and seemed to be using the latest implementations of commands (target_include_directory instead of include_directories etc..)
# Set sources for ${TARGET_LIB}
target_sources(
${TARGET_LIB}
PUBLIC
${PROJECT_SOURCE_DIR}/src
)
You add source files, not directories. Just:
add_library(... STATIC
src/vec2f.cpp
)
Do not use PROJECT_SOURCE_DIR, it will change when someone does add_subirectory from above. If you want current project source dir, thats ${CMAKE_CURRENT_SOURCE_DIR}.
# Set linker language to CXX (Gets error without it)
set_target_properties(${TARGET_LIB} PROPERTIES LINKER_LANGUAGE CXX)
Remove it. Yes, without source files, no one knows what language your library is in.

C++ uniform declaration is not working in class

I am trying to have a member array in a class with its length specified by the const static int variable for future needs.
My compiler throws an error with it, and I am not sure this is an error about a uniform initialization or array initialization, or both.
Here is the header file:
#ifndef SOUECE_H
#define SOURCE_H
class Test
{
public:
Test();
static const int array_length{2};
double array[array_length];
};
#endif
This is the source file.
#include "source.h"
Test::Test()
:array_length{0} //Typo of array{0}
{
}
These are the problem messages.
[enter image description here][1]
[1]: https://i.stack.imgur.com/IQ2Mk.png
This is the CMake file.
cmake_minimum_required(VERSION 3.0.0)
project(temp VERSION 0.1.0)
include(CTest)
enable_testing()
add_executable(temp main.cpp)
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
Finally, these are what the compiler is complaining about when I try to build the project.
[main] Building folder: temp
[build] Starting build
[proc] Executing command: /usr/local/bin/cmake --build /Users/USERNAME/Desktop/temp/build --config Debug --target all -j 14 --
[build] [1/2 50% :: 0.066] Building CXX object CMakeFiles/temp.dir/main.cpp.o
[build] FAILED: CMakeFiles/temp.dir/main.cpp.o
[build] /usr/bin/clang++ -g -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk -MD -MT CMakeFiles/temp.dir/main.cpp.o -MF CMakeFiles/temp.dir/main.cpp.o.d -o CMakeFiles/temp.dir/main.cpp.o -c ../main.cpp
[build] In file included from ../main.cpp:1:
[build] ../source.h:8:22: error: function definition does not declare parameters
[build] static const int array_length{2};
[build] ^
[build] ../source.h:9:18: error: use of undeclared identifier 'array_length'
[build] double array[array_length];
[build] ^
[build] 2 errors generated.
[build] ninja: build stopped: subcommand failed.
[build] Build finished with exit code 1
I am using macOS 12.0 beta, with VS Code 1.60.2, clang 13.0.0, CMake 3.20.2.
Please let me know if you see any wrong or have any suggestions.
This cannot work:
Test::Test()
:array_length{0}
You cannot set a const static data member in your constructor. A static value has the same value accross all object instances. But as it is also const no instance is allowed to change the value.
Since array_length is static const you have to initialize it like this:
source.h
#ifndef SOUECE_H
#define SOURCE_H
class Test
{
public:
Test();
static const int array_length = 2;
//static const int array_length{2}; this will also work just note that we cannot use constructor initializer list to initialize static data members
double array[array_length];
};
#endif
source.cpp
#include "source.h"
Test::Test()//we cannot use constructor initializer list to initialize array_length
{
}
Note that static const int array_length{2}; will also work but the thing is that in the constructor we cannot use constructor initializer list to initialize static data member.
EDIT:
If you decide to use static const int array_length{2}; then make sure that you have C++11(or later) enabled in your CMakeLists.txt. For this you can add
set (CMAKE_CXX_STANDARD 11)
to your CMakeLists.txt.
Alternatively, you could instead add
target_compile_features(targetname PUBLIC cxx_std_11)
in your CMakeLists.txt. In your case replace targetname with temp since that is the targetname that you have. Check this out for more how to active C++11 in CMake .

Undefined Reference when compiling ThorSerializer example

I am looking into JSON Serializer/ Deserializer c++ libraries and found the ThorSerializer to have all essential functoinalities and it keeps the variable names of key value pairs in the JSON Format as they are when serializing user defined classes, without replacing the keys with generic names, which is exactly what I need.
When I try to compile the basic example provided on the project's home page:
#include "ThorSerialize/Traits.h"
#include "ThorSerialize/JsonThor.h"
struct Shirt
{
int red;
int green;
int blue;
};
class TeamMember
{
std::string name;
int score;
int damage;
Shirt team;
public:
TeamMember(std::string const& name, int score, int damage, Shirt const& team)
: name(name)
, score(score)
, damage(damage)
, team(team)
{}
// Define the trait as a friend to get accesses to private
// Members.
friend class ThorsAnvil::Serialize::Traits<TeamMember>;
};
// Declare the traits.
// Specifying what members need to be serialized.
ThorsAnvil_MakeTrait(Shirt, red, green, blue);
ThorsAnvil_MakeTrait(TeamMember, name, score, damage, team);
int main()
{
using ThorsAnvil::Serialize::jsonExporter;
TeamMember mark("mark", 10, 5, Shirt{255,0,0});
// Use the export function to serialize
std::cout << jsonExporter(mark) << "\n";
}
I always end up receiving the following error output
/usr/bin/ld: CMakeFiles/test.dir/src/test.cpp.o: in function `main':
test.cpp:(.text+0xb7): undefined reference to `ThorsAnvil::Serialize::defaultPolymorphicMarker[abi:cxx11]'
/usr/bin/ld: CMakeFiles/test.dir/src/test.cpp.o: in function `ThorsAnvil::Serialize::JsonPrinter::~JsonPrinter()':
test.cpp:(.text._ZN10ThorsAnvil9Serialize11JsonPrinterD2Ev[_ZN10ThorsAnvil9Serialize11JsonPrinterD5Ev]+0x13): undefined reference to `vtable for ThorsAnvil::Serialize::JsonPrinter'
/usr/bin/ld: CMakeFiles/test.dir/src/test.cpp.o: in function `ThorsAnvil::Serialize::operator<<(std::ostream&, ThorsAnvil::Serialize::Exporter<ThorsAnvil::Serialize::Json, TeamMember> const&)':
test.cpp:(.text._ZN10ThorsAnvil9SerializelsERSoRKNS0_8ExporterINS0_4JsonE10TeamMemberEE[_ZN10ThorsAnvil9SerializelsERSoRKNS0_8ExporterINS0_4JsonE10TeamMemberEE]+0x65): undefined reference to `ThorsAnvil::Serialize::JsonPrinter::JsonPrinter(std::ostream&, ThorsAnvil::Serialize::PrinterInterface::PrinterConfig)'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/test.dir/build.make:103: test] Error 1
make[1]: *** [CMakeFiles/Makefile2:95: CMakeFiles/test.dir/all] Error 2
make: *** [Makefile:103: all] Error 2
I am using the header only version and my CMakeLists.txt looks as follows:
cmake_minimum_required(VERSION 3.4 FATAL_ERROR)
project("test")
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Pistache REQUIRED)
include_directories(include)
add_executable(test src/test.cpp)
target_include_directories(test PUBLIC include include/ThorsSerializer)
To me it looks like the compiler can't find certain functions, although all of them are present in the respective header files and the namespaces. I checked for typos and similar but can't find the cause for my problems.
Does Anyone know what I might be doing wrong?
At the very beginning, you have to define a macro HEADER_ONLY by including a special header file named "ThorSerialize/SerializeConfig.h". This will unlock template definition functions located in .tpp file and remove "undefined definition" at compilation time.
Although, you may encounter other troubles because of "#endif" declared without its corresponding "#if". The quick fix I found, is to comment last line "#endif" of these following files:
ThorSerialize/CustomSerialization.source
ThorSerialize/Serialize.source
And finally, it will compile.

JNI UnsatisfiedLinkError even after everything compiles good

I started looking into Java's JNI feature. I followed this [tutorial][1]. So my class goes like this :
package me.gagan.pheonix.natve;
public class HelloJNI {
static {
System.loadLibrary("hello");
}
private native void sayHello();
public static void main(String... args) {
new HelloJNI().sayHello();
}
}
HelloJNI.c file like this:
\#include <jni.h>
\#include <stdio.h>
\#include "me_gagan_pheonix_natve_HelloJNI.h"
JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject thisObj) {
printf("Hello World in native C!\n");
return;
}
My directory structure is:
<pre>
C:.
├───bin
│ ├───me
│ │ └───gagan
│ │ └───pheonix
│ │ └───natve
│ │ └───HelloJNI.class
│ ├───resources
│ │ └───hello.dll
│ ├───me_gagan_pheonix_natve_HelloJNI.h
│ └───HelloJNI.c
└───src
├───me
│ └───gagan
│ └───pheonix
│ └───natve
│ └───HelloJNI.java
└───resources
</pre>
I also added
-Djava.library.path=C:\Gagan\workspace\Pheonix\bin\resources
But still error is coming. Any idea what is going wrong. Followed each step correctly.
Following commands worked for me
gcc -Wl,--add-stdcall-alias -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -shared -o hello.dll HelloJNI.c
echo %JAVA_HOME%
C:\Java\jdk1.6.0_45
javah -verbose -jni -classpath . me.gagan.pheonix.natve.HelloJNI
EDIT:
Exception encountered is :
Exception in thread "main" java.lang.UnsatisfiedLinkError: me.gagan.pheonix.natve.HelloJNI.sayHello()V
at me.gagan.pheonix.natve.HelloJNI.sayHello(Native Method)
at me.gagan.pheonix.natve.HelloJNI.main(HelloJNI.java:12)
[1]: https://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html
Hi anybody having any suggestions ??
package me.gagan.pheonix.natve;
public class HelloJNI {
...
JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject thisObj) {
You've changed the package name after you generated the .h file with javah. Do it again, and adjust the function name in the .c file accordingly, to agree with the declaration in the .h file.