본문으로 건너뛰기
Abseil Code Review · 6/79

Abseil 매크로 — ABSL_HAVE_*·ABSL_ATTRIBUTE_*

· Hawk · 4분 읽기

한 줄 요약: ABSL_HAVE_*는 컴파일러·플랫폼 기능의 존재를 감지하고, ABSL_ATTRIBUTE_*는 표준화되지 않은 attribute의 컴파일러별 차이를 흡수한다. Abseil의 모든 코드가 이 두 매크로 위에 서 있다.

#어떤 문제를 푸는가

C++의 portability는 표준만으로 부족하다. 같은 표준 버전이라도 컴파일러마다 지원 정도가 다르고, 비표준 attribute가 코드의 성능·정확성에 큰 영향을 준다.

세 가지 종류의 차이를 처리해야 한다.

  1. 표준 기능의 존재 감지 — C++17이라도 <filesystem> 지원 여부가 컴파일러마다 다름.
  2. 컴파일러별 builtin__builtin_expect, __builtin_unreachable은 GCC/Clang에만.
  3. attribute의 syntax 차이[[nodiscard]]는 C++17, MSVC의 __declspec(...)은 별도.

Abseil은 이 셋을 매크로로 감싸 통일한다.

#ABSL_HAVE_* — 기능 감지

ABSL_HAVE_*는 컴파일 시점에 사용 가능한 기능을 알려준다.

absl/base/config.h
#if defined(__cpp_lib_filesystem)
#define ABSL_HAVE_STD_FILESYSTEM 1
#endif
#if defined(__has_builtin) && __has_builtin(__builtin_expect)
#define ABSL_HAVE_BUILTIN_EXPECT 1
#endif
#if defined(__cpp_exceptions) && __cpp_exceptions
#define ABSL_HAVE_EXCEPTIONS 1
#endif
#if defined(__cpp_rtti) && __cpp_rtti
#define ABSL_HAVE_RTTI 1
#endif

사용 패턴은 단순하다.

#include "absl/base/config.h"
void DoIO() {
#ifdef ABSL_HAVE_STD_FILESYSTEM
std::filesystem::path p = "/tmp/x";
#else
// POSIX fallback
const char* p = "/tmp/x";
#endif
}

#주요 ABSL_HAVE_* 목록

매크로감지 대상
ABSL_HAVE_BUILTIN(x)clang/GCC builtin 존재
ABSL_HAVE_FEATURE(x)clang feature 존재
ABSL_HAVE_ATTRIBUTE(x)attribute 존재
ABSL_HAVE_CPP_ATTRIBUTE(x)[[attr]] 형태 존재
ABSL_HAVE_EXCEPTIONS-fno-exceptions 여부
ABSL_HAVE_RTTI-fno-rtti 여부
ABSL_HAVE_THREAD_LOCALTLS 지원
ABSL_HAVE_ALIAS_TEMPLATE_ARGUMENT_DEDUCTIONC++20 alias template CTAD
ABSL_HAVE_STD_OPTIONAL<optional> 사용 가능
ABSL_HAVE_STD_VARIANT<variant> 사용 가능
ABSL_HAVE_STD_STRING_VIEW<string_view> 사용 가능

#_has* 컴파일러 builtin 활용

ABSL_HAVE_BUILTIN, ABSL_HAVE_ATTRIBUTE는 GCC/Clang의 __has_builtin, __has_attribute를 감싼다.

#if defined(__has_builtin)
#define ABSL_HAVE_BUILTIN(x) __has_builtin(x)
#else
#define ABSL_HAVE_BUILTIN(x) 0
#endif
// 사용
#if ABSL_HAVE_BUILTIN(__builtin_unreachable)
[[noreturn]] inline void Unreachable() { __builtin_unreachable(); }
#else
[[noreturn]] inline void Unreachable() { std::abort(); }
#endif

이 패턴이 Abseil 전반에서 반복된다. “지원하면 빠른 경로, 없으면 안전한 fallback.”

#ABSL_ATTRIBUTE_* — attribute 추상화

C++ attribute는 컴파일러마다 syntax가 다르다.

