Yesterday I have started including spdlog into a personal project of mine to use for logging. So far I have had some problems with getting library inclusion to work but those are now solved completely.
Now everything compiles just fine, with all headers found however when I try to create loggers or simply set the pattern for logging the code crashes with a segmentation fault. More specifically no matter which function I call from the spdlog namespace for the very first time in the program causes the crash.
I have a class abstracting some parts from spdlog (based on this repo) as follows:
//Logger.hpp
#ifndef TE_LOGGER_HPP
#define TE_LOGGER_HPP
#include <spdlog/spdlog.h>
namespace te {
class Logger {
public:
static void Init();
inline static std::shared_ptr<spdlog::logger> &getCoreLogger() {
return sCoreLogger;
}
inline static std::shared_ptr<spdlog::logger> &getClientLogger() {
return sClientLogger;
}
private:
static std::shared_ptr<spdlog::logger> sCoreLogger;
static std::shared_ptr<spdlog::logger> sClientLogger;
};
}
#endif //TE_LOGGER_HPP
//Logger.cpp
#include "Logger.hpp"
#include <spdlog/sinks/stdout_color_sinks.h>
std::shared_ptr<spdlog::logger> te::Logger::sCoreLogger;
std::shared_ptr<spdlog::logger> te::Logger::sClientLogger;
void te::Logger::Init() {
//The first of any of the following three lines cause a crash
//no matter the order, regardless of the pattern used in set_pattern
spdlog::set_pattern("%v");
sCoreLogger = spdlog::stdout_color_mt("CORE");
sClientLogger = spdlog::stdout_color_mt("CORE");
sCoreLogger->set_level(spdlog::level::trace);
sClientLogger->set_level(spdlog::level::trace);
}
From the stack traces it seems that the issue is with the formatter class in spdlog being set null for some reason somewhere within the library. I am using the latest CLion, C++14 (I am aware that spdlog is C++11, but I need features from 14 later down the line, also setting -std=c++11 doesn't solve the issue) and the latest version of spdlog as of yesterday (pulled straight from their GitHub repo) on Ubuntu 18.04.
EDIT: As per the request in the comments I have created a small project (single cpp file, include spdlog the way I do in the real project, or the same code and library setup as in the real project referenced from the main.cpp file and linked to accordingly) that aims to reproduce the issue and here are my findings:
* The issue is not present when I use spdlog directly in the executable
* The issue is present if the Logger class is moved into a shared library and linked to from there
Here is the error message I am getting:
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
And the CMakeLists.txt files I am using (I nest the library's one into the project since as of now CLion does not support "multiple projects in the same solution" like for example VS does):
#CMakeLists.txt for Library
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
project(TokenEngine VERSION 0.0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 14)
set(SOURCE_FILES src/Application.cpp src/Application.hpp src/EntryPoint.hpp src/Logger.cpp src/Logger.hpp)
#include_directories("${CMAKE_CURRENT_SOURCE_DIR}/libs/")
add_library(TokenEngine SHARED ${SOURCE_FILES})
target_include_directories(TokenEngine PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/libs/spdlog-1.x/include")
#Expose the public API of the engine to any project that might use it
target_include_directories(TokenEngine PUBLIC include)
#CMakeLists.txt for top level project
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
add_definitions(-DTE_PLATFORM_LINUX)
project(Build CXX)
add_subdirectory(TokenEngine)
add_subdirectory(Sandbox)
You are using the same name for both loggers, when you run it you'll get:
$ ./logger
libc++abi.dylib: terminating with uncaught exception of type spdlog::spdlog_ex: logger with name 'CORE' already exists
Abort trap: 6
If you change the name of the client logger to something else it works fine:
sCoreLogger = spdlog::stdout_color_mt("CORE");
sClientLogger = spdlog::stdout_color_mt("CLIENT");
The problem is probably that spdlog’s static objects are defined twice - from inside the shared library and from client code that includes your logger header (which incldes spdlog.h).
Try to remove the include to spdlog.h from the header file and (and use forward declaration of spdlog::logger instead), and include spdlog.h only from your Logger.cpp file.
Edit:
spdlog::logger cannot be forward declared across compilation unit boundries.
The solution is to wrap the logger with some simple class defined in logger.cpp and only export it in logger.h
Related
I want to know how to use CMake to dynamically link CUDA libraries, I know it seems to require some extra restrictions, but don't know exactly how to do it. Here is a simple example I wrote to illustrate my problem.
Directory structure:
Dir/
├── CMakeLists.txt
├── header.cuh
├── kernel.cu
└── main.cpp
Environment:
OS: Windows 11
GPU: RTX 3060 laptop
CUDA Toolkit: 11.6
Platform: Visual Studio 2022
header.cuh:
#include "stdio.h"
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
extern "C" void f();
kernel.cu:
#include "header.cuh"
void __global__ print()
{
int idx = blockIdx.x * blockDim.x + threadIdx.x;
printf("%d\n", idx);
}
void f()
{
print<<<1, 10>>>();
}
main.cpp:
#include "header.cuh"
extern "C" void f();
int main()
{
f();
return 0;
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.17)
project(test)
set(CMAKE_CXX_STANDARD 17)
find_package(CUDA REQUIRED)
enable_language("CUDA")
set(CMAKE_CUDA_STANDARD 14)
set(CUDA_SEPARABLE_COMPILATION ON)
string(APPEND CMAKE_CUDA_FLAGS " -rdc=true --cudart shared")
add_library(CUDA_COMP SHARED header.cuh kernel.cu)
set_property(TARGET CUDA_COMP PROPERTY CUDA_ARCHITECTURES 86-real 86-virtual)
add_executable(main main.cpp)
target_link_libraries(main CUDA_COMP)
The project can be configured successfully, but a problem with unresolved external symbols f referenced in function main occurs when building it.
I also looked up the corresponding solution on Stackoverflow, but it didn't work. For example, an answer on Stackoverflow mentioned adding " -rdc=true --cudart shared" to cmake, and I did the same (see line 10 of CMakeLists.txt).
This problem has been bothering me for a long time. I hope you can tell me the cause of the problem and how to solve it. Thank you so much!
There is a lot of issues with OP's code.
CUDA/C++ issues:
CUDA nowadays is a C++ dialect, not C. Therefore I would not declare f() to be extern "C".
There seems to be no reason for the header to include anything. When something is needed by the implementation, but not by the interface, the include should not be part of the interface.
If you want to use this header to interface with non-CUDA C++ code (a .cpp file) it should be named .h (or .hpp). I would expect .cuh files to only ever be included by .cu files. One also usually puts the same name on interface and implementation, so I renamed the header to kernel.h.
Header guards are missing.
Why would you redeclare f() in main.cpp? That is what the header is for.
You forgot to synchronize after the kernel launch, so the app will most probably print nothing at all.
CMake issues:
Setting properties etc. in CMakeLists.txt globally is discouraged. One should use the target specific APIs whenever possible. Some of these global properties have to be set before project() to work at all.
CMake sets -rdc=true automatically when CUDA_SEPARABLE_COMPILATION is set.
CUDA separable compilation is only needed when e.g. a kernel uses a device function that is defined in a different translation unit. I am not sure if OP thinks he needs it, or if the part that he needs it for was removed when creating a minimal example.
There is the CUDA_RUNTIME_LIBRARY property for dynamically linking the CUDA runtime.
Using find_package(CUDA) is deprecated in favor of using CUDA as a language. In the rare case where one does not want to use the CUDA language, but still needs to find the CUDA toolkit, there is also FindCUDAToolkit. OP only needs the language. But the linked documentation also lists all the CUDA libraries that come with the toolkit. Here we can see that most libraries provide a _static version to differentiate between static and dynamic linking. When using the CUDA language, these are also available but without the CUDA:: "namespace". Do not use target_link_libraries(... cudart) when using the language, as this will not influence the CUDA_RUNTIME_LIBRARY property, i.e. you might get some kind of undefined CMake behavior. But when you want to dynamically link against e.g. CUBLAS, you can use target_link_libraries(... cublas).
The CUDA architecture should not be hardcoded into the CMakeLists.txt as long as the project does not use some kind of architecture-specific intrinsics. Instead one should specify the architecture at configuration time. CMake 3.18 added CMAKE_CUDA_ARCHITECTURES in version so I would recommend setting the minimum to 3.18 instead of 3.17. Then you can call e.g. cmake -DCMAKE_CUDA_ARCHITECTURES=86 (not sure how this is handled in Visual Studio). I added a default for 86 that can still be overwritten at configuration time when needed.
Fixed Example
The following files work for me under Linux with CMake 3.23.1, CUDA 11.8.0 and GCC 11.3.0:
kernel.h:
#pragma once
void f();
kernel.cu:
#include "kernel.h"
#include <cstdio>
void __global__ print()
{
int idx = blockIdx.x * blockDim.x + threadIdx.x;
std::printf("%d\n", idx);
}
void f()
{
print<<<1, 10>>>();
cudaDeviceSynchronize();
}
main.cpp:
#include "kernel.h"
int main()
{
f();
return 0;
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.18)
# this has to be set before project()
if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES)
set(CMAKE_CUDA_ARCHITECTURES 86)
endif()
project(test LANGUAGES CXX CUDA)
add_library(cuda_comp SHARED kernel.h kernel.cu)
# this command would only set the minimum standard,
# i.e. CMake can still decide to use -std=c++17 instead
# if the given compilers support C++17
target_compile_features(cuda_comp PRIVATE cuda_std_14)
set_target_properties(cuda_comp
PROPERTIES
CUDA_RUNTIME_LIBRARY Shared
# CUDA_STANDARD 14 # this one cannot be changed by CMake
# CUDA_SEPARABLE_COMPILATION ON # not needed for this example
)
add_executable(main main.cpp)
target_compile_features(main PRIVATE cxx_std_17)
# set_target_properties(main CXX_STANDARD 17)
target_link_libraries(main cuda_comp)
I am pretty comfortable with CMake when used in Linux. That is, I use CMake to logically group my code into "targets" and abstract such things as "include directories" and other dependencies. Say, inside a large project my modus operandi would be to add_library(..SHARED) for a bunch of related files that provide one logically coherent unit, and target_link_libraries(...) to it from somewhere else.
(Of course, idiosyncrasies of CMake is another topic. I won't digress to discussions about e.g. its syntax.)
Now, for Windows 10. I fired up Visual Studio 2019 Community with the MSVC toolchain (that is, not WSL/WSL2), and created a CMake Project using VS's "New Project" wizard.
It has created for me some boilerplate code, and the whole project tree looks as below:
In this image, EnRouteGeneratedLibrary is something I added as the next step. Inside it, I have one *.hpp file:
#pragma once
template<typename T>
class __declspec(dllexport) SampleTemplateKlass {
public:
T sum(T x, T y) { return x + y; }
};
template class __declspec(dllexport) SampleTemplateKlass<int>;
and the CMakeLists.txt's contents are as follows:
add_library(${TRG} SHARED ${CMAKE_CURRENT_SOURCE_DIR}/SampleTemplateKlass.hpp)
# set_target_sources(${TRG} PUBLIC SampleTemplateKlass.hpp)
set_target_properties(${TRG} PROPERTIES LINKER_LANGUAGE CXX)
target_include_directories(${TRG} PUBLIC .)
set(WINDOWS_EXPORT_ALL_SYMBOLS 1)
Finally, in when building a simple "Hello World" executable, I have the following:
//
#include "SampleProjectWithDll.h"
#include "SampleTemplateKlass.hpp"
using namespace std;
int main()
{
cout << "Hello CMake." << endl;
SampleTemplateKlass<int> tmp;
std::cout << tmp.sum(42, 314) << std::endl;
return 0;
}
(This is the boilerplate code created by VS + my additions).
I link as follows:
# CMakeList.txt : CMake project for SampleProjectWithDll, include source and define
# project specific logic here.
#
cmake_minimum_required (VERSION 3.8)
add_subdirectory(EnRouteGeneratedLibrary)
# Add source to this project's executable.
add_executable (SampleProjectWithDll "SampleProjectWithDll.cpp" "SampleProjectWithDll.h")
target_link_libraries(SampleProjectWithDll PUBLIC SampleLibrary)
# TODO: Add tests and install targets if needed.
When building all this, I get two errors,
LNK2001 unresolved external symbol _DllMainCRTStartup and LNK1120 1 unresolved externalswith SampleLibrary.dll in the "File" field of the error panel.
The question is, how to make this simple "Hello World" compile and execute under Windows,
using the CMake infrastructure?
I know there are already a few threads on this topic, however, after reading through many of them I have been unable to find a solution to my problem. I am working on a file loader/parser and am using CMake for the first time. My CMakeList.txt file is being used to import an XML parser (xerces) and currently looks like:
cmake_minimum_required(VERSION 2.8)
project(fileloader)
set(SRCS
Source.cpp
)
include_directories(./
${SPF_ROOT}/xerces/win64/include/xercesc/dom
)
add_executables(${PROJECT_NAME} ${SRCS})
add_library(HEADER_ONLY_TARGET STATIC XMLString.hpp XercesDOMParser.hpp DOM.hpp HandlerBase.hpp PlatformUtils.hpp)
set_target_properties(HEADER_ONLY_TARGET PROPERTIES LINKER_LANGUAGE CXX)
When running my solution the error I am recieving is "CMake can not determine linker language for target: fileloader"
I am relatively new to c++ and completely new to CMake so hopefully I am missing something simple, but any and all help is greatly appreciated!
EDIT:
The code I am writing is on a non-internet enabled machine so I cannot copy and paste the entire code, however this is the except causing the issue:
...
#include "XMLString.hpp"
#include "XercesDOMParser.hpp"
#include "DOM.hpp"
#include "HandlerBase.hpp"
#include "PlatformUtils.hpp"
class XMLReader : public IFileReader {
public:
XMLReader(){};
void read(std::ifstream& file) {
xerces::XMLPlatformUtils::Initialize();
xercesc::XercesDOMParser* parser = new xercesc::XercesDOMParser();
parser->setValidationScheme(xercesc::XercesDOMParser::Val_Always);
parser->setDoNamespaces(true);
xercesc::ErrorHandler* errHandler = (xercesc::ErrorHandler*) new xercesc::HandlerBase();
parser->setErrorHandler(errHandler);
std::getline(file, line);
newFile = line.c_str();
parser->parse(newFile);
}
}
...
HEADER_ONLY_TARGET is not a keyword argument.
If you want a header-only library, use an interface library:
add_library(<name> INTERFACE [IMPORTED [GLOBAL]])
Added:
SET_TARGET_PROPERTIES([some name] PROPERTIES LINKER_LANGUAGE C11)
to the end of my program and the error went away. After reading a million web pages I found https://kuniganotas.wordpress.com/2011/05/25/error-cmake-can-not-determine-linker-language-for-target/ and the solution was literally that simple! Hopefully this can help others with this error!
Let me preface this by saying I'm quite new to CMake, so please let me know if I'm just misunderstanding something here.
First I'm compiling a static library that acts as a wrapper to an external program (Paraview). The CMakeLists file I use to create this library is like so:
cmake_minimum_required(VERSION 3.3)
project(POP)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY [LIBRARY HOME]/lib/)
set(ParaView_DIR [PARAVIEW HOME]/ParaView-v5.4.1/)
find_package(ParaView REQUIRED COMPONENTS vtkPVPythonCatalyst)
include("${PARAVIEW_USE_FILE}")
if(NOT PARAVIEW_USE_MPI)
message(SEND_ERROR "ParaView must be built with MPI enabled")
endif()
set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/pop_API.h ${CMAKE_CURRENT_SOURCE_DIR}/include/pop_API_vars.h ${CMAKE_CURRENT_SOURCE_DIR}/include/pop_Structures.h ${CMAKE_CURRENT_SOURCE_DIR}/include/wrapper_pop_API.h)
add_library(POP STATIC src/pop_API.cpp src/wrapper_pop_API.cpp src/pop_Adaptor.cpp ${HEADER_FILES})
target_include_directories(POP PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(POP LINK_PUBLIC vtkPVPythonCatalyst)
include(vtkModuleMacros)
include(vtkMPI)
vtk_mpi_link(POP)
This links in a specific Paraview module I need, and some VTK datastructures that are required for that as well.
This CMake file runs just fine, and I can compile the libraries no problem. If I add a Main function to my C++ code and compile it as an executable, that runs no problem as well. So everything seems fine here.
Now, I ultimately want this static library to be called by a second piece of code. To test this I've written a toy C++ project that just calls the main functions of this library
#include "pop_Structures.h"
#include "pop_API.h"
int main()
{
PopGrid* grid;
PopSolution* solution;
a = 1;
b = 2;
int x = POP_INITIALIZE(a, b);
int y = COPROCESS(PopGrid, PopSolution, a, b, a, b);
int z = POP_FINALIZE();
}
The included header files just define the PopSolution and PopGrid structures, as well as the prototypes for the three functions being called. I create the executable for this program using a CMakeLists file that looks like
cmake_minimum_required(VERSION 3.3)
project(CPP_Test)
find_package(MPI REQUIRED)
include_directories(${MPI_CXX_INCLUDE_PATH})
set(HEADER_FILES [LIBRARY HOME]/include/pop_API.h [LIBRARY HOME]/include/pop_Structures.h)
ADD_EXECUTABLE(CPP_TEST CPP_Test.cpp ${HEADER_FILES})
target_include_directories(CPP_TEST PUBLIC [LIBRARY HOME]/include)
target_link_libraries(CPP_TEST[LIBRARY HOME]/lib/libPOP.a)
This CMake also runs just fine, but when I compile it I get a number of undefined reference errors pointing to functions in libPOP.a where I refer to VTK/Paraview data structures and functions. These are not directly defined in the toy code above, but they should be defined in the library, correct? If these structures are all defined when I compile the initial library, why are they not defined when I call that library externally?
Thanks.
Some time ago I started a big header library in C++1x using XCode. The current layout of the library is () something like (partial output from ls -R sponf)
sponf/sponf:
ancestors sponf.h sponf_utilities.h
categories sponf_children.h utilities
children sponf_macros.h
sponf/sponf/ancestors:
function.h meter.h set.h simulation.h
sponf/sponf/categories:
free_space.h prng.h random_distribution.h series.h
sponf/sponf/children:
distributions histogram.h random simulations
meters numeric series spaces
sponf/sponf/children/distributions:
arcsine_der.h exponential.h
box_muller.h uniform.h
sponf/sponf/children/meters:
accumulator.h timer.h
#... other subdirs of 'children' ...
sponf/sponf/utilities:
common_math.h limits.h string_const.h
#... other directories ...
I wanted to port this project to CLion, which seems a really good IDE (based on the similar AndroidStudio IDE) but I'm getting some troubles.
Small test program
I tried this small program as a test:
#include <iostream>
#include <sponf/sponf.h>
using namespace std;
int main() {
using space = sponf::spaces::euclidean_free_space<double, 3>;
sponf::simulations::random_walk<space> rw;
rw.step(1);
std::cout << rw.position.value << std::endl;
return 0;
}
The program compiles and runs fine. However, CLion does not recognize the spaces namespace (declared in one of the children files), nor the simulations namespace; they are both marked red and I cannot inspect their content, nor navigate to their definitions by ⌘-clicking, etc. etc...
Relevant parts of the library
Looking in "sponf.h" we find
#ifndef sponf_h
#define sponf_h
/* The classes below are exported */
#pragma GCC visibility push(default)
// include some of the standard library files
// ...
#include <Eigen/Eigen>
#include "sponf_macros.h"
#include "sponf_utilities.h"
#include "sponf_children.h"
#pragma GCC visibility pop
#endif
while in "sponf_children.h" (which is located at the top level, next to "sponf.h") we find
#ifndef sponf_locp_sponf_children_h
#define sponf_locp_sponf_children_h
namespace sponf {
// include some of the children
// ...
#include "children/spaces/euclidean_free_space.h"
#include "children/simulations/random_walk.h"
// include remaining children
// ...
}
#endif
Each "child" header will then include its corresponding "ancestor" or "category" header (which defines the superclass of the "child" itself).
The reaction of CLion
Despite the autocompletition prediction, which easily finds all the subdirectories and the headers, all the include directives in this last file get marked red and ⌘-clicking on any of them leads to a popup message
Cannot find declaration to go to
while the right ribbon of the editor signal many errors like
',' or ) expected
) expected
Declarator expected
Expecting type
Missing ;
Unexpected symbol
which are not the same for each include statement (each generates from 2 to all of these errors).
On the other hand, CLion is perfectly able to find all Eigen headers, which have pretty much the same structure!
I have put both libs in /opt/local/include and changed CMakeLists.txt accordingly
cmake_minimum_required(VERSION 2.8.4)
project(sponf)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
include_directories(/opt/local/include/sponf /opt/local/include/eigen3)
set(SOURCE_FILES main.cpp)
add_executable(sponf ${SOURCE_FILES})
Why can't CLion properly parse the project structure? XCode, after having included /opt/local/include/sponf and /opt/local/include/eigen3 in the HEADER_SEARCH_PATHS env. variable of the project, is able to find any header while compiling the same exact program.
Is there anything else I need to know? Am I doing it wrong or is it that CLion isn't that mature yet and this is just a sorry bug? This is my first approach to the CLion and the CMake toolchain, so any kind of information about it will be greatly appreciated!
Sorry for the very long question, I didn't manage to shrink it further... Thanks in advance guys, see you soon!
Here what I did in windows using cigwin64. I wanted to use Eigen library include in my project.
Eigen library is places in /usr/include/eigen then edited CMakeLists.txt and add
include_directories("/usr/include/eigen")
into it. Now CLion can find all source files in eigen lib. May be this what you wanted too.
Downgrade to Clion 2016.1.4 fixes the problem