I'm writing logger file. I'd prefer to use macros __FUNCTION__ there. I don't like the way like:
Logger.write("Message", __FUNCTION__);
Maybe, it's possible to make something like:
void write(const string &message, string functionName = __FUNCTION__)
{
// ...
}
If not, are there any ways to do that stuff not by hands (I mean passing function name)?
You could do something like that by wrapping it all in a macro:
#define log(msg) Logger.write(msg, __FUNCTION__)
The downside is that you will need to have Logger in scope when using this macro.
You can finally do it without macros magic in c++20:
void write(std::string_view message, const std::source_location& location =
std::source_location::current()) {
std::cout << "Message= " << message
<< ", Function=" << location.function_name();
}
int main() {
write("Hello world!");
}
Macros work just by text substitution - the preprocessor puts the macro definition in place of its name. You can't have "intelligent" macros like the one you suggest.
There is no support for what you want in standard C++.
Related
In my current project, I have used:
Log(__LINE__, __FUNCTION__, message);
But the new C++20 utility class std::source_location
comes with functions line(), column(), file_name(), function_name(), which do the same thing. So, the new C++20 way would be:
log(std::string message,const std::source_location& location = std::source_location::current())
{
std::cout << "Debug:"
<< location.file_name() << ':'
<< location.line() << ' '
<< message << '\n';
}
What is the advantage of the new, C++20 way over the old standard double-underscore macros, __LINE__, __FILE__, __func__, which have already been standard C++ for a long time?
I'm trying to decide if the advantage(s) are so great as to justify modifying the code in my current projects to use the new std::source_location object in preference to the macros.
Your example is the best motivation I can think of. In the past __LINE__ and _FILE__ were the best excuse to use macros for debug logs. Now this does not hold anymore.
In the past you had basically two choiches. Either let the caller pass info on where the logging happens:
log(__LINE__,__FILE__,"Hello World");
or use a macro to get same output via
log("Hello World");
//^--- log must be a macro if we want this __LINE__ in the log
Both is very inconvenient, because either the user has to pass parameters that have to be the same anyhow on each call, or we need to use a macro with all the known downsides. With source_location we can get log("Hello World"); without any macros involved, but still include line and file of the call, because default parameters are substituted at the call site. For example:
#include <source_location>
#include <string>
#include <iostream>
void log(std::string message,const std::source_location& location = std::source_location::current())
{
std::cout << "Debug:"
<< location.file_name() << ':'
<< location.line() << ' '
<< message << '\n';
}
int main() {
log("hello");
log("world");
}
Output:
Debug:/app/example.cpp:15 hello
Debug:/app/example.cpp:16 world
Pre-C++20 you had to choose between being verbose (passing __LINE__, __FILE__, __func__ to each call manually) and using a macro to do that for you.
std::source_location gives you a nice calling syntax without a macro. There are no other hidden advantages.
Note that both Clang, GCC, and MSVC have the same non-standard extension that lets you do it pre-C++20: __builtin_LINE(), __builtin_FILE(), and __builtin_FUNCTION() that can be used as default arguments to achieve the same effect. You should be able to write your own class source_location using them as well.
The main advantage of std::source_location is mostly convenience, because it represents a source location with a single, easily storable structure.
It has also the advantage of not being based on preprocessor magic, so for the language it is not just an integer literal but a valid type, which is much nicer from the point of view of type-safety.
If you are fine with the older approach, there's no reason to change good, already working code. This is generally true for most new features though, not only for std::source_location.
I want to concate a string and a enumerate name into string format. And then use them as input into some function. For example:
define
// The following line doesn't work
#define CONCAT_STR_TYPE(str, type) str ## type
enum EnumA{TEST=1};
void SomeFunction(const std::string &str)
{
std::cout << str << std::endl;
}
call
SomeFunction(CONCAT_STR_TYPE("test_", EnumA));
Result
test_EnumA
The codes above don't work. I wander if there is a way to do so? If there is any way, please kindly show me.
I figure it out. Define macro as following:
#define CONCAT_STR_TYPE(str, type) str#type
Besides, this is not related to enumerate type. It works for other types.
I wrote my own logger that I can use as follows:
LOG("This is the number five: " << 5 << ".");
I'd like to write an error checking method that simply takes a return code, checks if it's an error, and if so prints an error message
Thus, I'd like to turn:
if(result == -1)
{
LOG("ERROR! We got a result of: " << result << ".");
// Do some other stuff
}
into:
checkForError(result, "ERROR! We got a result of: " << result << ".");
void checkForError(int result, SomeStringType message)
{
if(result == -1)
{
LOG(message);
// Do some other stuff
}
}
is this possible? I've tried passing it as a char*, a std:string, and as a stringstream, but I can't seem to get it to work correctly
Is there any way to do this so I'm simply concatenating the error message in the call to the function?
Yes, it's possible. You could do it like this:
#define LOG(X) \
do { \
std::ostringstream log_stream; \
log_stream << X; \
LOG::log_internal(log_stream.str()); \
} while (0)
where LOG::log_internal might be declared in your logger header like
struct LOG {
static void log_internal(const std::string &);
};
The main drawback of this is that it's not the most hygienic macro ever -- if the argument X contains semicolons, then you could have additional statements executed inside the macro's scope, and there won't be a compiler error. This macro is far from cryptic though in my opinion, and fortunately you don't actually have to worry about an attacker performing SQL-injection-esque attacks against your macros.
I use loggers that basically look like this in my projects, and I learned to do it this way from other projects. (Actually the one I use is a little more developed, it also passes __FILE__ and __LINE__ to the LOG::log_internal function, and there's log channels and log levels which may or may not be active etc. etc...)
I've been developing application which uses logging mechanism, and I implemented sort of printf function which takes various number of arguments and prints to log certain message, what I want is to add a function name to this function, but I dont what to write this argument in each function call.
So I decided to write a macros, turns out it not so easy
#define WriteToLogParams(szMessage, nLogLevel, param1, param2) WriteToLogParamsFunc(szMessage, __FUNCDNAME__, nLogLevel, param1, param2)
First I thought that there is some kind of macros overloading and I can easily do it , but turns out if I write another macros with the same name but with different number of argument it wont compile. So to make it work I should make each macros name unique.
So is there are any intelligent way to do this?
Thank you on advance.
You can use __VA_ARGS__ macro
for example:
WriteToLogParamsFunc(const char *__file, int __line, const char* __func, int nLogLevel, const char *szMessage, ...);
#define WriteToLogParams(nLogLevel, szMessage, ...) WriteToLogParamsFunc(__FILE__, __LINE__, __FUNCTION__, nLogLevel, szMessage, __VA_ARGS__ )
I believe there are some preprocessors that support variadic macros but I'm not sure if it's a standard and there definately are compilers that don't support it so it's not portable.
However, instead of trying to have a variable number of parameters, have only one parameter. This is what I have done:-
#define WriteToLogParams(args) WriteToLogParamsObject::Instance (__FUNCDNAME__) << args
where the WriteToLogParamsObject is a class that is created by the static member Instance and has overloaded streaming operators. This gives you the advantages that using these operators has, such as putting streaming overloads into classes:-
class SomeClass
{
static friend WriteToLogParamsObject &operator << (WriteToLogParamsObject &stream, const SomeClass &item_to_log)
{
stream << "member1 = " << item_to_log.m_member1 << ", member2 = " << item_to_log.m_member2;
// and so on (syntax might be off)
return stream;
}
};
And to use the macro:-
WriteToLogParams ("some message " << some_value << " another bit of text " << another_value << " and so on");
In my program I want to use asserts that show an error message. Apart from the well known workarounds for C and C++ there's the "real" solution as BOOST offers BOOST_ASSERT_MSG( expr, msg ) (see also assert() with message)
But a static message isn't enough for me, I also want to show sometimes the failed variables, e.g. in a case like
BOOST_ASSERT_MSG( length >= 0, "No positive length found! It is " << length )
As you can see I'd like to format the message "string" as an stringstream or ostream as that'd allow me to easily show custom types (assuming I've defined the relevant formating function).
The problem here is that BOOST_ASSERT_MSG is by default requiring a char const * so that's not compatible.
Is there a way to redefine / overload assertion_failed_msg() in such a way that using a stream as message will work? How?
(My naive approach failed as the compiler first wanted to do an operator<<("foo",bar) on the message itself...)
You could define your own macro
#define ASSERT_WITH_MSG(cond, msg) do \
{ if (!(cond)) { std::ostringstream str; str << msg; std::cerr << str.str(); std::abort(); } \
} while(0)
It's relatively trivial to achieve this.
BOOST_ASSERT_MSG( length >= 0, (std::stringstream() << "No positive length found! It is " << length).str().c_str() )
Here is a solution that doesn't rely on macros. Instead, it uses a tiny bit of templating and lambda syntax.
template<typename Fn>
void assert_fn( bool expr, Fn fn) {
if (!expr) {
fn();
abort();
}
}
The argument fn can be any callable.
For instance you can call it like so:
assert_fn( a==b, [&](){ cout << "Assertion failed: a="<< a <<
" is different from but b=" << b << endl; } );
The advantage is that the output is that you are not calling abort explicitly and the output is fully customizable. The this advantage, of course, are the seven extra characters of lambda function boilerplate: [&](){} )
I use the BOOST_ASSERT_MSG with my own wrapper around it, so that specifying the assert message with multiple operator<< seems less complex.
#if defined ASSERT_ENABLED
#define ASSERT(cond, msg) {\
if(!(cond))\
{\
std::stringstream str;\
str << msg;\
BOOST_ASSERT_MSG(cond, str.str().c_str());\
}\
}
#else
#define ASSERT(...)
#endif
usage example, provide custom message like you are outputting to cout:
ASSERT(execSize == (_oldSize - remaining), "execSize : " << execSize << ", _oldSize : " << _oldSize << ", remaining : " << remaining);
What it does is, if ASSERT_ENABLED is defined,enable the assertion messages. if(!(cond)) part is optimization, which avoids the costly string operations specified by macro parameter msg, if cond is true
If you are working on Windows only, you can take a look to assert macro. Under the hood it uses _wassert. You can write your own assert macro using it. For instance in my case if I get some point, I want to show assert without conditions:
#ifdef DEBUG
const std::wstring assert_msg = /* build the string here */;
_wassert(assert_msg.c_str(), _CRT_WIDE(__FILE__), (unsigned)(__LINE__));
#endif
I think on other OS you can do the same trick, just take look at assert macro.