// 표준 C++11+
[[noreturn]] void Die();
// GCC/Clang 확장
__attribute__((noinline)) void Foo();
// MSVC 확장
__declspec(noinline) void Bar();

Abseil은 이런 차이를 매크로로 흡수한다.

// absl/base/attributes.h에서 발췌
#if ABSL_HAVE_ATTRIBUTE(noinline) || (defined(__GNUC__) && !defined(__clang__))
#define ABSL_ATTRIBUTE_NOINLINE __attribute__((noinline))
#elif defined(_MSC_VER)
#define ABSL_ATTRIBUTE_NOINLINE __declspec(noinline)
#else
#define ABSL_ATTRIBUTE_NOINLINE
#endif
ABSL_ATTRIBUTE_NOINLINE void SlowPath();

#주요 ABSL_ATTRIBUTE_*

매크로의미
ABSL_ATTRIBUTE_NOINLINE인라인 금지
ABSL_ATTRIBUTE_ALWAYS_INLINE강제 인라인
ABSL_ATTRIBUTE_COLD거의 호출되지 않는 함수 (저속 경로)
ABSL_ATTRIBUTE_HOT자주 호출되는 함수
ABSL_ATTRIBUTE_NORETURN돌아오지 않는 함수
ABSL_ATTRIBUTE_UNUSED사용되지 않아도 경고 안 함
ABSL_ATTRIBUTE_PACKEDstruct padding 제거
ABSL_ATTRIBUTE_WEAKweak symbol
ABSL_ATTRIBUTE_LIFETIME_BOUND인자 lifetime이 반환값을 결정
ABSL_ATTRIBUTE_TRIVIAL_ABIclang의 trivial_abi
ABSL_ATTRIBUTE_REINITIALIZES객체가 재초기화되었음을 분석기에 알림

#LIFETIME_BOUND — 잘 알려지지 않은 중요한 도구

ABSL_ATTRIBUTE_LIFETIME_BOUND는 clang에서 dangling reference를 컴파일 시점에 잡아준다.

#include "absl/base/attributes.h"
class Person {
public:
const std::string& name() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return name_;
}
private:
std::string name_;
};
// 위험한 사용 — clang이 경고
const std::string& bad = Person().name();
// warning: returning reference to local temporary

string_view, Span 같이 비소유 타입을 반환하는 함수에 붙이는 것이 권장된다. absl::string_view 자체가 모든 const string 반환 함수에 이걸 붙이고 있다.

#TRIVIAL_ABI — pass-by-value 최적화

ABSL_ATTRIBUTE_TRIVIAL_ABI는 destructor가 non-trivial한 타입이라도 ABI 관점에서 trivial로 다루도록 한다.

class ABSL_ATTRIBUTE_TRIVIAL_ABI MyHandle {
public:
~MyHandle() { Release(); }
// ...
private:
int fd_;
};
void Process(MyHandle h);
// MyHandle이 register로 전달될 수 있음 — 호출 비용 감소

표준 ABI에서는 non-trivial destructor가 있으면 무조건 메모리로 전달된다. trivial_abi는 이 제약을 우회한다. clang에서만 동작하고, ABI break를 동반하므로 신중히 써야 한다.

#ABSL_PREDICT_*, ABSL_FALLTHROUGH_INTENDED

비슷한 카테고리에 속하는 매크로들.

// branch hint
if (ABSL_PREDICT_FALSE(error_occurred)) {
HandleError();
}
// switch fall-through 의도 표시
switch (x) {
case 1:
DoOne();
ABSL_FALLTHROUGH_INTENDED;
case 2:
DoTwo();
break;
}

ABSL_PREDICT_*Part 2-02에서 따로 다룬다.

#ABSL_DEPRECATED — 안전한 deprecation

ABSL_DEPRECATED는 컴파일러의 deprecation attribute를 감싼다.

#if ABSL_HAVE_CPP_ATTRIBUTE(deprecated)
#define ABSL_DEPRECATED(msg) [[deprecated(msg)]]
#elif defined(__GNUC__)
#define ABSL_DEPRECATED(msg) __attribute__((deprecated(msg)))
#else
#define ABSL_DEPRECATED(msg)
#endif
ABSL_DEPRECATED("Use NewAPI() instead") void OldAPI();

