I can’t figure out why but I keep getting linker errors.
I followed/read several problems for example this and I can’t get it to work.
My root is as follows:
.
├── bin
├── CMakeLists.txt
├── external
│ ├── CMakeLists.txt
│ └── tinyformat.h
├── source
│ ├── CMakeLists.txt
│ ├── main.cpp
│ └── utils
│ ├── assert.cpp
│ ├── assert.hpp
│ ├── CMakeLists.txt
│ ├── config.hpp
│ ├── print.hpp
│ ├── timer.cpp
│ └── timer.hpp
└── tests
├── CMakeLists.txt
└── test.cpp
Also TLDR
I tried looking for repos with a similar (possible smarter) structure. If you find any, let me know and I will try to make that work.
Code
In timer.hpp
I have defined a class:
#pragma once
#include "config.hpp"
#include <chrono>
#include <iostream>
#include <string>
#include <vector>
namespace WingDesigner {
/**
* Simple timer class: add checkpoints throughout the code and measure execution time between them.
*/
class Timer {
protected:
typedef std::chrono::steady_clock::time_point time_type;/// Time type.
std::vector<std::string> m_Labels; /// List of checkpoint labels.
std::vector<time_type> m_Times; /// List of checkpoint times.
// and some methods...
};
}
which is then implemented in timer.cpp
.
In my main.cpp
I want to create an instance of Timer and some prn
statement which is a macro.
#include "utils/timer.hpp"
#include <iostream>
#include <omp.h>
#include <vector>
using namespace WingDesigner;
int main() {
std::cout << "Hello, World!" << std::endl;
Timer t;
prn("Hi");
return 0;
}
My assert.hpp
also uses an external header from external
directory.
/**
* Implementation of custom assert and debug utilities.
*/
#pragma once
#include "config.hpp"
#include "print.hpp"
#include "tinyformat.h"
namespace WingDesigner {
// print macro
#define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(__VA_ARGS__, 5, 4, 3, 2, 1, 0)
#define VA_NUM_ARGS_IMPL(_1, _2, _3, _4, _5, N, ...) N
#define macro_dispatcher(func, ...) \
macro_dispatcher_(func, VA_NUM_ARGS(__VA_ARGS__))
#define macro_dispatcher_(func, nargs) macro_dispatcher__(func, nargs)
#define macro_dispatcher__(func, nargs) func##nargs
#define addflag(a) \
{ std::cerr << "flags=[flags, " << (a) << "];" << std::endl; }
#define prnv2(a, b) \
{ std::cerr << a << " = " << (b) << ";" << std::endl; }
#define prnv1(a) \
{ std::cerr << #a << " = " << (a) << ";" << std::endl; }
/**
* Prints a variable name and value to standard output. Can take one or two
* parameters. Example: int a = 6; prn(a) // prints 'a = 6;' prn("value", a) //
* prints 'value = 6;'
*/
#define prn(...) macro_dispatcher(prnv, __VA_ARGS__)(__VA_ARGS__)
using tinyformat::format;
using tinyformat::printf;
/// Namespace holding custom assert implementation.
namespace assert_internal {
/**
* Actual assert implementation.
* @param condition Condition to test, e.g.\ `n > 0`.
* @param file File where the assertion failed.
* @param func_name Function name where the assertion failed.
* @param line Line on which the assertion failed.
* @param message Message as specified in the `assert_msg` macro.
* @param format_list List of format field values to pass to
* `tinyformat::format` function.
*/
bool assert_handler_implementation(const char *condition, const char *file,
const char *func_name, int line,
const char *message,
tfm::FormatListRef format_list);
/// Assert handler that unpacks varargs.
template <typename... Args>
bool assert_handler(const char *condition, const char *file,
const char *func_name, int line, const char *message,
const Args &...args) { // unpacks first argument
tfm::FormatListRef arg_list = tfm::makeFormatList(args...);
return assert_handler_implementation(condition, file, func_name, line,
message, arg_list);
}
} // namespace assert_internal
#ifdef NDEBUG
#define assert_msg(cond, ...) ((void)sizeof(cond))
#else
/**
* @brief Assert with better error reporting.
* @param cond Conditions to test.
* @param ... The second parameter is also required and represents the message
* to print on failure. For every %* field in message one additional parameter
* must be present.
*
* Example:
* @code
* assert_msg(n > 0, "n must be positive, got %d.", n);
* @endcode
*/
#define assert_msg(cond, ...) \
((void)(!(cond) && \
WingDesigner::assert_internal::assert_handler( \
#cond, __FILE__, __PRETTY_FUNCTION__, __LINE__, __VA_ARGS__) && \
(assert(0), 1)))
// #cond, __FILE__, __PRETTY_FUNCTION__, __LINE__, __VA_ARGS__) &&
// (exit(1), 1)))
#endif
/**
* Prints given text in bold red.
* @param s text to print.
*/
inline void print_red(const std::string &s) {
std::cout << "\x1b[31;1m" << s << "\x1b[37;0m";
}
/**
* Prints given text in bold white.
* @param s text to print.
*/
inline void print_white(const std::string &s) {
std::cout << "\x1b[37;1m" << s << "\x1b[37;0m";
}
/**
* Prints given text in bold green.
* @param s text to print.
*/
inline void print_green(const std::string &s) {
std::cout << "\x1b[32;1m" << s << "\x1b[37;0m";
}
} // namespace WingDesigner
Note that my top level CMakeLists.txt
is:
cmake_minimum_required(VERSION 3.22)
project(wingdesigner LANGUAGES CXX)
# Variables.
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_subdirectory(source)
# contains an "external" library we will link to
add_subdirectory(external)
# enable testing and define tests
enable_testing()
add_subdirectory(tests)
my source/CMakeLists.txt
is:
add_executable(wingdesigner main.cpp)
add_subdirectory(utils)
target_link_libraries(wingdesigner
PRIVATE
tinyformat
utils
)
my source/utils//CMakeLists.txt
is:
add_library(assert "")
target_sources(assert
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/assert.cpp
PUBLIC
${CMAKE_CURRENT_LIST_DIR}/assert.hpp
)
target_include_directories(assert
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
add_library(timer
${CMAKE_CURRENT_LIST_DIR}/timer.hpp
${CMAKE_CURRENT_LIST_DIR}/timer.cpp)
target_link_libraries(timer assert)
and finally my external/CMakeLists.txt
is:
add_library(tinyformat "")
target_include_directories(tinyformat
PRIVATE
include
)
The error
No matter what I try I get the following error:
CMake Error at external/CMakeLists.txt:1 (add_library):
No SOURCES given to target: tinyformat
but there aren’t any sources, just a header.
Is there a better way to do this?