SimplifyC++ Article
std::source_location in Modern C++: A Better Way to Build Debugging, Logging, and Diagnostics Systems
std::source_location in Modern C++: A Better Way to Build Debugging, Logging, and Diagnostics Systems
In low-level and high-performance C++ systems, one of the most persistent challenges is obtaining accurate, maintainable, and low-overhead diagnostic information.
For decades, developers relied on macros such as:
__FILE____LINE____FUNCTION__While effective, these approaches suffer from serious limitations:
They rely on macros (not type-safe)
They pollute interfaces
They are difficult to integrate into modern abstractions
They lack composability
With C++20, the language introduces a powerful and elegant solution:
std::source_location— a zero-overhead, type-safe mechanism for capturing call-site information
What is std::source_location?
std::source_location is a lightweight object that captures:
File name
Line number
Column number
Function name
It is defined in:
The key capability lies in:
std::source_location::current()This function retrieves information about the call site, not where it is written.
Basic Example
void log(const std::source_location& loc = std::source_location::current()) { std::cout << "File: " << loc.file_name() << "\n" << "Line: " << loc.line() << "\n" << "Function: " << loc.function_name() << "\n";}
int main() { log();}Output (example)
File: main.cppLine: 12Function: mainWhy std::source_location is Important
1. Eliminates Macros
Old approach:
Modern approach:
void log(std::string_view msg, const std::source_location& loc = std::source_location::current());No macros. Cleaner. Safer.
2. Call-Site Transparency
The most powerful feature is:
The default argument is evaluated at the call site, not at the function definition.
This enables writing reusable utilities without losing context.
3. Zero Runtime Overhead
No heap allocation
Typically optimized as compile-time constants
Comparable cost to macros
This makes it suitable even for:
real-time systems
embedded systems
high-frequency logging
Practical Usage Patterns
1. Building a Modern Logging System
void log_info(std::string_view message, const std::source_location& loc = std::source_location::current()) { std::cout << "[INFO] " << loc.file_name() << ":" << loc.line() << " (" << loc.function_name() << ") " << message << "\n";}Usage:
log_info("Server started");2. Error Handling Without Macros
[[noreturn]]void fail(std::string_view msg, const std::source_location& loc = std::source_location::current()) { throw std::runtime_error( std::string(msg) + " at " + loc.file_name() + ":" + std::to_string(loc.line()));}3. Assertions System
void assert_true(bool condition, const std::source_location& loc = std::source_location::current()) { if (!condition) { std::cerr << "Assertion failed at " << loc.file_name() << ":" << loc.line() << "\n"; std::abort(); }}4. Integration with High-Performance Backends
In systems like:
Boost.Asio servers
trading engines
distributed systems
You can attach source_location to:
log entries
tracing events
metrics systems
without affecting performance.
Advanced Usage (Important for Experts)
Passing Explicit Source Location
Sometimes you want to propagate location manually:
void wrapper(const std::source_location& loc = std::source_location::current()) { log_info("Wrapped call", loc);}Storing source_location
You can embed it into structures:
struct ErrorContext { std::string message; std::source_location location;};Combining with std::expected
std::expected<int, ErrorContext> parse() { return std::unexpected(ErrorContext{ "parse error", std::source_location::current() });}This creates rich error propagation without exceptions.
Best Practices for Efficient Usage
1. Always Use Default Parameter Pattern
const std::source_location& loc = std::source_location::current()This ensures:
correct call-site capture
zero additional effort for callers
2. Avoid Manual Passing Unless Necessary
Only pass explicitly when forwarding context.
3. Do Not Overuse in Tight Loops
Even though it is lightweight:
avoid excessive logging in hot paths
use conditional logging
4. Combine with Compile-Time Logging
Use with:
constexprflagscompile-time log levels
to eliminate overhead entirely in release builds.
5. Replace Legacy Macros Gradually
You can migrate:
From:
To:
inline void log_error(std::string_view msg, const std::source_location& loc = std::source_location::current());Comparison with Other Languages
Rust: uses
file!(),line!()macrosC++: now provides a type-safe alternative without macros
This aligns C++ with modern language design principles while preserving performance.
Final Insight
std::source_location is not just a small utility feature.
It represents a design shift in Modern C++:
Moving from macro-based debugging to type-safe, composable diagnostics.
For advanced developers building:
compilers
backends
system tools
performance-critical applications
this feature becomes a foundational building block.
Closing Thought
The difference between basic logging and professional-grade diagnostics systems often lies in details like this.
And std::source_location is one of those details that quietly transforms your entire architecture.