LOG4CXX_INFO clean wrapper that is not a #define - c++

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.

Related

Emscripten: how to disable warning: explicit specialization cannot have a storage class

I am building my program by using the latest Emscripten compiler.
It is based on Clang version 14. Actually it is a small test program which is the following:
#include <iostream>
struct Test {
template<typename T>
static inline void Dump(const T& value) {
std::cout << "[generic] = '" << value << "'\n";
}
template<>
static inline void Dump<std::string>(const std::string& value) {
std::cout << "[std::string] = '" << value << "'\n";
}
};
int main() {
std::string text = "hello";
Test::Dump(text);
return 0;
}
When I build it by Emscripten compiler I got the warning:
D:\em_test>emcc a.cpp
a.cpp:10:24: warning: explicit specialization cannot have a storage class
static inline void Dump<std::string>(const std::string& value) {
~~~~~~~ ^
1 warning generated.
If I just remove static keyword from void Dump<std::string> line
then there will be no warning. However, this code will cause compilation error in Visual Studio:
D:\em_test\a.cpp(17,11): error C2352: 'Test::Dump': illegal call of non-static member function
But this error is expected and clear.
I would like to write a cross-platform program.
So, I think I should simple disable this warning in Emscripten.
However, I can not find any Emscripten (which is based on clang version 14)
command line option for that!
And I am asking advice for that.
Actually I tried to use -Wno-static-inline-explicit-instantiation command line option but it did not help:
D:\em_test>emcc -Wno-static-inline-explicit-instantiation a.cpp
a.cpp:10:24: warning: explicit specialization cannot have a storage class
static inline void Dump<std::string>(const std::string& value) {
~~~~~~~ ^
1 warning generated.
However, I see in Clang version 13 user manual description about -Wstatic-inline-explicit-instantiation option but it is about a slightly another warning text.
Also it seems that Clang version 14 is not fully released, so, there is no public Clang version 14 user manual.
I can not find any Emscripten or Clang command line option to disable the above warning.
Could somebody help me?
Explicit specialization of (both static and non-static) function templates cannot be put into class definitions.
Just put it into the enclosing namespace(i.e somewhere after the class):
#include <iostream>
struct Test {
template <typename T>
static inline void Dump(const T& value) {
std::cout << "[generic] = '" << value << "'\n";
}
};
// Notice Test::
template <>
inline void Test::Dump<std::string>(const std::string& value) {
std::cout << "[std::string] = '" << value << "'\n";
}
int main() {
std::string text = "hello";
Test::Dump(text);
return 0;
}
inline is never necessary for in-class function definitions but it has different meaning for member variables.
inline for out-class is necessary in header files because the explicit specialization is not a template anymore.

Overloading C Preprocessor Macros - Discimination Based on Call Syntax

I'm currently working on a cpp logger which aims at displaying the __FILE__ and the __LINE__ before each printed message. In my case, we are mostly using 2 methods for printing out: printf-style and std::cout-style. For the moment I have a macros for each style:
#define HATFormatFatal(...) HATLogger::logFormat(HATLogger::LogLevel::FATAL, __FILE__, __LINE__, __VA_ARGS__)
#define HATFormatError(...) HATLogger::logFormat(HATLogger::LogLevel::ERROR, __FILE__, __LINE__, __VA_ARGS__)
etc... and:
#define HATStreamFatal HATLogger::logStream(HATLogger::LogLevel::FATAL, __FILE__, __LINE__)
#define HATStreamError HATLogger::logStream(HATLogger::LogLevel::ERROR, __FILE__, __LINE__)
These macros can be called in the following:
HATFormatError("This is an %s message", "ERROR");
HATStreamError << "This is an " << "ERROR" << " message" << std::endl;
I would like to call them with the same name: HATLogError. The right macro would be determine at compilation while looking for parenthesis. So far I've seen some examples showing how it is possible to discriminate the macros by the number of arguments, but nothing that could handle a "non-parenthesis" case.
Does anyone have any idea on how this could be achieved ?
The simplest approach would be not overloading the macro at all, but instead having the macro return an object that has both operator<< and operator() overloaded. Something like this:
class error_logger {
public:
error_logger(
HATLogger::LogLevel level,
char const * file,
char const * line
) : level{level}, file{file}, line{line} { }
template <typename... T>
void operator()(T && ... args) {
HATLogger::logFormat(level, file, line, std::forward<T>(args)...);
}
template <typename T>
HATLogger::logStream operator<<(T && arg) {
HATLogger::logStream stream{level, file, line};
stream << std::forward<T>(arg);
return stream;
}
private:
HATLogger::LogLevel level;
char const * file;
char const * line;
};
(This example assumes HATLogger::logStream can be moved. Adjustments to this example implementation may need to be made based on the details of your code, but the basic approach is what I'm demonstrating here.)
Now you could do:
#define HATFormatFatal (error_logger{HATLogger::LogLevel::FATAL, __FILE__, __LINE__})
And then both HATFormatFatal << ... and HATFormatFatal(...) can be used.

Preprocessor, expand macro to nothing when undefined

The preprocessor has always been black magic to me but I think I finally need to use it.
I have implemented a logger class which I want to conditionally (compile flag) expand to nothing if the flag is not set so that I don't get all my prints in production.
This would have a usecase like so
FO_LOG << name() << "Hello World" << std::endl;
I thought I could define it like this
#ifdef TRACE
#define FO_LOG {return Faceoff::trace::log();}
#else
#define FO_LOG \
if(false){\
return Faceoff::trace::log();\
}
#endif
But this won't compile with the following errors
no viable conversion from returned value of type 'Faceoff::trace' to function return type 'int'
FO_LOG << name() << "omitted" << std::endl;
^~~~~~
/omitted/include/globals.h:69:16: note: expanded from macro 'FO_LOG'
return Faceoff::trace::log();\
^~~~~~~~~~~~~~~~~~~~~
Now the error is clear, but I don't know how to express my intention in preprocessor syntax...
Here you go:
class devnull : public std::ostream {
class devnullbuff : public std::streambuf {
public:
int overflow( int c ) { return c; }
} m_nb;
public:
devnull() : std::ostream( &m_nb ) {}
};
#ifdef TRACE
#define FO_LOG Faceoff::trace::log()
#else
#define FO_LOG devnull()
#endif
Assuming Faceoff::trace::log() returns some sort of ostream, then you can do what you want:
FO_LOG << name() << "Hello World" << std::endl;
This is not quite efficient because it creates a new devnull object every time. You can just create it once in your program and forward declare to it to avoid this. It's not as elegant though.

Can #define preprocessor directive contain if and else?

I was trying the logger code from this link, but it gives me error. How to implement a good debug/logging feature in a project
#ifndef _LOGGER_HPP_
#define _LOGGER_HPP_
#include <iostream>
#include <sstream>
/* consider adding boost thread id since we'll want to know whose writting and
* won't want to repeat it for every single call */
/* consider adding policy class to allow users to redirect logging to specific
* files via the command line
*/
enum loglevel_e
{logERROR, logWARNING, logINFO, logDEBUG, logDEBUG1, logDEBUG2, logDEBUG3, logDEBUG4};
class logIt
{
public:
logIt(loglevel_e _loglevel = logERROR) {
_buffer << _loglevel << " :"
<< std::string(
_loglevel > logDEBUG
? (_loglevel - logDEBUG) * 4
: 1
, ' ');
}
template <typename T>
logIt & operator<<(T const & value)
{
_buffer << value;
return *this;
}
~logIt()
{
_buffer << std::endl;
// This is atomic according to the POSIX standard
// http://www.gnu.org/s/libc/manual/html_node/Streams-and-Threads.html
std::cerr << _buffer.str();
}
private:
std::ostringstream _buffer;
};
extern loglevel_e loglevel;
#define log(level) \
if (level > loglevel) ; \
else logIt(level)
#endif
More precisely, this #define is giving errors:
#define log(level) \
if (level > loglevel) ; \
else logIt(level)
The errors are Syntax error: if and Syntax error: else
But later, I noticed that if I move #include "logger.hpp" from main.h to main.cpp, the problem disappeared. Though 'main.h' is included many times in different places, it does contain '#pragma once'.
Any idea?
If loglevel is known at compile time you can do the following:
template <bool>
struct LogSystem
{
template <class T>
LogSystem& operator << (const T &)
{
//ignore the input
return (*this);
}
};
template <>
struct LogSystem <true>
{
template <class T>
LogSystem& operator << (const T & v)
{
cout << v;
return (*this);
}
};
template <bool B>
LogSystem<B> getLog()
{
return LogSystem<B>();
}
#define log(level) getLog< (level <= loglevel) >()
if loglevel is not known at compile time:
class iLogSystem
{
public:
virtual iLogSystem& operator << (const int &)
{
//empty
return (*this);
}
virtual iLogSystem& operator << (const custom_type &);
{
return (*this);
}
//make functions for logging all the types you want
};
class LogSystem : public iLogSystem
{
public:
virtual iLogSystem& operator << (const int & v)
{
cout << v;
return (*this);
}
virtual iLogSystem& operator << (const custom_type & q);
{
cout << q.toString();
return (*this);
}
//make functions for logging all the types you want
};
iLogSystem& getLog(const bool l)
{
static LogSystem actual_log;
static iLogSystem empty_log;
if(l)
return &actual_log;
return &empty_log;
}
#define log(level) getLog( level <= loglevel )
Any time you want to define a macro that expands to a statement, if the definition contains any compound statements (including if/else), you should wrap the definition in do ... while (0). The enclosed code will still execute exactly once, and it can be used in any context that requires a statement.
That's the only way I know of to avoid syntax errors when the macro is used within an if/else statement, due to the use of semicolons.
So rather that this:
#define log(level) \
if ((level) > loglevel) ; \
else logIt(level)
you can use this:
#define log(level) \
do { \
if ((level) > loglevel) ; \
else logIt(level) \
} while (0)
I've added parentheses around references to the macro's level parameter, to avoid any possible operator precedence problems. Note also the lack of a semicolon at the end; the semicolon will be supplied by the caller.
On the other hand, an if/else can often be replaced by the conditional (ternary) ?: operator:
#define log(level) \
((level) > loglevel ? 0 : logIt(level))
which allows log(level) to be used anywhere an expression can be used; that includes statement context if you add a semicolon. You might want to replace 0 by something of the type returned by logIt; if logIt is a void function, you might want:
#define log(level) \
((level) > loglevel ? (void)0 : logIt(level))
This all assumes that a macro is the right tool for the job. It's likely that a template (as suggested by this answer) or an inline function will do the job better and with less potential for confusion.
Can #define preprocessor directive contain if and else?
Yes.
Regarding your problem: preprocessor is dumb as a rock and performs only simple text subsitution. It is not a function, it is not a language construct, it is simple, dumb text subsitution. As a a result, this:
#define log(level) \
if (level > loglevel) ; \
else logIt(level)
...
log(logINFO) << "foo " << "bar " << "baz";
Turns into this:
if (logINFO > loglevel); // << here's your problem.
else
logIt(logInfo)
<< "foo " << "bar " << "baz";
Your problem is;. Here, semicolon indicates end of c++ if statement, so when compiler encounters else afterwards, it doesn't know what to do with it.
I noticed that if I move #include "logger.hpp" from main.h to main.cpp, the problem disappeared
C++ has "logarithm" function. Which is called log. If your other files use logarithm function, things will get very interesting, because it'll be replaced by your if/else logging code everywhere.
For example, if there's inlined logarithm code somewhere in a header, it'll turn into nonsense if you include your logger header first. For example log(6.0) + 1 will turn into log (if (6.0 > logLevel); else logIt(6.0)) + 1, which is not a valid C++ statement.

Template Member Function Overloading and Multiple Inheritance in C++

I am observing behavior in the below code which I cannot readily explain and would like to understand the theory of better. I cannot seem to find an online documentation source or existing question which covers this particular situation. For reference, I am using Visual Studio C++ 2010 to compile and run the following code:
#include <iostream>
using namespace std;
struct Bottom_Class
{
template<typename This_Type>
void Dispatch()
{
// A: When this comment is removed, the program does not compile
// citing an ambiguous call to Print_Hello
// ((This_Type*)this)->Print_Hello();
// B: When this comment is removed instead, the program compiles and
// generates the following output:
// >> "Goodbye from Top Class!"
// ((This_Type*)this)->Print_Goodbye<void>();
}
void Print_Hello() {cout << "Hello from Bottom Class!" << endl;}
template<typename This_Type>
void Print_Goodbye() {cout << "Goodbye from Bottom Class!" << endl;}
};
struct Top_Class
{
void Print_Hello() {cout << "Hello from Top Class!" << endl;}
template<typename This_Type>
void Print_Goodbye() {cout << "Goodbye from Top Class!" << endl;}
};
template<typename Top_Type,typename Bottom_Type>
struct Merged_Class : public Top_Type, public Bottom_Type {};
typedef Merged_Class<Top_Class,Bottom_Class> My_Merged_Class;
void main()
{
My_Merged_Class my_merged_object;
my_merged_object.Dispatch<My_Merged_Class>();
}
Why does this work differently for the templated member function vs. non-templated member function cases ?
How does the compiler decide (in the templated case) that Top_Class::Print_Goodbye() is the appropriate overload rather than Bottom_Class::Print_Goodbye() ?
Thank you in advance for your consideration.
Both comments (AFAIK correctly) generate compilation error with GCC 4.6.3. May be the Microsoft compiler is doing something incorrect.
➜ scratch g++ -O2 templ.cc
templ.cc: In member function ‘void Bottom_Class::Dispatch() [with This_Type = Merged_Class<Top_Class, Bottom_Class>]’:
templ.cc:42:48: instantiated from here
templ.cc:16:9: error: request for member ‘Print_Goodbye’ is ambiguous
templ.cc:22:10: error: candidates are: template<class This_Type> void Bottom_Class::Print_Goodbye()
templ.cc:30:10: error: template<class This_Type> void Top_Class::Print_Goodbye()
In the Dispatch method, This_Type is the same as My_Merged_Class. The My_Merged_Class has two methods with the names of Print_Hello, of course the compiler is going to have problems to distinguish between them.
The call to Print_Hello in Dispatch, after template replacement, looks like this:
((My_Merged_Class*)this)->Print_Hello();
I hope the above substitution helps you see better why there is an ambiguity. The same problem should actually occur for Print_Goodbye, but it might be a bug in the compiler you are using that lets it through.