제목: “이번 주의 팁 #215: AbslStringify()
를 사용한 사용자 정의 타입 문자열화”
원문 게시일: 2022년 11월 2일
업데이트: 2022년 11월 16일
작성자: Phoebe Liang
빠른 링크: abseil.io/tips/215
개요
Abseil은 사용자 정의 타입을 문자열로 포맷할 수 있는 새로운 경량 메커니즘인 AbslStringify()
를 지원합니다. AbslStringify()
를 확장한 사용자 정의 타입은 absl::StrFormat
, absl::StrCat
, 그리고 absl::Substitute
와 함께 바로 사용할 수 있습니다.
대부분의 타입 확장과 마찬가지로, 확장하려는 타입을 직접 소유하고 있어야 합니다.
사용 예제
간단한 Point
구조체를 예로 들어보겠습니다:
struct Point {
int x;
int y;
};
Point
를 absl::StrFormat()
, absl::StrCat()
및 absl::Substitute()
에서 사용하고 싶다면, AbslStringify()
라는 friend
함수 템플릿을 추가하면 됩니다:
struct Point {
template <typename Sink>
friend void AbslStringify(Sink& sink, const Point& p) {
absl::Format(&sink, "(%d, %d)", p.x, p.y);
}
int x;
int y;
};
참고:
AbslStringify()
는 문자열을 생성하기 위해 제네릭 “sink” 버퍼를 사용합니다. 이 sink는absl::FormatSink
와 유사한 인터페이스를 가지지만,PutPaddedString()
은 지원하지 않습니다.
이제 absl::StrCat("The point is ", p)
및 absl::Substitute("The point is $0", p)
를 사용할 수 있습니다.
%v
타입 지정자와 타입 유추
absl::StrFormat()
을 사용해 타입을 포맷하고 싶다면 어떻게 해야 할까요? 기존의 타입 지정자는 AbslStringify()
를 확장한 사용자 정의 타입을 지원하지 않기 때문에 다음과 같이 작성해야 합니다:
absl::StrFormat("The point is (%d, %d)", p.x, p.y);
하지만 이는 비효율적입니다. 포맷 문자열을 반복해야 하고 AbslStringify()
확장을 사용하지도 않습니다. 대신 새로운 %v
타입 지정자를 사용할 수 있습니다:
absl::StrFormat("The point is %v", p);
%v
는 타입 유추를 통해 인자를 포맷합니다. %v
는 대부분의 기본 타입과 AbslStringify()
로 확장된 타입을 지원합니다. %v
를 사용하면 “값”을 범용적으로 포맷할 수 있습니다.
%v가 유추하는 타입
- 정수형:
d
(부호 있는 정수) - 부호 없는 정수형:
u
- 부동소수점:
g
double
,float
,long double
- 문자열:
s
std::string
,absl::string_view
,std::string_view
,absl::Cord
주의:
const char*
는 지원되지 않습니다.
예제
absl::StrFormat("%v", std::string{"hello"}) -> "hello"
absl::StrFormat("%v", 42) -> "42"
absl::StrFormat("%v", uint64_t{16}) -> "16"
absl::StrFormat("%v", 1.6) -> "1.6"
absl::StrFormat("%v", true) -> "true"
다른 라이브러리와의 통합
로깅 지원
AbslStringify()
를 정의한 타입은 바로 로깅에 사용할 수 있습니다:
struct Point {
template <typename Sink>
friend void AbslStringify(Sink& sink, const Point& p) {
absl::Format(&sink, "(%v, %v)", p.x, p.y);
}
int x = 10;
int y = 20;
};
Point p;
LOG(INFO) << p;
이 코드의 로그 출력 결과는 다음과 같습니다:
I0926 09:00:00.000000 12345 main.cc:10] (10, 20)
operator<<
대신 AbslStringify()
를 구현하는 것을 권장합니다. 이렇게 하면 absl::StrFormat
, absl::StrCat
, absl::Substitute
에서도 사용할 수 있기 때문입니다.
프로토콜 버퍼 지원
프로토콜 버퍼도 AbslStringify()
를 통해 문자열로 포맷할 수 있습니다:
message MyProto {
optional string my_string = 1;
}
MyProto my_proto;
my_proto.set_my_string("hello world");
absl::StrCat("My proto is: ", my_proto);
absl::StrFormat("My proto is: %v", my_proto);
LOG(INFO) << my_proto;
마무리
%v
타입 지정자는 “그냥 사람이 읽을 수 있는 형태로 출력”하는 것이 목적입니다. 사용자 정의 타입과 같이 필요한 경우를 제외하고는 %v
를 간단한 포맷팅에 사용하는 것을 추천합니다. 하지만 더 세부적인 포맷이 필요한 경우에는 기존의 타입 지정자를 사용해야 합니다.