C++20 std::source_location 완전 가이드
C++20 이전에는 파일명, 줄 번호, 함수명을 얻으려면 __FILE__, __LINE__, __func__ 매크로를 사용해야 했습니다. 이 매크로들은 타입 안전하지 않고 템플릿과 조합하기 어렵습니다. C++20의 std::source_location은 이를 표준화된 방법으로 해결합니다.
1. 기본 API
섹션 제목: “1. 기본 API”#include <source_location>#include <iostream>
void logLocation() { auto loc = std::source_location::current();
std::cout << "파일: " << loc.file_name() << "\n"; std::cout << "함수: " << loc.function_name() << "\n"; std::cout << "줄 번호: " << loc.line() << "\n"; std::cout << "열 번호: " << loc.column() << "\n";}| 멤버 함수 | 반환값 |
|---|---|
file_name() | 소스 파일 경로 (const char*) |
function_name() | 함수 이름 (시그니처 포함) |
line() | 줄 번호 (uint_least32_t) |
column() | 열 번호 (uint_least32_t) |
2. 매크로 대체
섹션 제목: “2. 매크로 대체”// 기존 매크로 방식#define LOG(msg) std::cout << "[" << __FILE__ << ":" << __LINE__ << "] " << msg << "\n"
// std::source_location 방식void log(std::string_view msg, std::source_location loc = std::source_location::current()) { std::cout << "[" << loc.file_name() << ":" << loc.line() << "] " << msg << "\n";}
// 호출 측 코드가 위치 정보를 자동으로 전달log("초기화 완료"); // [main.cpp:42] 초기화 완료std::source_location::current()를 기본 인수로 사용하면 호출 지점의 위치가 자동으로 캡처됩니다.
3. 로깅 시스템 구현
섹션 제목: “3. 로깅 시스템 구현”#include <source_location>#include <string_view>#include <iostream>#include <format>
enum class LogLevel { Debug, Info, Warn, Error };
class Logger {public: static void log( LogLevel level, std::string_view message, std::source_location loc = std::source_location::current()) { const char* levelStr = [level] { switch (level) { case LogLevel::Debug: return "DEBUG"; case LogLevel::Info: return "INFO "; case LogLevel::Warn: return "WARN "; case LogLevel::Error: return "ERROR"; } return "?????"; }();
std::cout << std::format("[{}] {}:{} ({}) — {}\n", levelStr, loc.file_name(), loc.line(), loc.function_name(), message); }};
void initialize() { Logger::log(LogLevel::Info, "시스템 초기화 시작"); // [INFO ] main.cpp:55 (void initialize()) — 시스템 초기화 시작}4. 커스텀 어서션
섹션 제목: “4. 커스텀 어서션”void assert_impl(bool condition, std::string_view message, std::source_location loc = std::source_location::current()) { if (!condition) { std::cerr << "Assertion failed: " << message << "\n" << " at " << loc.file_name() << ":" << loc.line() << " in " << loc.function_name() << "\n"; std::abort(); }}
#define ASSERT(cond, msg) assert_impl((cond), (msg))
void processData(int value) { ASSERT(value > 0, "값은 양수여야 합니다"); // Assertion failed: 값은 양수여야 합니다 // at process.cpp:42 in void processData(int)}5. 템플릿과 조합
섹션 제목: “5. 템플릿과 조합”기존 __FILE__, __LINE__ 매크로는 템플릿 함수에서 항상 호출 지점이 아닌 정의 지점을 가리킵니다. source_location은 기본 인수를 통해 올바른 호출 지점을 캡처합니다.
template<typename T>void logValue(const T& value, std::source_location loc = std::source_location::current()) { std::cout << loc.file_name() << ":" << loc.line() << " value=" << value << "\n";}
// 각각 다른 파일:줄 출력logValue(42);logValue(std::string("hello"));logValue(3.14);6. 추적 클래스 구현
섹션 제목: “6. 추적 클래스 구현”class CallTracer { std::source_location loc_; std::string funcName_;
public: explicit CallTracer( std::source_location loc = std::source_location::current()) : loc_(loc), funcName_(loc.function_name()) { std::cout << "[ENTER] " << funcName_ << "\n"; }
~CallTracer() { std::cout << "[EXIT] " << funcName_ << "\n"; }};
void complexFunction() { CallTracer tracer; // 함수 진입/종료 자동 로그 // 작업 수행}// [ENTER] void complexFunction()// [EXIT] void complexFunction()7. 예외 처리와 결합
섹션 제목: “7. 예외 처리와 결합”class AppException : public std::exception { std::string message_; std::source_location location_;
public: explicit AppException( std::string message, std::source_location loc = std::source_location::current()) : message_(std::move(message)), location_(loc) {}
const char* what() const noexcept override { return message_.c_str(); }
const std::source_location& where() const noexcept { return location_; }};
void riskyOperation() { throw AppException("데이터베이스 연결 실패");}
int main() { try { riskyOperation(); } catch (const AppException& e) { std::cerr << "오류: " << e.what() << "\n" << "위치: " << e.where().file_name() << ":" << e.where().line() << "\n"; }}8. FILE / LINE 매크로와 비교
섹션 제목: “8. FILE / LINE 매크로와 비교”| 특성 | __FILE__ / __LINE__ | std::source_location |
|---|---|---|
| 타입 | const char* / int | 타입 안전 구조체 |
| 템플릿 호환 | X (정의 지점 캡처) | O (호출 지점 캡처) |
| 컴파일러 확장 | 비표준 매크로 | C++20 표준 |
| 함수명 | __func__ (비표준) | function_name() |
| 열 번호 | 비표준 | column() |
9. 실전 주의사항
섹션 제목: “9. 실전 주의사항”// 주의: 함수 객체나 람다에서의 function_name()은 구현체마다 다름auto lambda = [] { auto loc = std::source_location::current(); std::cout << loc.function_name() << "\n"; // GCC: "main()::<lambda>" // MSVC: "__cdecl main::<lambda_...>"};
// 주의: current()는 반드시 기본 인수 위치에서 사용해야 함// 함수 본문 내부에서 호출하면 함수 내부 위치가 캡처됨void badUsage(std::source_location loc) { // 기본 인수 없음 // loc는 항상 badUsage 호출 측의 정보가 아님}std::source_location::current()를 기본 인수로 사용하면 호출 지점 정보 자동 캡처__FILE__,__LINE__,__func__매크로를 타입 안전하게 대체- 로깅, 어서션, 예외, 트레이싱에 활용
- 템플릿 함수에서도 올바른 호출 지점을 캡처