C++

[C++20] printf() 쓰지 마세요! std::format()

카루-R 2020. 3. 21. 09:54
반응형
printf("안녕하세요, %s씨. 당신의 나이는 %d살이고\n당신의 SNS 친구는 %ull명이군요",
    "카루", 17, 823784974892i64);

이걸 C++의 std::cout으로 출력하려면 어떻게 해야 할까요?

std::cout << "안녕하세요, " << "카루" << "씨. 당신의 나이는 " << 17
    << "살이고" << std::endl << "당신의 SNS 친구는 " << 823784974892i64 << "명이군요";

C++에서는 출력시 %d, %s, %ull 이런 것들을 신경 쓸 필요가 없기 때문에 정말 편합니다...만.

이게 어떨 땐 쓸데없이 코드가 길어지게 만들고, printf()보다 복잡한 경우가 있기 때문에 C++에서도 printf()를 고수하는 분들이 계시더라고요.

이젠 그럴 필요 없습니다. 문자열 형식 라이브러리가 등장했습니다.

#include <format>

자, 준비 되셨나요?

우선 메인 함수인 std::format부터 알아보겠습니다.

template<class... Args>
std::string format(std::string_view fmt, const Args&... args);

template<class... Args>
std::wstring format(std::wstring_view fmt, const Args&... args);

template<class... Args>
std::string format(const std::locale& loc, std::string_view fmt, const Args&... args);
 
template<class... Args>
std::wstring format(const std::locale& loc, std::wstring_view fmt, const Args&... args);

 저렇게 다중정의가 되어있는데, 우리가 쓸 건 맨 첫번째입니다. string_view와 가변인수를 받는 함수죠.

string_view는 C++17에 소개되었는데, const char*을 전달해도 바로 사용 가능합니다. 우선 위의 코드를 format을 사용해서 나타내보죠.

std::cout << std::format("안녕하세요, {}씨. 당신의 나이는 {}살이고\n당신의 SNS 친구는 {}명이군요",
    "카루", 17, 823784974892i64);

 뭔가 보이시나요? C# 의 Write를 좀 닮았습니다. 저게 파이선 문법이라는 말이 있는데 제가 파이선은 안 해봐서 잘 모르겠네요. 아무튼 C의 간결함과 C++의 편리함을 모두 누릴 수 있습니다. 반환형은 string이니 그대로 cout에서 출력 가능합니다. 원한다면 C# 처럼 사용도 가능합니다.

std::format("안녕 {0}, 나는 {1}(이)야. 내 나이는 {3}살. {2}.", "독자들", "카루", "반가워", 17);

위 함수의 반환값은

string{"안녕 독자들, 나는 카루(이)야. 내 나이는 17살. 반가워."}입니다.

이뿐 아니라 C언어에서 제공했던 printf()의 강력한 형식 지정자도 제공합니다.

이곳의 예제를 가져왔습니다.

char c = 120;
auto s0 = std::format("{:6}", 42);    // s0 = "    42"
auto s1 = std::format("{:6}", 'x');   // s1 = "x     "
auto s2 = std::format("{:*<6}", 'x'); // s2 = "x*****"
auto s3 = std::format("{:*>6}", 'x'); // s3 = "*****x"
auto s4 = std::format("{:*^6}", 'x'); // s4 = "**x***"
auto s5 = std::format("{:6d}", c);    // s5 = "   120"
auto s6 = std::format("{:6}", true);  // s6 = "true  "

double inf = std::numeric_limits<double>::infinity();
double nan = std::numeric_limits<double>::quiet_NaN();
auto s0 = std::format("{0:},{0:+},{0:-},{0: }", 1);   // s0 = "1,+1,1, 1"
auto s1 = std::format("{0:},{0:+},{0:-},{0: }", -1);  // s1 = "-1,-1,-1,-1"
auto s2 = std::format("{0:},{0:+},{0:-},{0: }", inf); // s2 = "inf,+inf,inf, inf"
auto s3 = std::format("{0:},{0:+},{0:-},{0: }", nan); // s3 = "nan,+nan,nan, nan"

char c = 120;
auto s1 = std::format("{:+06d}", c);   // s1 = "+00120"
auto s2 = std::format("{:#06x}", 0xa); // s2 = "0x000a"
auto s3 = std::format("{:<06}", -42);  // s3 = "-42   " (0 is ignored because of < alignment)

C#에서 이런 걸 얼핏 봤던 것 같은데, 제 기억이 맞을까 모르겠네요. 엄밀히 말하면 이런 중괄호 스타일은 파이썬의 f-string이 제일 먼저이긴 합니다. C#에서 어느 순간 {} 문자열이 등장했고, C++에서도 사용이 가능한거죠. 다만 파이썬의 f-string이나 C#의 $-string처럼 문자열 내에 변수를 직접 넣지는 못합니다. 조금 아쉬워요.

반응형