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.
__FILE__ This macro expands to the name of the current input file, in the form of a C string constant. This is the path by which the preprocessor opened the file, not the short name specified in ' #include ' or as the input file name argument. For example, "/usr/local/include/myheader.
__LINE__ is a preprocessor macro that expands to current line number in the source file, as an integer. __LINE__ is useful when generating log statements, error messages intended for programmers, when throwing exceptions, or when writing debugging code.
C++ preprocessor macro __cplusplus The __cplusplus preprocessor macro is defined if the compilation unit is compiled with a C++ compiler. Its value corresponds to the C++ standard that the compiler uses to compile a compilation unit.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With