deprecation message는 사용자가 마이그레이션 경로를 알 수 있도록 구체적으로 쓴다. “deprecated” 한 단어만 쓰는 것은 안티패턴.

#코드 리뷰 포인트

// 회피 — 컴파일러별 attribute를 직접 사용
__attribute__((noinline)) void SlowPath();
// MSVC에서 컴파일 안 됨.
// Good — Abseil 매크로
ABSL_ATTRIBUTE_NOINLINE void SlowPath();
// 회피 — __has_builtin을 직접 사용
#if __has_builtin(__builtin_expect)
// 정의되지 않은 컴파일러에서 syntax error.
// Good
#if ABSL_HAVE_BUILTIN(__builtin_expect)
// 회피 — string_view 반환에 LIFETIME_BOUND 누락
absl::string_view GetName() const { return name_; }
// Good
absl::string_view GetName() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return name_;
}

리뷰에서 봐야 할 것:

  1. 컴파일러별 분기를 직접 쓰는가 — Abseil 매크로로 감싸야 함.
  2. 비소유 타입을 반환하는데 LIFETIME_BOUND 없는가 — clang에서 미리 잡을 수 있는 버그.
  3. deprecation message가 마이그레이션 경로를 알려주는가.

#자주 보는 안티패턴

// 회피 — ABSL_HAVE_*를 그냥 무시
std::optional<int> Find(int key); // C++17 가정
// C++14 빌드에서 깨짐.
// Good
#ifdef ABSL_HAVE_STD_OPTIONAL
std::optional<int> Find(int key);
#else
absl::optional<int> Find(int key);
#endif
// 또는 처음부터 absl::optional만 쓰고 마이그레이션은 한 번에
// 회피 — ALWAYS_INLINE을 무분별하게
ABSL_ATTRIBUTE_ALWAYS_INLINE void HelperA();
ABSL_ATTRIBUTE_ALWAYS_INLINE void HelperB();
ABSL_ATTRIBUTE_ALWAYS_INLINE void HelperC();
// 코드 크기 증가, instruction cache 압박.
// 측정 없이 ALWAYS_INLINE을 붙이는 것은 보통 손해.
// Good — hot path만 측정해서 선택적으로

#정리

  • ABSL_HAVE_*는 컴파일러·플랫폼 기능 존재 감지.
  • ABSL_ATTRIBUTE_*는 컴파일러별 attribute 차이 흡수.
  • LIFETIME_BOUND, TRIVIAL_ABI 같은 잘 안 알려진 매크로가 안전성·성능에 큰 도움.
  • 컴파일러별 분기를 직접 쓰지 말고 Abseil 매크로로 감쌀 것.
  • ALWAYS_INLINE은 측정 후에만.

#다음 편

Part 2-02에서 ABSL_PREDICT_TRUE / ABSL_PREDICT_FALSE를 본다. branch hint가 성능에 실제로 얼마나 영향을 주는지, modern CPU에서 어디까지 의미가 있는지 살핀다.

#관련 항목

