I have a logging macro that is made of multiple functions and i looks like this:
#define MY_LOG(s) { myMutex.lock(); std::cout << __PRETTY_FUNCTION__ << " (" << __LINE << ")" << s << std::endl; myMutex.unlock(); }
I want to be able to call the macro so it looks similar to calling a normal function. I want to have it beeing terminated by a semicolon.
It does work in most cases. Here is an example:
if (1 == 1)
MY_LOG("ok " << 1);
It works fine. No problem. But it does not work in this case:
if (1 == 1)
MY_LOG("1 == 1");
else
c ;
I get an error that there is an else without previous if. Is it possible to fix this?
CodePudding user response:
The reason that it works at all is that an extra ;
will just be ignored by the compiler in most cases. What you are writing is { ... };
.
Not so for if/else
. Consider this code:
if (cond1)
if (cond2) something();
else {
...
}
If the extra ;
would not close the inner if
then you would have to add else { }
. The designer of C/C went with using ;
as a shorter version of that.
Now to fix your macro you have to end in something that requires a ;
but had otherwise no effect. The solution to this is Why use apparently meaningless do-while and if-else statements in macros?
#define mymacro(parameter) do{/**/} while(0)
But the whole macro is obsolete in modern (since c 20) C with the introduction of std::source_location. Just make it an actual function:
void log(const std::string_view message,
const std::source_location location =
std::source_location::current())
{
std::lock_guard<std::mutex> lock(myMutex);
std::cout << "file: "
<< location.file_name() << "("
<< location.line() << ":"
<< location.column() << ") `"
<< location.function_name() << "`: "
<< message << '\n';
}
CodePudding user response:
The usual solution is to use do { ... } while(0)
like this:
#define MY_LOG(s) do { \
std::lock_guard<std::mutex> lock(myMutex); \
std::clog << __PRETTY_FUNCTION__ << " (" << __LINE__ << ")" << s << '\n'; \
} while(false)
Note the std::lock_guard
instead of manual locking.
But with a variadic macro, a variadic function template and C 20 std::source_location
you can make something far more versatile:
#include <iostream>
#include <mutex>
#include <sstream>
#include <source_location>
template<class... Args>
void my_log(const std::source_location loc, Args&&... args)
{
static std::mutex myMutex;
std::ostringstream os; // used to build the logging message
os << loc.function_name() << " (" << loc.line() << "):" << std::boolalpha;
// fold expression:
((os << ' ' << args), ...); // add all arguments to os
std::lock_guard<std::mutex> lock(myMutex); // use a lock_guard
std::clog << os.str(); // and stream
}
// this just forwards the source location and arguments to my_log:
#define MY_LOG(...) my_log(std::source_location::current(), __VA_ARGS__)
int main() {
MY_LOG("A", 1, 2, 'B');
}