std → absl 마이그레이션 전략
#한 번에 다 바꾸지 않는다
Abseil은 std의 보완 이지 대체가 아니다. 도입은 영역별로 점진적이며 가치가 명확한 곳부터 한다.
권장 우선순위:
- 새 코드만 Abseil 사용 — 기존 코드는 그대로
- 에러 처리 —
absl::Status/absl::StatusOr도입 - 컨테이너 hot spot —
flat_hash_map등 성능 측정 가능한 곳 - 문자열 —
StrCat,StrSplit,string_view - 시간 —
absl::Time/Duration - 나머지 유틸 — flags, log, random
#1단계 — 새 코드 한정 도입
가장 안전한 시작.
// 기존 module — 손대지 않음class LegacyService { std::unordered_map<int, User> users_; // 그대로};
// 새 module — Abseil 사용class NewService { absl::flat_hash_map<int, User> users_;};빌드에 absl dependency를 추가해 두고, 사용은 점진. 코드 리뷰에서 “새 코드는 abseil 권장” 컨벤션을 명문화.
#2단계 — 에러 처리 통일
가장 가치가 큰 마이그레이션. optional + error_code 혼재된 인터페이스를 StatusOr로 통일.
// beforestruct GetResult { bool ok; User user; std::string error_message;};GetResult GetUser(int id);
// afterabsl::StatusOr<User> GetUser(int id);매크로(ASSIGN_OR_RETURN, RETURN_IF_ERROR)가 호출 측 코드를 크게 줄여 준다.
// beforeGetResult ProcessFlow() { auto a = GetUser(1); if (!a.ok) return {.ok = false, .error_message = a.error_message}; auto b = GetUser(2); if (!b.ok) return {.ok = false, .error_message = b.error_message}; return Combine(a.user, b.user);}
// afterabsl::StatusOr<Combined> ProcessFlow() { ASSIGN_OR_RETURN(User a, GetUser(1)); ASSIGN_OR_RETURN(User b, GetUser(2)); return Combine(a, b);}#3단계 — 컨테이너 hot spot
벤치마크 결과 1-2배 차이 나는 hot path만 우선 치환.
// before — profile에서 unordered_map이 hotstd::unordered_map<std::string, Counter> hits_;
// after — flat_hash_mapabsl::flat_hash_map<std::string, Counter> hits_;모든 unordered_map을 한 번에 바꾸지 않는다. 그런 wholesale 변경은 PR이 비대해지고 부작용 검증이 어렵다. 한 hot path씩.
이때 주의 — flat_hash_map은 iterator invalidation 규칙이 더 엄격 (Part 5-01). 기존 코드가 iterator 유지를 가정하면 node_hash_map으로.
#4단계 — string 도구
// beforestd::string result;for (int i = 0; i < n; ++i) { result += std::to_string(values[i]) + ", ";}
// afterstd::string result = absl::StrJoin(values, ", ");// beforestd::vector<std::string> tokens;boost::split(tokens, input, boost::is_any_of(","));
// afterstd::vector<std::string> tokens = absl::StrSplit(input, ',');치환이 국소적 이라 작은 PR로 분할하기 쉽다.
#5단계 — Time
// before — chrono boilerplatevoid Wait(std::chrono::milliseconds ms);Wait(std::chrono::seconds(5));
// aftervoid Wait(absl::Duration d);Wait(absl::Seconds(5));Chrono ↔ Abseil 변환은 absl::FromChrono / absl::ToChrono*로 가능.
absl::Duration d = absl::FromChrono(std::chrono::seconds(5));auto chrono_d = absl::ToChronoMilliseconds(d);라이브러리 경계에서 변환하고 내부는 한 시스템으로 통일.
#ABI 격리 — 헤더 인터페이스 분리
라이브러리를 제공하는 입장에서는 공개 헤더에 Abseil 타입 노출 여부 가 큰 결정이다.
// 회피 — 공개 헤더에 absl 타입#include "absl/container/flat_hash_map.h"class MyLib {public: void Process(const absl::flat_hash_map<int, Data>& data);};사용자가 우리와 같은 Abseil 버전 을 써야 한다. ABI 격리가 깨진다.
// Good — 공개 헤더는 std/raw types#include <unordered_map>class MyLib {public: void Process(const std::unordered_map<int, Data>& data);private: class Impl; std::unique_ptr<Impl> impl_; // pimpl};
// my_lib.cc — 구현은 자유롭게 absl#include "absl/container/flat_hash_map.h"class MyLib::Impl { absl::flat_hash_map<int, Data> cache_;};내부 cache는 빠른 flat_hash, 외부 인터페이스는 std 호환.
#마이그레이션 도구 — clang-tidy / refactoring scripts
Abseil은 공식 자동 마이그레이션 도구를 일부 제공한다(/tools/ 디렉터리). 다만 대규모 코드베이스용이고, 보통은 IDE의 find & replace + 컴파일 에러 활용 이 실용적.
# 예: unordered_map → flat_hash_map (대량 변환)rg -l "std::unordered_map" src/ | xargs sed -i \ -e 's/std::unordered_map/absl::flat_hash_map/g' \ -e '/^#include/a #include "absl/container/flat_hash_map.h"'# 그 후 컴파일·테스트 반복자동 변환 후 반드시 테스트 및 수동 리뷰. iterator invalidation 같은 의미 차이는 sed가 못 잡는다.
#”Living at Head” — 버전 관리
Abseil은 LTS와 HEAD 두 가지 트랙(Part 1-04). 새 도입은 LTS로 시작, 안정화되면 HEAD 추적 평가.
| 트랙 | 추천 대상 |
|---|---|
| LTS (semver) | 외부 배포 라이브러리, 보수적 팀 |
| HEAD (continuous) | monorepo, 잦은 빌드, Google 사내 스타일 |
#마이그레이션 PR 패턴
권장 PR 분할:
- 빌드 dependency 추가 —
absl링크. 코드는 안 바꿈. - 새 코드만 absl — 컨벤션 문서화.
- 에러 처리 — module별로 Status 도입. 1 module = 1 PR.
- hot path 컨테이너 — 벤치마크 첨부.
- string/time — 영역별 작은 PR.
- legacy 호환 layer 정리 — 마지막.
대형 wholesale PR은 리뷰가 마비된다. 각 PR이 독립적으로 의미 있게 작동 하도록 분할.
#실패 패턴 — 흔한 마이그레이션 실수
// 실패 — 임시 절충namespace mylib {template <typename K, typename V>using HashMap = absl::flat_hash_map<K, V>; // ❌ 별칭이 abseil ABI 노출}별칭이 우리 공개 API 가 되면 사용자 코드가 그 타입에 의존. Abseil 버전 업그레이드 시 충돌. 차라리 명시적 import.
// 실패 — std와 absl 혼재된 컨테이너absl::flat_hash_map<std::string, int> a;std::unordered_map<std::string, int> b;a.insert(b.begin(), b.end()); // ❌ 컨테이너 종류가 다른 인터페이스내부적으로 두 컨테이너가 섞이면 변환 비용 이 자주 발생. 모듈별로 하나만 선택.
#정리
- 전부 한 번에 바꾸지 말 것. 가치 큰 영역부터 점진.
- 우선순위: 새 코드 → Status → 컨테이너 hot spot → string → Time → 나머지.
- 공개 헤더는 std/raw 타입 — Abseil 노출은 ABI 격리 깨짐.
- pimpl 패턴으로 외부 std, 내부 Abseil 분리.
- PR 분할: 1 PR = 1 module, 1 영역.
#시리즈 마무리
Part 1~13으로 Google이 사내 코드 리뷰에서 무엇을 보는가를 컴포넌트 단위로 정리했다. Abseil은 도구 모음이 아니라 코드 리뷰 컨벤션의 코드화에 가깝다. 같은 문제를 같은 방식으로 풀면 큰 코드베이스의 변경 비용이 줄어든다.
#다음 편
Part 14~16에서는 본 시리즈에서 다루지 않은 모듈을 추가로 본다. Part 14-01 — Cleanup부터 시작해 algorithm wrapper, function_ref/any_invocable, Cord, charconv, stacktrace, CRC32C, PeriodicSampler까지.
#관련 항목
- Part 14-01: Cleanup — RAII scope guard
- Part 15-01: Cord — 분산 시스템용 문자열
- Part 16-01: Stacktrace / Symbolize — crash 분석
- Part 1-01: Abseil 개요
- Part 13-01: Google 스타일의 Abseil 사용 패턴
- Part 13-02: 자주 보는 anti-pattern
- Folly Code Review 시리즈 — Meta의 같은 종류 라이브러리
- 원문 — Abseil compatibility
Abseil Code Review · 69 of 79
- 1 Abseil Code Review — Google production-grade C++ 라이브러리 분석
- 2 Abseil 개요 — Google이 std를 보완한 이유
- 3 Abseil 설계 철학 — std 호환과 추가 기능의 균형
- 4 Abseil 빌드와 의존성 — Bazel vs CMake
- 5 Abseil LTS vs HEAD 릴리스 모델 분석
- 6 Abseil Versioning과 ABI 호환성 정책
- 7 Abseil 매크로 — ABSL_HAVE_*·ABSL_ATTRIBUTE_*
- 8 Abseil ABSL_PREDICT_TRUE/FALSE — branch hint
- 9 absl::LogSeverity — 로그 레벨 타입
- 10 Abseil type_traits — negation·conjunction·void_t
- 11 Abseil Conformance·Policy 분석
- 12 Abseil Memory utilities 분석
- 13 Abseil raw_logging — heap-free 로깅
- 14 Abseil thread_annotations — clang TSA 통합
- 15 absl::Status — exception-free error handling
- 16 absl::StatusOr<T> — 값 또는 에러
- 17 absl status_macros — ASSIGN_OR_RETURN·RETURN_IF_ERROR
- 18 absl::Status payload — 구조화된 에러 컨텍스트
- 19 absl::Status ↔ exception 변환 패턴
- 20 absl::string_view — non-owning 문자열 참조
- 21 absl::string_view 함정 — dangling·c_str·임시 객체
- 22 absl::StrCat — 가변 인자 문자열 연결과 AlphaNum
- 23 absl::StrSplit — Delimiter·Predicate·컨테이너 변환
- 24 absl::StrJoin — 컨테이너 결합과 Formatter
- 25 absl::StrFormat — type-safe printf·FormatSpec
- 26 Abseil ASCII 함수 — locale-free 분류·대소문자 변환
- 27 Abseil Escape — CEscape·HexEscape·Base64
- 28 absl::flat_hash_map — Swiss Table 기반 hash map
- 29 absl::flat_hash_set — set 버전 Swiss Table
- 30 absl::node_hash_map — stable pointer가 필요할 때
- 31 absl::btree_map — sorted·cache-friendly B-tree
- 32 absl::FixedArray — 런타임 크기 stack 배열
- 33 absl::InlinedVector — small buffer optimization
- 34 Abseil Swiss Table internals — control byte·SIMD probing
- 35 absl::Mutex — reader-writer·fairness·deadlock 검출
- 36 absl::Mutex Conditional Critical Section — Await로 cv 없애기
- 37 absl::Notification — once-only signal
- 38 absl::BlockingCounter·Barrier — 다중 thread 조율
- 39 absl::Mutex annotations — clang thread-safety로 race를 컴파일 타임에
- 40 absl::Time·Duration 분석 — 단단한 type
- 41 absl::Time Format·Parse
- 42 absl::CivilTime 분석
- 43 absl::time_zone 분석
- 44 absl::Time mocking — 테스트 친화 시간
- 45 absl::BitGen — 모던 난수 생성기
- 46 Abseil Random Distributions — Uniform·Exponential
- 47 Abseil Mocking Random — 테스트 결정성
- 48 Abseil Random Seeding·Entropy
- 49 absl::int128·uint128 분석
- 50 absl::bits — popcount·countl_zero
- 51 absl::optional vs std::optional
- 52 absl::variant 분석
- 53 absl::span 분석
- 54 absl::any 분석
- 55 absl::compare — three-way 비교
- 56 Abseil utility — apply·in_place
- 57 Abseil AbslHashValue 분석
- 58 Abseil HashState chaining
- 59 Abseil Custom hashable 구현
- 60 Abseil LOG·VLOG·CHECK 분석
- 61 Abseil LogSink 분석
- 62 Abseil LogEntry·structured logging
- 63 Abseil Stack trace·failure_signal_handler
- 64 ABSL_FLAG 정의 분석
- 65 Abseil ParseCommandLine 동작
- 66 Abseil Flag introspection·validation
- 67 Google 스타일의 Abseil 사용 패턴
- 68 Abseil 자주 보는 anti-pattern
- 69 std → absl 마이그레이션 전략
- 70 absl::Cleanup — 함수 종료 시 실행 보장
- 71 Abseil algorithm container 확장 — c_sort·c_find_if·c_count_if
- 72 absl::function_ref와 any_invocable — 함수 객체 전달의 두 축
- 73 absl::bind_front와 Overload — 함수 객체 보조 도구
- 74 absl::Cord — 분산 시스템용 대용량 문자열
- 75 absl::from_chars·SimpleAtoi — 빠른 숫자 변환
- 76 absl::Cord vs std::string — 선택 기준과 메모리 프로파일
- 77 absl::GetStackTrace와 Symbolize — crash 시 readable stack
- 78 absl::ComputeCrc32c — 하드웨어 가속 체크섬
- 79 absl::PeriodicSampler — 적응형 샘플링·jitter 회피
관련 글
Abseil 설계 철학 — std 호환과 추가 기능의 균형
Part 1-02: Abseil 설계 철학 — std 호환, ABI 안정성 정책, Live at Head 모델.
같은 시리즈에서 이어 읽기
absl::PeriodicSampler — 적응형 샘플링·jitter 회피
absl::profiling_internal::PeriodicSampler — sampling rate를 동적으로 조정, geometric distribution으로 jitter 회피. 메모리 할당 추적·profiling 인프라의 기반.
같은 시리즈에서 이어 읽기
absl::ComputeCrc32c — 하드웨어 가속 체크섬
absl::ComputeCrc32c — SSE4.2 CRC32, ARM CRC 명령어로 가속된 CRC32C 구현. iSCSI·Btrfs·protobuf에서 표준화된 무결성 검사.
같은 시리즈에서 이어 읽기