Abseil Code Review · 7 of 79

  1. 1 Abseil Code Review — Google production-grade C++ 라이브러리 분석
  2. 2 Abseil 개요 — Google이 std를 보완한 이유
  3. 3 Abseil 설계 철학 — std 호환과 추가 기능의 균형
  4. 4 Abseil 빌드와 의존성 — Bazel vs CMake
  5. 5 Abseil LTS vs HEAD 릴리스 모델 분석
  6. 6 Abseil Versioning과 ABI 호환성 정책
  7. 7 Abseil 매크로 — ABSL_HAVE_*·ABSL_ATTRIBUTE_*
  8. 8 Abseil ABSL_PREDICT_TRUE/FALSE — branch hint
  9. 9 absl::LogSeverity — 로그 레벨 타입
  10. 10 Abseil type_traits — negation·conjunction·void_t
  11. 11 Abseil Conformance·Policy 분석
  12. 12 Abseil Memory utilities 분석
  13. 13 Abseil raw_logging — heap-free 로깅
  14. 14 Abseil thread_annotations — clang TSA 통합
  15. 15 absl::Status — exception-free error handling
  16. 16 absl::StatusOr<T> — 값 또는 에러
  17. 17 absl status_macros — ASSIGN_OR_RETURN·RETURN_IF_ERROR
  18. 18 absl::Status payload — 구조화된 에러 컨텍스트
  19. 19 absl::Status ↔ exception 변환 패턴
  20. 20 absl::string_view — non-owning 문자열 참조
  21. 21 absl::string_view 함정 — dangling·c_str·임시 객체
  22. 22 absl::StrCat — 가변 인자 문자열 연결과 AlphaNum
  23. 23 absl::StrSplit — Delimiter·Predicate·컨테이너 변환
  24. 24 absl::StrJoin — 컨테이너 결합과 Formatter
  25. 25 absl::StrFormat — type-safe printf·FormatSpec
  26. 26 Abseil ASCII 함수 — locale-free 분류·대소문자 변환
  27. 27 Abseil Escape — CEscape·HexEscape·Base64
  28. 28 absl::flat_hash_map — Swiss Table 기반 hash map
  29. 29 absl::flat_hash_set — set 버전 Swiss Table
  30. 30 absl::node_hash_map — stable pointer가 필요할 때
  31. 31 absl::btree_map — sorted·cache-friendly B-tree
  32. 32 absl::FixedArray — 런타임 크기 stack 배열
  33. 33 absl::InlinedVector — small buffer optimization
  34. 34 Abseil Swiss Table internals — control byte·SIMD probing
  35. 35 absl::Mutex — reader-writer·fairness·deadlock 검출
  36. 36 absl::Mutex Conditional Critical Section — Await로 cv 없애기
  37. 37 absl::Notification — once-only signal
  38. 38 absl::BlockingCounter·Barrier — 다중 thread 조율
  39. 39 absl::Mutex annotations — clang thread-safety로 race를 컴파일 타임에
  40. 40 absl::Time·Duration 분석 — 단단한 type
  41. 41 absl::Time Format·Parse
  42. 42 absl::CivilTime 분석
  43. 43 absl::time_zone 분석
  44. 44 absl::Time mocking — 테스트 친화 시간
  45. 45 absl::BitGen — 모던 난수 생성기
  46. 46 Abseil Random Distributions — Uniform·Exponential
  47. 47 Abseil Mocking Random — 테스트 결정성
  48. 48 Abseil Random Seeding·Entropy
  49. 49 absl::int128·uint128 분석
  50. 50 absl::bits — popcount·countl_zero
  51. 51 absl::optional vs std::optional
  52. 52 absl::variant 분석
  53. 53 absl::span 분석
  54. 54 absl::any 분석
  55. 55 absl::compare — three-way 비교
  56. 56 Abseil utility — apply·in_place
  57. 57 Abseil AbslHashValue 분석
  58. 58 Abseil HashState chaining
  59. 59 Abseil Custom hashable 구현
  60. 60 Abseil LOG·VLOG·CHECK 분석
  61. 61 Abseil LogSink 분석
  62. 62 Abseil LogEntry·structured logging
  63. 63 Abseil Stack trace·failure_signal_handler
  64. 64 ABSL_FLAG 정의 분석
  65. 65 Abseil ParseCommandLine 동작
  66. 66 Abseil Flag introspection·validation
  67. 67 Google 스타일의 Abseil 사용 패턴
  68. 68 Abseil 자주 보는 anti-pattern
  69. 69 std → absl 마이그레이션 전략
  70. 70 absl::Cleanup — 함수 종료 시 실행 보장
  71. 71 Abseil algorithm container 확장 — c_sort·c_find_if·c_count_if
  72. 72 absl::function_ref와 any_invocable — 함수 객체 전달의 두 축
  73. 73 absl::bind_front와 Overload — 함수 객체 보조 도구
  74. 74 absl::Cord — 분산 시스템용 대용량 문자열
  75. 75 absl::from_chars·SimpleAtoi — 빠른 숫자 변환
  76. 76 absl::Cord vs std::string — 선택 기준과 메모리 프로파일
  77. 77 absl::GetStackTrace와 Symbolize — crash 시 readable stack
  78. 78 absl::ComputeCrc32c — 하드웨어 가속 체크섬
  79. 79 absl::PeriodicSampler — 적응형 샘플링·jitter 회피