C++20 std::format — 타입 안전 문자열 포매팅
std::format은 C++20에서 도입된 타입 안전 문자열 포매팅 라이브러리입니다. Python의 str.format()에서 영감을 받았으며, printf의 간결함과 iostream의 타입 안전성을 결합합니다.
1. 기본 사용법
섹션 제목: “1. 기본 사용법”#include <format>#include <string>#include <iostream>
int main() { // 기본 포매팅 std::string s = std::format("Hello, {}!", "world"); std::cout << s << '\n'; // Hello, world!
// 인덱스 기반 std::string s2 = std::format("{0} + {1} = {2}", 1, 2, 3); std::cout << s2 << '\n'; // 1 + 2 = 3}2. 포맷 지정자
섹션 제목: “2. 포맷 지정자”#include <format>#include <numbers>
// 정수std::format("{:d}", 42); // "42"std::format("{:08d}", 42); // "00000042"std::format("{:#x}", 255); // "0xff"std::format("{:#b}", 10); // "0b1010"
// 부동소수점std::format("{:.3f}", 3.14159); // "3.142"std::format("{:e}", 1234.5); // "1.234500e+03"std::format("{:g}", 0.0001); // "0.0001"
// 문자열 정렬std::format("{:>10}", "right"); // " right"std::format("{:<10}", "left"); // "left "std::format("{:^10}", "center"); // " center "std::format("{:*^10}", "hi"); // "****hi****"3. std::print / std::println (C++23)
섹션 제목: “3. std::print / std::println (C++23)”#include <print>
std::print("값: {}\n", 42); // 출력 후 개행 없음std::println("값: {}", 42); // 출력 후 자동 개행std::println(stderr, "오류: {}", "실패"); // stderr에 출력4. 커스텀 포매터 구현
섹션 제목: “4. 커스텀 포매터 구현”#include <format>
struct Color { uint8_t r, g, b; };
template<>struct std::formatter<Color> { // 포맷 지정자 파싱 (없으면 기본) constexpr auto parse(std::format_parse_context& ctx) { return ctx.begin(); }
// 실제 포매팅 auto format(const Color& c, std::format_context& ctx) const { return std::format_to(ctx.out(), "#{:02X}{:02X}{:02X}", c.r, c.g, c.b); }};
int main() { Color red{255, 0, 0}; std::string s = std::format("색상: {}", red); // "색상: #FF0000"}5. 동적 포맷 문자열 — std::vformat
섹션 제목: “5. 동적 포맷 문자열 — std::vformat”#include <format>
void log(std::string_view fmt, std::format_args args) { std::string msg = std::vformat(fmt, args); // 로그 처리...}
// 호출log("{}: {}", std::make_format_args("level", 42));6. 성능 비교
섹션 제목: “6. 성능 비교”| 방식 | 타입 안전 | 성능 | 가독성 |
|---|---|---|---|
printf | ✗ | 빠름 | 중간 |
stringstream | ✓ | 느림 | 낮음 |
std::format | ✓ | 빠름 | 높음 |
{fmt} 라이브러리 | ✓ | 매우 빠름 | 높음 |
std::format은 {fmt} 라이브러리를 표준화한 것으로, 컴파일러 최적화를 통해 printf에 근접한 성능을 냅니다.
7. 동적 너비/정밀도 지정
섹션 제목: “7. 동적 너비/정밀도 지정”#include <format>
// 런타임에 너비와 정밀도 지정int width = 10;int precision = 3;double value = 3.14159;
std::string s = std::format("{:{}.{}f}", value, width, precision);// " 3.142" (너비 10, 소수점 3자리)
// 런타임 채움 문자는 불가 — 너비/정밀도만 동적 지정 가능std::string aligned = std::format("{:>{}}", "hi", width); // " hi"8. format_to — 버퍼에 직접 쓰기
섹션 제목: “8. format_to — 버퍼에 직접 쓰기”#include <format>#include <iterator>#include <string>
// 기존 string에 이어 쓰기std::string output;output.reserve(256);
std::format_to(std::back_inserter(output), "Hello, {}!\n", "World");std::format_to(std::back_inserter(output), "Value: {:.2f}\n", 3.14159);
// 고정 버퍼에 쓰기 (오버플로 방지)char buf[64];auto [ptr, ec] = std::format_to_n(buf, sizeof(buf) - 1, "x={}, y={}", 10, 20);*ptr = '\0';// buf == "x=10, y=20"9. 고성능 로깅 패턴
섹션 제목: “9. 고성능 로깅 패턴”#include <format>#include <string>#include <chrono>
enum class LogLevel { Debug, Info, Warning, Error };
std::string_view level_str(LogLevel lvl) { switch (lvl) { case LogLevel::Debug: return "DEBUG"; case LogLevel::Info: return "INFO "; case LogLevel::Warning: return "WARN "; case LogLevel::Error: return "ERROR"; } return "?????";}
template<typename... Args>void log(LogLevel lvl, std::format_string<Args...> fmt, Args&&... args) { auto now = std::chrono::system_clock::now(); auto msg = std::format(fmt, std::forward<Args>(args)...); // 헤더와 메시지를 한 번에 조합 std::string line = std::format("[{}] {}\n", level_str(lvl), msg); // write to output...}
// 사용log(LogLevel::Info, "연결 성공: {}:{}", "127.0.0.1", 8080);log(LogLevel::Error, "파일 없음: {}", "/etc/config.txt");std::format은 C++ 문자열 처리의 패러다임을 바꿉니다. 포맷 지정자가 컴파일 타임에 검증되고, 커스텀 타입도 std::formatter 특수화로 자연스럽게 지원됩니다. C++23의 std::print와 함께 사용하면 완전히 printf를 대체할 수 있습니다.