What advantages does C++20's std::source_location have over the pre-defined macros __FILE__, __LINE__ and __FUNCTION__?

Viewed 1898

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.

3 Answers

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.

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.

Related