[모던 C++ #6] printf에서 벗어나 std::format으로!

과거 C++98/03 시절, 문자열 형식 지정 출력은 주로 C 표준 라이브러리 함수를 이용했습니다. printf 계열 함수들은 간단히 사용할 수 있지만, 타입 미스매치 문제나 서식 문자열 관리가 번거로운 단점이 있었습니다. 예를 들어 %d, %f, %s 등의 포맷 지정자가 실제 매개변수와 불일치할 경우 런타임 버그가 발생할 수 있으며, 이는 디버깅하기도 까다롭습니다.

모던 C++에서는 이러한 문제를 해결하기 위해 C++20부터 std::format 함수를 도입했습니다. 이는 타입 안전하고, Python의 f-string과 유사한 형식 지정 문법을 제공하며, 가독성과 유지보수성을 크게 향상시킵니다.

관련 참고 자료:

과거: printf 계열 함수의 문제점

printf는 서식 문자열과 가변 인자를 이용하여 출력을 형식화합니다. 하지만 포맷 문자열과 인자의 타입 불일치를 컴파일 타임에 잡기 어렵고, 문자열 결합 시 타입 변환 오류가 런타임까지 이어질 수 있습니다.

#include <cstdio>

int main() {
    int x = 42;
    // 정수 출력
    std::printf("x = %d\n", x);

    // 실수 서식에 정수 변수 전달(실수 출력 기대)
    // std::printf("x as float = %f\n", x); // 정의되지 않은 동작(UB)!

    return 0;
}

위 예제에서 %f 자리에 정수 변수를 전달하면 정의되지 않은 동작을 일으킬 수 있습니다. 컴파일러는 이를 잡아내지 못하고, 런타임에 이상한 값이 출력되거나 프로그램이 오동작할 수 있습니다.

또한 형식 문자열을 외부 소스로부터 받아 처리할 경우, %s나 %d 등의 포맷 지정자를 악용한 공격(포맷 문자열 공격)으로부터 안전하지 않습니다.

현재: std::format으로 타입 안전한 출력

C++20에서 도입된 std::format은 타입 안전성을 보장하고, 가독성 좋은 문자열 포맷팅을 제공합니다. Python의 f-string과 유사한 구문으로 인자를 순서대로 {} 안에 배치하며, 선택적으로 형식을 지정할 수 있습니다.

#include <format>
#include <iostream>

int main() {
    int x = 42;
    double pi = 3.14159;
    std::string name = "Alice";

    std::string msg = std::format("x = {}, pi = {:.2f}, name = {}", x, pi, name);
    std::cout << msg << "\n"; 
    // 출력: "x = 42, pi = 3.14, name = Alice"

    return 0;
}

std::format은 인자 타입에 따라 적절한 형식 변환을 자동 처리하며, 컴파일러가 인자의 개수와 타입을 체크하므로 타입 불일치로 인한 런타임 에러를 예방할 수 있습니다. 또한 {:.2f}와 같이 포맷 스펙을 지정하여 부동소수점 정밀도를 간단히 조절할 수 있습니다.

std::format과 iostream

std::format은 문자열을 반환하므로, std::cout << std::format("...", ...) 형태로 바로 출력하거나, 다른 문자열 조작 후 출력할 수 있습니다. 이를 통해 기존 iostream과 자연스럽게 연계할 수 있습니다.

또한 std::format_to, std::format_to_n 함수로 특정 이터레이터나 버퍼에 출력할 수도 있어, 다양한 상황에 맞춘 유연한 포맷팅이 가능합니다.

왜 이런 변화가 필요한가?

  1. 타입 안전성 확보
    std::format은 컴파일 타임에 인자 개수와 타입을 체크하여, printf 계열 함수의 런타임 에러 위험을 줄여줍니다.
  2. 가독성과 유지보수성 향상
    {} 플레이스홀더와 선택적 형식 지정자는 코드 읽기와 관리가 훨씬 수월합니다. 타입 변환 로직을 일일이 신경 쓸 필요가 없습니다.
  3. 보안성과 신뢰성 개선
    printf 형식 문자열 취약점이 없는 것은 아니지만, std::format은 C++ 타입 시스템에 더 밀접하게 연결되어 있어 문제 발생 가능성을 낮춥니다. 코드를 더 안전하게 유지할 수 있습니다.
반응형