I want to use a C++ logging library in a mixed C/C++ application. The legacy application is full of printf style logging. The new library also supports that.
The C files are compiled with C compiler, so I am not able to include C++ logging headers into them.
I am struggling to create a C++ library with C-style headers and C++ source file:
//logger.h
extern "C" {
extern void RS_LOG_INFO_PRIVATE(const char* fmt, ...);
#define LOG_INFO(...) RS_LOG_INFO_PRIVATE(__VA_ARGS__)
}
//logger.cpp
#include "logger.h"
#include <aws/core/utils/logging/LogMacros.h>
#include <utility>
template<class... Args>
void RS_LOG_INFO_PRIVATE(const char* fmt, Args&&... args) {
AWS_LOG_DEBUG(fmt, std::forward<Args>(args)...);
}
//main file
#include "logger.h"
int main() {
LOG_INFO("test %s", "me");
}
I get undefined reference to 'RS_LOG_INFO_PRIVATE'
.
nm -C
also verifies this:
U RS_LOG_INFO_PRIVATE(char const*, ...)
Is there a solution to solve this, or I should change my approach?
If you are recompiling your application, you can seriously consider changing from a C compiler to a C++ compiler. Doing so will cause various compilation issues, but resolving them is usually no more difficult than some major modification to how C compilation was done (e.g., new compiler vendor, or increasing compiler warning levels). In this way, you may be able to avoid using C style variable arguments, and replace them with variadic template calls instead.
If switching to a C++ compiler is not viable, then you have to make your header files act as a C header file for the C compiler, and act as a C++ header file for the C++ compiler (or maintain two different header files for the different compilers).
Assuming your C and C++ compiler have compatible C ABIs, then the usual mechanism for detecting which compiler is being used is the __cplusplus
macro.
#pragma once
#ifdef __cplusplus
// ... C++ stuff
#endif
One common trick is to make a C header file useable to a C++ header file by wrapping its contents with extern "C"
. It seems you attempted to do this, but without the __cplusplus
check.
#pragma once
/* A C header file made to work for C++ */
#ifdef __cplusplus
extern "C" {
#endif
/* rest C stuff ... */
#ifdef __cplusplus
}
#endif
A C compiler will not understand what extern "C"
is, hence it is hidden by the __cplusplus
check, which would be defined for a C++ compiler.
The library implementation of the variable argument functions should not be different when implemented in C++ (#include
ing the header file). The implementation would then call whatever C++ implementation code is required to actually complete what the call is supposed to do.