folly::to·tryTo — text↔num 변환 분석
#한 줄 요약
folly::to<T>(src)는 source/target 타입 쌍을 컴파일 타임에 dispatch해 변환을 수행한다. 실패 시 throw. folly::tryTo<T>(src)는 Expected<T, ConversionCode>를 반환해 throw 없이 분기한다.
#동기
C++의 변환 API는 산만하다.
std::stoi→ 예외, locale 의존, partial parse.std::from_chars→ exception-free, locale 비의존이지만 C++17이고 API가 낮은 레벨.std::to_string→ locale 비의존이나 부동소수 출력이 부정확.boost::lexical_cast→ 통일된 API지만 stringstream 기반이라 느리다.
fbcode는 log 한 줄, ID parsing, query string 같은 곳에서 매 마이크로초가 보이는 경로다. 통일된 빠른 API가 필요했다.
int n = folly::to<int>("42");std::string s = folly::to<std::string>(3.14);double d = folly::to<double>(s);
auto r = folly::tryTo<int>("not-a-number");if (r.hasValue()) Use(*r);#API & 사용법
#include <folly/Conv.h>
// 1. throw-on-errorint i = folly::to<int>("42"); // OKint j = folly::to<int>("abc"); // throws ConversionErrorauto s = folly::to<std::string>(42); // "42"auto d = folly::to<double>("3.14");
// 2. 부분 parse — view를 진행시키며 자른다folly::StringPiece sp = "42,17";int x = folly::to<int>(&sp); // x=42, sp=",17"
// 3. tryTo — Expected 반환folly::Expected<int, folly::ConversionCode> r = folly::tryTo<int>("99");if (r) { Use(*r); }else { LOG(WARNING) << "code=" << static_cast<int>(r.error()); }
// 4. 여러 인자를 한 문자열로std::string s2 = folly::to<std::string>("id=", id, ",v=", val);
// 5. variadic — output buffer에 appendstd::string out;folly::toAppend("count=", n, " of ", total, &out);folly::to<std::string>(a, b, c)는 + operator overload 없이 가변 인자를 받아 한 번에 만든다. 모든 인자의 size를 미리 계산해 reserve 한다.
#내부 구현
#dispatch 구조
// 약식 — folly/Conv.htemplate <class Tgt, class Src>Tgt to(const Src& src) { if constexpr (std::is_same_v<Tgt, Src>) return src; else return detail::Converter<Tgt, Src>::convert(src);}Converter는 SFINAE/if constexpr로 source-target 쌍마다 specialize 된다. 주요 분기는 다음과 같다.
| Source → Target | 구현 |
|---|---|
| integer → string | to_ascii_base10 (lookup table) |
| float → string | Ryu(Grisu3) 또는 dtoa |
| string → integer | digits_to로 자릿수별 SWAR |
| string → float | strtod 또는 자체 fast parser |
| variadic → string | 각 size 계산 → reserve → append |
#integer → string의 자릿수 lookup
// 약식 — 2자리씩 처리constexpr char digits[] = "0001020304050607080910111213141516..." // "00".."99";
uint32_t to_ascii_base10(char* buf, uint64_t v) { // 8자리씩 SWAR 분할 // 마지막 2-3자리만 1자리씩 처리 // ...}itoa 구현이 char by char 처리한다면 folly는 2-digit pair를 lookup table로 한 번에 쓴다. 10^16 까지의 정수에서 약 2배 빠르다.
#string → integer의 SWAR
// 약식 — 8 바이트를 한 word로 처리uint64_t parse_8_digits_swar(const char* s) { uint64_t word; memcpy(&word, s, 8); word -= 0x3030303030303030ULL; // '0' 8개 빼기 // multiply chain 로 자리수 결합 // ... return result;}8자리 정수를 한 instruction 흐름으로 parse. fbcode의 query string·log 라인 parsing에서 큰 차이.
#tryTo의 Expected 반환
// 약식template <class Tgt, class Src>Expected<Tgt, ConversionCode> tryTo(const Src& src) { Tgt result; auto code = detail::Converter<Tgt, Src>::tryConvert(src, &result); if (code == ConversionCode::SUCCESS) return result; return makeUnexpected(code);}tryConvert는 errno 같은 thread-local 상태 없이 enum을 직접 반환한다. exception unwinding cost가 hot path에서 사라진다.
ConversionCode는 EMPTY_INPUT, INVALID_LEADING_CHAR, NON_DIGIT_CHAR, OVERFLOW, NEGATIVE_OVERFLOW 등 12개 정도. 호출자가 어떤 실패인지 분기 가능하다.
#std/abseil 비교
// std::from_chars (C++17) — 저수준이지만 가장 빠른 표준int v;auto r = std::from_chars(sv.data(), sv.data() + sv.size(), v);if (r.ec == std::errc{}) { /* ok */ }
// abseilint v2;if (absl::SimpleAtoi("42", &v2)) { /* ok */ }std::string s = absl::StrCat(1, ",", 2.5);
// follyint v3 = folly::to<int>("42"); // throwsauto r3 = folly::tryTo<int>("42"); // Expectedstd::string s2 = folly::to<std::string>(1, ",", 2.5);| 항목 | std::from_chars | absl::SimpleAtoi | folly::tryTo |
|---|---|---|---|
| Exception | 없음 | 없음 | tryTo는 없음, to는 throw |
| Return | from_chars_result | bool | Expected<T, Code> |
| Float | C++17 (libstdc++ 늦게) | SimpleAtod | tryTo<double> |
| Locale | 비의존 | 비의존 | 비의존 |
| 가변 to_string | X | StrCat, StrAppend | to<std::string>(...), toAppend |
세 라이브러리 모두 locale 비의존. 표준이 from_chars를 늦게 도입한 탓에 folly와 abseil이 더 풍부한 API를 제공한다.
#코드 리뷰 포인트
// Bad — hot path에서 throw 의존for (auto& s : tokens) { try { Process(folly::to<int>(s)); } catch (...) { Skip(); }}
// Good — tryTo로 분기for (auto& s : tokens) { if (auto r = folly::tryTo<int>(s)) Process(*r); else Skip();}throw는 happy path가 throw 비율이 낮으면 빠르지만, throw 비율이 높으면 수백 배 느려진다. 외부 입력 parsing은 항상 tryTo.
// Bad — std::string 누적std::string s;for (int i = 0; i < N; ++i) { s += folly::to<std::string>(i) + ","; // 매번 임시 할당}
// Good — toAppend로 in-placestd::string s;for (int i = 0; i < N; ++i) { folly::toAppend(i, ",", &s); // s에 직접 append}toAppend는 임시 string을 만들지 않고 target buffer에 바로 쓴다.
#안티패턴
std::to_string(double)을 fbcode에 섞기: locale 의존 출력이라 다른 노드에서 다른 결과.folly::to<std::string>(double)또는fmt::format로 통일.tryTo의Expected검사를value()로: error case에서 throw.if (r) *r또는r.value_or(default).- 부분 parse에 view 진행 미적용:
folly::to<int>(&sp)는 sp를 진행시킨다. 같은 입력을 두 번 parse 하면 안 된다.
#정리
to<T>(src)는 통일된 변환 API, 실패 시 throw.tryTo<T>(src)는Expected<T, ConversionCode>반환, 외부 입력에 적합.- variadic
to<std::string>(...),toAppend(...)로 한 번에 누적. - 내부적으로 lookup table, SWAR로 std::stoi/std::to_string 대비 2-5배 빠름.
- locale 비의존.
std::from_chars보다 high-level이고 풍부.
#다음 편
다음은 사용자 정의 타입을 folly::to 변환 가능하게 만드는 customization point를 본다.
#관련 항목
- Part 6-02: Conv customization —
parseTo/toAppend특화 - Part 6-03: Conv performance — benchmark 상세
- Part 13-01: exception_wrapper — Expected와 함께 쓰는 에러 처리
- 원문 — folly/Conv.h
Folly Code Review · 28 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 dynamic ↔ struct — manual marshaling
Part 11-03: dynamic을 strongly-typed struct로. type safety boundary를 어디에 그을지, marshaling 패턴 비교.
같은 시리즈에서 이어 읽기
folly Conv 성능 비교 — sprintf·stringstream 대비
folly::to의 성능 — lookup table itoa, SWAR atoi, sprintf/iostream과의 5-10배 차이.
같은 시리즈에서 이어 읽기
folly Conv Customization — 사용자 타입 지원
folly::to에 사용자 타입을 hook하기 — parseTo, toAppend ADL 확장.
같은 시리즈에서 이어 읽기
이 글을 참조하는 글 (9)
- folly::format — legacy formatter 분석 — Folly Code Review
- folly::Optional vs std::optional — Folly Code Review
- folly::ExceptionWrapper — type-erased exception holder — Folly Code Review
- folly dynamic ↔ struct — manual marshaling — Folly Code Review
- folly JSON conversion — toJson·parseJson — Folly Code Review
- folly Conv 성능 비교 — sprintf·stringstream 대비 — Folly Code Review
- folly Conv Customization — 사용자 타입 지원 — Folly Code Review
- folly Join·Split utilities — 문자열 분해와 결합 — Folly Code Review
- folly::Promise·makeFuture — Future를 만드는 두 길 — Folly Code Review