mirror of
https://github.com/abseil/abseil-cpp.git
synced 2026-06-04 12:07:05 +08:00
219 lines
12 KiB
Markdown
219 lines
12 KiB
Markdown
# Abseil FAQ
|
|
|
|
## Is Abseil the right home for my utility library?
|
|
|
|
Most often the answer to the question is "no." As both the
|
|
[About Abseil](https://abseil.io/about/) page and our
|
|
[contributing guidelines](https://github.com/abseil/abseil-cpp/blob/master/CONTRIBUTING.md#contribution-guidelines)
|
|
explain, Abseil contains a variety of core C++ library code that is widely used
|
|
at [Google](https://www.google.com/). As such, Abseil's primary purpose is to be
|
|
used as a dependency by Google's open source C++ projects. While we do hope that
|
|
Abseil is also useful to the C++ community at large, this added constraint also
|
|
means that we are unlikely to accept a contribution of utility code that isn't
|
|
already widely used by Google.
|
|
|
|
## How do I set the C++ dialect used to build Abseil?
|
|
|
|
The short answer is that whatever mechanism you choose, you need to make sure
|
|
that you set this option consistently at the global level for your entire
|
|
project. If, for example, you want to set the C++ dialect to C++17, with
|
|
[Bazel](https://bazel.build/) as the build system and `gcc` or `clang` as the
|
|
compiler, there are several ways to do this: * Pass `--cxxopt=-std=c++17` on the
|
|
command line (for example, `bazel build --cxxopt=-std=c++17 ...`) * Set the
|
|
environment variable `BAZEL_CXXOPTS` (for example, `BAZEL_CXXOPTS=-std=c++17`) *
|
|
Add `build --cxxopt=-std=c++17` to your
|
|
[`.bazelrc` file](https://docs.bazel.build/versions/master/guide.html#bazelrc)
|
|
|
|
If you are using CMake as the build system, you'll need to add a line like
|
|
`set(CMAKE_CXX_STANDARD 17)` to your top level `CMakeLists.txt` file. If you are
|
|
developing a library designed to be used by other clients, you should instead
|
|
leave `CMAKE_CXX_STANDARD` unset and configure the minimum C++ standard required
|
|
by each of your library targets via `target_compile_features`. See the
|
|
[CMake build instructions](https://github.com/abseil/abseil-cpp/blob/master/CMake/README.md)
|
|
for more information.
|
|
|
|
For a longer answer to this question and to understand why some other approaches
|
|
don't work, see the answer to ["What is ABI and why don't you recommend using a
|
|
pre-compiled version of
|
|
Abseil?"](#what-is-abi-and-why-dont-you-recommend-using-a-pre-compiled-version-of-abseil)
|
|
|
|
## What is ABI and why don't you recommend using a pre-compiled version of Abseil?
|
|
|
|
For the purposes of this discussion,
|
|
[ABI](https://en.wikipedia.org/wiki/Application_binary_interface) refers to the
|
|
compiled representation of code interfaces. This contrasts with
|
|
[API](https://en.wikipedia.org/wiki/Application_programming_interface), which
|
|
refers to the interfaces defined in the source code. [Abseil has a strong
|
|
promise of API compatibility, but does not make any promise of ABI
|
|
compatibility](https://abseil.io/about/compatibility). Let's take a look at what
|
|
this means in practice.
|
|
|
|
You might be tempted to do something like this in a
|
|
[Bazel](https://bazel.build/) `BUILD` file:
|
|
|
|
```
|
|
# DON'T DO THIS!!!
|
|
cc_library(
|
|
name = "my_library",
|
|
srcs = ["my_library.cc"],
|
|
copts = ["-std=c++17"], # May create a mixed-mode compile!
|
|
deps = ["@com_google_absl//absl/strings"],
|
|
)
|
|
```
|
|
|
|
Applying `-std=c++17` to an individual target in your `BUILD` file is going to
|
|
compile that specific target in C++17 mode, but it isn't going to ensure the
|
|
Abseil library is built in C++17 mode, since the Abseil library itself is a
|
|
different build target. If your code includes an Abseil header, then your
|
|
program may contain conflicting definitions of the same
|
|
class/function/variable/enum, etc. As a rule, all compile options that affect
|
|
the ABI of a program need to be applied to the entire build on a global basis.
|
|
|
|
C++ has something called the
|
|
[One Definition Rule](https://en.wikipedia.org/wiki/One_Definition_Rule) (ODR).
|
|
C++ doesn't allow multiple definitions of the same class/function/variable/enum,
|
|
etc. ODR violations sometimes result in linker errors, but linkers do not always
|
|
catch violations. Uncaught ODR violations can result in strange runtime
|
|
behaviors or crashes that can be hard to debug.
|
|
|
|
If you build the Abseil library and your code using different compile options
|
|
that affect ABI, there is a good chance you will run afoul of the One Definition
|
|
Rule. Examples of GCC compile options that affect ABI include (but aren't
|
|
limited to) language dialect (e.g. `-std=`), optimization level (e.g. `-O2`),
|
|
code generation flags (e.g. `-fexceptions`), and preprocessor defines (e.g.
|
|
`-DNDEBUG`).
|
|
|
|
If you use a pre-compiled version of Abseil, (for example, from your Linux
|
|
distribution package manager or from something like
|
|
[vcpkg](https://github.com/microsoft/vcpkg)) you have to be very careful to
|
|
ensure ABI compatibility across the components of your program. The only way you
|
|
can be sure your program is going to be correct regarding ABI is to ensure
|
|
you've used the exact same compile options as were used to build the
|
|
pre-compiled library. This does not mean that Abseil cannot work as part of a
|
|
Linux distribution since a knowledgeable binary packager will have ensured that
|
|
all packages have been built with consistent compile options. This is one of the
|
|
reasons we warn against - though do not outright reject - using Abseil as a
|
|
pre-compiled library.
|
|
|
|
Another possible way that you might run afoul of ABI issues is if you
|
|
accidentally include two versions of Abseil in your program. Multiple versions
|
|
of Abseil can end up within the same binary if your program uses the Abseil
|
|
library and another library also transitively depends on Abseil (resulting in
|
|
what is sometimes called the diamond dependency problem). In cases such as this
|
|
you must structure your build so that all libraries use the same version of
|
|
Abseil.
|
|
[Abseil's strong promise of API compatibility between releases](https://abseil.io/about/compatibility)
|
|
means the latest "HEAD" release of Abseil is almost certainly the right choice
|
|
if you are doing as we recommend and building all of your code from source.
|
|
|
|
For these reasons we recommend you avoid pre-compiled code and build the Abseil
|
|
library yourself in a consistent manner with the rest of your code.
|
|
|
|
## What is "live at head" and how do I do it?
|
|
|
|
From Abseil's point-of-view, "live at head" means that every Abseil source
|
|
release (which happens on an almost daily basis) is either API compatible with
|
|
the previous release, or comes with an automated tool that you can run over code
|
|
to make it compatible. In practice, the need to use an automated tool is
|
|
extremely rare. This means that upgrading from one source release to another
|
|
should be a routine practice that can and should be performed often.
|
|
|
|
We recommend you update to the
|
|
[latest commit in the `master` branch of Abseil](https://github.com/abseil/abseil-cpp/commits/master)
|
|
as often as possible. Not only will you pick up bug fixes more quickly, but if
|
|
you have good automated testing, you will catch and be able to fix any
|
|
[Hyrum's Law](https://www.hyrumslaw.com/) dependency problems on an incremental
|
|
basis instead of being overwhelmed by them and having difficulty isolating them
|
|
if you wait longer between updates.
|
|
|
|
If you are using the [Bazel](https://bazel.build/) build system with
|
|
[Bzlmod](https://bazel.build/external/overview#bzlmod), you can use a
|
|
`git_override` in your `MODULE.bazel` file to track the latest commit.
|
|
|
|
For example, to update to the latest commit, you would add (or update) the
|
|
following snippet in your `MODULE.bazel` file:
|
|
|
|
```starlark
|
|
bazel_dep(name = "abseil-cpp", version = "20260107.1")
|
|
|
|
git_override(
|
|
module_name = "abseil-cpp",
|
|
remote = "https://github.com/abseil/abseil-cpp.git",
|
|
# Replace the following line with the latest commit.
|
|
commit = "6ec9964c325db0610a376b3cb81de073ea6ada90",
|
|
)
|
|
```
|
|
|
|
You can commit the updated `MODULE.bazel` file to your source control every time
|
|
you update, and if you have good automated testing, you might even consider
|
|
automating this.
|
|
|
|
## Why do I see strange behaviors when I use Abseil hash tables?
|
|
|
|
Abseil's hash function uses a random seed.
|
|
|
|
Many programmers believe incorrectly that this is a defense against
|
|
[hash flooding](https://en.wikipedia.org/wiki/Collision_attack#Hash_flooding).
|
|
While it does make a hash flooding attack more difficult, Abseil's hash function
|
|
prioritizes speed over thorough mixing (and thus is not cryptographically
|
|
secure), and the current seed implementation is also not cryptographically
|
|
secure. If you are storing a large amount of attacker-controlled data, the most
|
|
reliable defense against hash-flooding is to use a container that does not have
|
|
`O(n)` worst-case behavior.
|
|
|
|
The real reason for hash randomization is to prevent
|
|
[Hyrum's Law](https://www.hyrumslaw.com/) dependencies on iteration order. This
|
|
has allowed us to roll out steady improvements to the implementation without
|
|
breaking users who may have otherwise written code that was dependent on
|
|
ordering or other characteristics.
|
|
|
|
The current implementation uses a global seed, which, if linked incorrectly
|
|
(e.g., static Abseil in multiple DSOs), can cause the ODR violations. If more
|
|
than one seed is linked, different calls to the hash function may return
|
|
different values, rendering hash elements inaccessible, causing crashes, or
|
|
other arbitrarily bad behaviors.
|
|
|
|
We are often asked for a knob to disable hash randomization. The answer is a
|
|
hard "no", even under test or under a flag, because people will find a way to
|
|
force it and allow their code or tests to depend on it. At Google-scale, the
|
|
compute costs that are saved by preserving the ability to improve the
|
|
implementation far outweigh the inconvenience of learning how to write code
|
|
resilient to change.
|
|
|
|
## How do I use the LLVM Sanitizers with Abseil?
|
|
|
|
LLVM Sanitizers are a suite of powerful, dynamic analysis tools that
|
|
automatically detect various critical bugs during program execution. They work
|
|
by instrumenting the compiled binary and linking a runtime library to intercept
|
|
operations and report issues.
|
|
|
|
We receive many incorrect bug reports from users trying to use the sanitizers.
|
|
The most common cause of these issues is ODR violations in the form of an
|
|
instrumentation mismatch. This happens when users try to link libraries compiled
|
|
with sanitizer instrumentation with uninstrumented libraries. It is important
|
|
that all code in the application is built with the same sanitizer configuration.
|
|
|
|
The easiest way to do this is to use [Bazel](https://bazel.build) and pass the
|
|
sanitizer options on the commandline, but it is important not to overlook the
|
|
importance of avoiding precompiled system libraries, including the C++ standard
|
|
library. For instance,
|
|
[MemorySanitizer requires an instrumented `libc++`](https://github.com/google/sanitizers/wiki/MemorySanitizerLibcxxHowTo).
|
|
|
|
Since most users are not going to build `libc++` with Bazel, here is a
|
|
MemorySanitizer recipe that currently works (and could easily be tweaked for
|
|
ThreadSanitizer and friends):
|
|
|
|
```shell
|
|
# From the root of the LLVM source tree, configure libc++ to be instrumented with MSAN:
|
|
cmake -G Ninja -S runtimes -B build_msan -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="${HOME}/llvm-msan" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DLLVM_USE_SANITIZER=MemoryWithOrigins -DLLVM_TARGETS_TO_BUILD="host"
|
|
|
|
# Build and install it into ${HOME}/llvm-msan
|
|
ninja -C build_msan install-cxx install-cxxabi install-unwind
|
|
|
|
# Then build (or test) your code like this:
|
|
bazel test --repo_env=CC=clang --repo_env=BAZEL_CXXOPTS=nostdinc++ --repo_env=BAZEL_LINKOPTS=-L${HOME}/llvm-msan/lib:-lc++:-lc++abi:-lgcc_s:-lm:-Wl,-rpath=${HOME}/llvm-msan/lib --repo_env=CPLUS_INCLUDE_PATH=${HOME}/llvm-msan/include/c++/v1 --copt=-fsanitize=memory --linkopt=-fsanitize=memory --linkopt=-fsanitize-link-c++-runtime ...
|
|
```
|
|
|
|
You should consider adding these options to a
|
|
[`.bazelrc`](https://bazel.build/run/bazelrc) file to avoid retyping them.
|