I am trying to wrap a #define macro in C++ into an inline or template function. In LOG4CXX it has a define such as:
#define LOG4CXX_INFO(logger, message) { \
if (logger->isInfoEnabled()) {\
::log4cxx::helpers::MessageBuffer oss_; \
logger->forcedLog(::log4cxx::Level::getInfo(), oss_.str(oss_ << message), LOG4CXX_LOCATION); }}
I am needing to add other context to the logs and have wrapped LOG4CXX_INFO in another define and have this implementation working. I am wanting to move away from using a #define to wrap this and do it in a more C++ way. This issue I am running into is that the message param can be of various types of objects concated together using + or << operators and this is currently handled in the MessageBuffer class with operator overrides.
If I try to template it such as:
template<typename T>
void info(LoggerPtr logger, const T& message) {
...do stuff ...
LOG4CXX_INFO(logger, message);
}
The compiler will complain about not finding << operators for various types of objects.
What needs to be done to make this work?
Compiled with the following on ubuntu
gcc main.cpp -lstdc++ -llog4cxx
#include <log4cxx/logger.h>
using namespace std;
#define info(logger, message) LOG4CXX_INFO(logger, message)
template<typename T> void tinfo(log4cxx::LoggerPtr logger, const T& message) {
LOG4CXX_INFO(logger, message);
}
int main()
{
log4cxx::LoggerPtr logger;
LOG4CXX_INFO(logger, "test" << "test2");
info(logger,"another test");
info(logger,"another test" << "another test2");
tinfo(logger,"test template");
//tinfo(logger,"test template" << "test template 2"); -> this line does not compile
return 0;
}
Compiler error:
main.cpp: In function ‘int main()’:
main.cpp:21:31: error: invalid operands of types ‘const char [14]’ and ‘const char [16]’ to binary ‘operator<<’
tinfo(logger,"test template" << "test template 2");
MessageBuffer code can be found here: https://github.com/apache/logging-log4cxx/blob/master/src/main/include/log4cxx/helpers/messagebuffer.h and here https://github.com/apache/logging-log4cxx/blob/master/src/main/cpp/messagebuffer.cpp
tinfo(logger,"test template" << "test template 2");
is wrong. It first evaluates "test template" << "test template 2"
and then passes the result to the function. But that makes no sense, since the left-hand side of <<
should be a stream object.
This works with the macro, because macros are pure text substitution, so the substitution
LOG4CXX_INFO(logger, "test" << "test2");
yields
oss_ << "test" << "test2"
which is evaluated left-to-right. If you add parentheses around the macro argument
LOG4CXX_INFO(logger, ("test" << "test2"));
you reproduce the function behavior faithfully and you will get the same error because you would be evaluating
oss_ << ("test" << "test2")
If you want to output multiple things in one function call I suggest using a fold expression (available since C++17):
template<typename... Ts>
void tinfo(log4cxx::LoggerPtr logger, const Ts&... messages) {
if (logger->isInfoEnabled()) {
::log4cxx::helpers::MessageBuffer oss_;
logger->forcedLog(::log4cxx::Level::getInfo(), oss_.str((oss_ << ... << messages)), LOG4CXX_LOCATION);
}
}
tinfo(logger, "test template", "test template 2");
If you want to keep the original syntax and/or refer to LOG4CXX_INFO
instead of reimplementing the body of that macro, then I don't think it is possible without macros.