At compile time, we sometimes need to conditionally compile code based on
whether a language feature is provided by the underlying platform or compiler
for various purposes, such as portability or performance. In such cases, we
define feature check macros in
absl/config.h to check whether the feature is
available or missing. Introducing a feature check macro is preferable to writing
a complex conditional directly because it explicitly states which feature it is
checking and makes the code more readable and maintainable.
Two types of feature check macros exist:
In Abseil, feature check macros are named with an
ABSL_* prefix and defined to
1 for a “true” state, and undefined for a “false” state. Defining them in this
manner allows you to use either
#ifdef constructs interchangeably.
#ifdef ABSL_HAVE_FEATURE_FOO #error "ABSL_HAVE_FEATURE_FOO cannot be overridden." #elif <complex preprocessor conditional> #define ABSL_HAVE_FEATURE_FOO 1 #endif
Feature check macros are used to conditionally compile code.
#if ABSL_HAVE_FEATURE_FOO // or #ifdef ABSL_HAVE_FEATURE_FOO // Handle the case where feature foo is available. #else // Handle the case where feature foo is missing. #endif
Feature check macros are not allowed to be overridden (
-D) on the compiler
command line. They should only be derived from pre-defined macros, other feature
check macros, or build configuration macros. For reusability, a feature check
macro should only be added to
absl/base/config.h if it is used in at least
three distinct modules.
#ifdef have different results when a macro is defined to
We explicitly state that a feature check macro should be undefined for a
“false” state so both
#ifdef can be used interchangeably.
When writing preprocessor conditionals, you can either opt into use of a feature by explicitly listing all platforms where a given feature is available, or opt out by explicitly listing all platforms where a given feature is missing or broken. Either option has pros and cons:
When choosing a opt-in or opt-out approach, think about what is likely to happen when a new platform is added and which approach is easier to maintain the invariant of your module.
If you are working around a compiler bug or a missing C++ feature in the standard library implementation, you probably want to deny-list the combinations where the feature is missing. When a new platform is added, you can assume it is standards-compliant, and your unit test will fail if it is not.
If you are relying on a low-level OS feature for better functionality (more logging, better debugging context), you probably want to allow-list the operating systems that claim to support this feature. When a new OS is added, you can assume the feature is missing, and your module will continue to work with minimum functionality. If desired, a developer can decide to opt-in by extending the allow-list.