folly Conv Customization — 사용자 타입 지원
#한 줄 요약
사용자 타입을 folly::to/folly::tryTo 변환 가능하게 만들려면 같은 namespace에 parseTo(StringPiece, T&)와 toAppend(const T&, std::string*) 자유 함수를 정의한다. ADL로 발견된다.
#동기
fbcode는 거의 모든 타입을 텍스트 로그로 흘려보낸다. 매 타입마다 << overload, toString() 멤버, from_string() factory를 따로 만들면 호출자가 어느 API를 써야 할지 헷갈린다. 통일된 hook 한 쌍을 정해 두면 folly::to가 자동으로 인식한다.
struct UserId { uint64_t value; };
// 두 hook만 추가하면void toAppend(const UserId& u, std::string* out) { /* ... */ }folly::Expected<folly::StringPiece, folly::ConversionCode>parseTo(folly::StringPiece sp, UserId& out) { /* ... */ }
// 자동 동작auto s = folly::to<std::string>(UserId{42});auto u = folly::to<UserId>("U#42");#API & 사용법
#toAppend — T → string
namespace myns {
struct Money { int64_t cents; std::string currency; // "USD" 등};
// 자유 함수, 같은 namespacevoid toAppend(const Money& m, std::string* out) { folly::toAppend(m.cents / 100, '.', folly::to<std::string>(m.cents % 100).insert(0, "00", 2 - 1), ' ', m.currency, out);}
// optional — size hintsize_t estimateSpaceNeeded(const Money& m) { return 32 + m.currency.size();}
} // myns
std::string s = folly::to<std::string>(myns::Money{12345, "USD"});// "123.45 USD"estimateSpaceNeeded는 reserve를 정확히 하기 위한 hook이다. 없어도 동작하나 reallocation 가능.
#parseTo — string → T
namespace myns {
folly::Expected<folly::StringPiece, folly::ConversionCode>parseTo(folly::StringPiece sp, Money& out) { // "123.45 USD" 형태 가정 auto dot = sp.find('.'); if (dot == folly::StringPiece::npos) { return folly::makeUnexpected(folly::ConversionCode::INVALID_LEADING_CHAR); } int64_t whole, cents; auto r1 = folly::tryTo<int64_t>(sp.subpiece(0, dot)); if (!r1) return folly::makeUnexpected(r1.error()); // ... cents, currency out = Money{whole * 100 + cents, "USD"}; return folly::StringPiece{}; // 모두 소비}
} // myns
auto r = folly::tryTo<myns::Money>("123.45 USD");반환은 남은 input. 모두 소비했으면 empty StringPiece. 부분 parse가 의도라면 남은 부분을 반환해 호출자가 이어 받게 한다.
#내부 구현
#ADL dispatch
// 약식 — folly/Conv.hnamespace folly {
template <class Tgt>Tgt to(StringPiece sp) { Tgt result; using detail::parseTo_; // 내부 default using ::parseTo; // global parseTo도 보이게 // ADL: sp가 user namespace의 parseTo도 찾는다 auto remaining = parseTo(sp, result); if (!remaining) throw ConversionError{}; if (!remaining->empty()) throw ConversionError{"trailing"}; return result;}
}using declaration + 같은 expression에서의 ADL이 핵심. user namespace의 parseTo가 자동으로 후보에 들어간다. 이게 Niebloid 식으로 customizable point를 만드는 표준 패턴.
toAppend도 같은 방식. 다음과 같은 chain이 동작한다.
folly::to<std::string>(a, b, c) → folly::toAppend(a, &out); // ADL → user 또는 folly 기본 → folly::toAppend(b, &out); → folly::toAppend(c, &out);#default 동작
기본 hook은 다음을 안다.
std::is_arithmetic_v<T>→ built-in 빠른 path.std::string/fbstring/StringPiece/const char*→ 직접 append.bool→ “true”/“false” 또는 “0”/“1” (<<bool>>로 지정).- enum → underlying integer로 폴백.
user 정의가 있으면 ADL이 우선. user가 enum class를 enum name으로 출력하고 싶으면 toAppend 특화로 enum-to-string switch 작성.
enum class Color { Red, Green, Blue };
inline void toAppend(Color c, std::string* out) { switch (c) { case Color::Red: out->append("Red"); break; case Color::Green: out->append("Green"); break; case Color::Blue: out->append("Blue"); break; }}#std/abseil 비교
// abseil — AbslStringify 멤버 또는 free functionstruct UserId { uint64_t value; };
template <typename Sink>void AbslStringify(Sink& sink, const UserId& u) { absl::Format(&sink, "U#%016x", u.value);}// 이제 absl::StrCat(UserId{42}), absl::StrFormat("%v", UserId{42}) 동작| 항목 | folly::to | abseil StrCat | std::format |
|---|---|---|---|
| Hook name | toAppend / parseTo | AbslStringify | std::formatter 특화 |
| Find 메커니즘 | ADL + using | template + ADL | template 특화 |
| 양방향 | O (parseTo도) | X (출력만) | X (출력만) |
| C++ standard | 무관 | C++14 | C++20 |
folly만이 양방향(text↔T)을 한 쌍으로 제공한다. abseil/std는 출력만 있어 parsing은 별도로 만들어야 한다.
#코드 리뷰 포인트
// Bad — friend 멤버struct UserId { friend std::string ToString(const UserId& u) { /* ... */ } // folly::to가 보지 못한다};
// Good — ADL 자유 함수struct UserId { uint64_t value; };void toAppend(const UserId& u, std::string* out) { /* ... */ }ToString/to_string 같은 별도 이름은 folly::to가 사용하지 못한다. 정확한 hook 이름을 따라야 dispatch가 된다.
// 위험 — parseTo가 estimateSpaceNeeded 없음void toAppend(const Big& b, std::string* out) { /* 1KB 출력 */ }// reserve 부정확 → realloc 여러 번
// 권장size_t estimateSpaceNeeded(const Big& b) { return 1024; }큰 출력은 estimate를 같이 정의해 reserve가 한 번에 끝나게.
#안티패턴
- template으로 toAppend 정의: ADL은 같은 namespace의 non-template 자유 함수에 가장 잘 동작. template은 다른 컴파일 단위에서 못 볼 수 있다. concrete 타입별로.
parseTo가 input 전체 소비 후에도 empty 반환 안 함: caller가 trailing data로 판단해 throw 한다. 모두 썼으면 empty StringPiece 반환.toAppend가 throw:to<std::string>은 nothrow를 기대한다. throw 가능성 있으면 contract 명시.
#정리
toAppend(T, std::string*)+parseTo(StringPiece, T&)로 양방향 hook.- ADL로 user namespace 함수가 자동 dispatch.
estimateSpaceNeeded는 옵션이나 큰 출력에 권장.- abseil/std는 출력만, folly는 양방향 — text 양방향이 잦은 fbcode에 맞춤.
- 멤버 함수가 아니라 자유 함수, 같은 namespace.
#다음 편
다음은 folly::to가 sprintf/stringstream 대비 얼마나 빠른지, lookup table과 SWAR가 어떻게 효과를 내는지 benchmark로 확인한다.
#관련 항목
- Part 6-01: folly::to / tryTo — hook을 호출하는 API
- Part 6-03: Conv performance — toAppend 성능 측정
- Part 5-02: fmt::format integration — formatter 특화와 비교
- 원문 — folly/Conv.h
Folly Code Review · 29 of 89
- 1 Folly Code Review — Meta의 production-grade C++ 라이브러리 코드 분석
- 2 Folly 개요 — Meta가 production에서 검증한 utility 모음 분석
- 3 Folly vs Abseil 철학 비교 — performance-first vs std-compatible
- 4 Folly 빌드와 fbcode 환경 — monorepo의 그림자
- 5 Folly API stability 정책 — 어떤 보장도 없다는 솔직함
- 6 Folly production validation 문화 — peta-scale에서 단련된 코드
- 7 folly::Future 분석 — std::future의 한계를 넘는 composable async
- 8 folly::Promise·makeFuture — Future를 만드는 두 길
- 9 folly::SemiFuture vs Future — executor binding의 명시화
- 10 folly::Future thenValue·thenError·thenTry — continuation 체인 분석
- 11 folly::collect·collectAll·collectAny — fan-in 패턴 분석
- 12 folly::Future retry·window·via — 제어 흐름 조합자
- 13 folly::fibers 분석 — M:N stackful coroutine
- 14 folly::InlineExecutor — 호출자 thread에서 즉시 실행
- 15 folly::CPUThreadPoolExecutor — CPU-bound 작업의 표준 thread pool
- 16 folly::IOThreadPoolExecutor — libevent 기반 I/O pool
- 17 folly::ManualExecutor — 결정적 테스트를 위한 수동 진행
- 18 folly::EventBase 분석 — libevent 이벤트 루프의 핵심
- 19 folly::IOBuf 분석 — zero-copy buffer chain의 기본 단위
- 20 folly::IOBufQueue — chain의 push/pull 추상화
- 21 folly::io::Cursor·RWCursor — chain 위의 stream
- 22 folly Zero-copy 패턴 — IOBuf로 ScatterGather I/O 표현
- 23 folly::IOBuf shared semantics — clone·unshare·takeOwnership
- 24 folly::FBString 분석 — SSO + COW 구현
- 25 folly의 fmt::format 통합 — 모던 포맷팅 채택
- 26 folly::StringPiece — string_view 호환 분석
- 27 folly Join·Split utilities — 문자열 분해와 결합
- 28 folly::to·tryTo — text↔num 변환 분석
- 29 folly Conv Customization — 사용자 타입 지원
- 30 folly Conv 성능 비교 — sprintf·stringstream 대비
- 31 folly::F14ValueMap vs std::unordered_map
- 32 folly::F14NodeMap — stable pointer가 필요할 때
- 33 folly::F14VectorMap — cache-friendly iteration
- 34 folly::F14FastMap — auto-select 동작
- 35 folly F14 internals — SIMD probing 메커니즘
- 36 folly::small_vector — inline storage 분석
- 37 folly::FixedString — compile-time string
- 38 folly::AtomicHashMap — lock-free read 분석
- 39 folly::ConcurrentHashMap — sharded 동시 해시 맵
- 40 folly::EvictingCacheMap — LRU 구현 분석
- 41 folly::Synchronized — lock wrapper 패턴
- 42 folly::SharedMutex 분석
- 43 folly::Baton — one-shot wait 동기화
- 44 folly::RWSpinLock 분석
- 45 folly::PicoSpinLock — 1-byte spinlock
- 46 folly::ProducerConsumerQueue — SPSC 큐 분석
- 47 folly::MPMCQueue — multi-producer multi-consumer
- 48 folly::UnboundedQueue — 동적 크기 lock-free
- 49 folly::fibers::Channel — Go-like channel
- 50 folly::dynamic — JSON-like dynamic type 분석
- 51 folly JSON conversion — toJson·parseJson
- 52 folly dynamic ↔ struct — manual marshaling
- 53 folly dynamic Visitor pattern — type별 분기
- 54 folly::Singleton vs Meyers/static — 왜 Folly의 Singleton인가
- 55 folly::SingletonVault 분석 — 등록·소멸·의존성
- 56 folly::Singleton try_get·try_get_fast — TLS-cached 접근
- 57 folly::ExceptionWrapper — type-erased exception holder
- 58 folly::ScopeGuard·SCOPE_EXIT — RAII cleanup
- 59 folly::Optional vs std::optional
- 60 folly::Function vs std::function
- 61 folly::Lazy — 지연 초기화 wrapper
- 62 folly Meta 스타일 code review 패턴
- 63 folly anti-patterns — 잘못 쓰면 std보다 느림
- 64 folly vs std 선택 기준 분석
- 65 folly::coro 개요 — production C++20 코루틴 어댑터
- 66 folly::coro::Task — lazy single-shot 코루틴
- 67 folly::coro::AsyncGenerator — 비동기 스트림
- 68 folly coro blockingWait·collectAll — 동기 경계와 fan-in
- 69 folly::coro::Baton·Mutex — 코루틴-aware 동기화
- 70 folly::Expected — 결과 또는 오류
- 71 folly::Try — Future 결과 wrapper
- 72 folly::Try vs Expected 선택 기준
- 73 folly::Range — 일반 iterator pair
- 74 folly::Uri — URL 파서
- 75 folly Fingerprint64·128 — 분산 hash
- 76 folly SpookyHashV2 — fast non-crypto hash
- 77 folly::Init — main() 부트스트랩
- 78 folly::Indestructible — global lifetime 패턴
- 79 folly::MicroLock — 1-byte 락
- 80 folly::MicroSpinLock — 가장 좁은 spin lock
- 81 folly::format — legacy formatter 분석
- 82 folly::demangle — typeid 디망글링
- 83 folly::DynamicConverter — dynamic ↔ struct
- 84 folly::RecordIO — append-only 로그 파일 포맷
- 85 folly::io::Compression — zstd·lz4·snappy wrapper
- 86 folly::AsyncIO — io_uring·Linux AIO
- 87 folly::CancellationToken — 코루틴·Future 취소 전파
- 88 folly::observer — hot config의 atomic refresh
- 89 fbcode 패턴 모음 — folly 사용의 실전
관련 글
folly Conv 성능 비교 — sprintf·stringstream 대비
folly::to의 성능 — lookup table itoa, SWAR atoi, sprintf/iostream과의 5-10배 차이.
같은 시리즈에서 이어 읽기
folly::to·tryTo — text↔num 변환 분석
folly::to의 throw-on-error 변환, tryTo의 Expected 반환, 양방향 string/number 처리.
같은 시리즈에서 이어 읽기
fbcode 패턴 모음 — folly 사용의 실전
Meta fbcode 코드 리뷰에서 반복적으로 등장하는 folly 사용 패턴 — overview + 시리즈 마무리.
같은 시리즈에서 이어 읽기