aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--CMake/AbseilHelpers.cmake2
-rw-r--r--CMake/CMakeLists.txt.in15
-rw-r--r--CMake/DownloadGTest.cmake32
-rw-r--r--CMake/README.md32
-rw-r--r--CMakeLists.txt22
-rw-r--r--WORKSPACE15
-rw-r--r--absl/algorithm/BUILD.bazel12
-rw-r--r--absl/algorithm/equal_benchmark.cc128
-rw-r--r--absl/base/BUILD.bazel25
-rw-r--r--absl/base/CMakeLists.txt33
-rw-r--r--absl/base/exception_safety_testing_test.cc21
-rw-r--r--absl/base/internal/atomic_hook.h59
-rw-r--r--absl/base/internal/atomic_hook_test.cc70
-rw-r--r--absl/base/internal/exception_safety_testing.h16
-rw-r--r--absl/base/internal/thread_identity_benchmark.cc40
-rw-r--r--absl/container/BUILD.bazel24
-rw-r--r--absl/container/fixed_array_benchmark.cc68
-rw-r--r--absl/container/inlined_vector_benchmark.cc376
-rw-r--r--absl/debugging/CMakeLists.txt41
-rw-r--r--absl/debugging/failure_signal_handler.cc1
-rw-r--r--absl/meta/CMakeLists.txt5
-rw-r--r--absl/strings/str_join_test.cc1
-rw-r--r--absl/time/internal/cctz/BUILD.bazel7
-rw-r--r--absl/time/internal/cctz/include/cctz/civil_time_detail.h4
-rw-r--r--absl/time/internal/cctz/src/civil_time_test.cc4
-rw-r--r--absl/time/internal/cctz/src/time_zone_fixed.cc34
-rw-r--r--absl/time/internal/cctz/src/time_zone_lookup.cc3
-rw-r--r--absl/time/internal/cctz/src/time_zone_lookup_test.cc12
-rw-r--r--absl/time/internal/test_util.cc3
-rw-r--r--absl/types/BUILD.bazel15
-rw-r--r--absl/types/CMakeLists.txt14
-rw-r--r--absl/types/optional.h38
-rw-r--r--absl/types/variant.h2
-rw-r--r--absl/types/variant_exception_safety_test.cc519
-rw-r--r--absl/utility/CMakeLists.txt9
35 files changed, 1583 insertions, 119 deletions
diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake
index 0520fba..7b6f087 100644
--- a/CMake/AbseilHelpers.cmake
+++ b/CMake/AbseilHelpers.cmake
@@ -139,7 +139,7 @@ function(absl_test)
PRIVATE ${GMOCK_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS}
)
- add_test(${_NAME}_test ${_NAME}_bin)
+ add_test(${_NAME} ${_NAME}_bin)
endif(BUILD_TESTING)
endfunction()
diff --git a/CMake/CMakeLists.txt.in b/CMake/CMakeLists.txt.in
new file mode 100644
index 0000000..d60a33e
--- /dev/null
+++ b/CMake/CMakeLists.txt.in
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 2.8.2)
+
+project(googletest-download NONE)
+
+include(ExternalProject)
+ExternalProject_Add(googletest
+ GIT_REPOSITORY https://github.com/google/googletest.git
+ GIT_TAG master
+ SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src"
+ BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build"
+ CONFIGURE_COMMAND ""
+ BUILD_COMMAND ""
+ INSTALL_COMMAND ""
+ TEST_COMMAND ""
+) \ No newline at end of file
diff --git a/CMake/DownloadGTest.cmake b/CMake/DownloadGTest.cmake
new file mode 100644
index 0000000..9d41321
--- /dev/null
+++ b/CMake/DownloadGTest.cmake
@@ -0,0 +1,32 @@
+# Downloads and unpacks googletest at configure time. Based on the instructions
+# at https://github.com/google/googletest/tree/master/googletest#incorporating-into-an-existing-cmake-project
+
+# Download the latest googletest from Github master
+configure_file(
+ ${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt.in
+ googletest-download/CMakeLists.txt
+)
+
+# Configure and build the downloaded googletest source
+execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
+ RESULT_VARIABLE result
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
+if(result)
+ message(FATAL_ERROR "CMake step for googletest failed: ${result}")
+endif()
+
+execute_process(COMMAND ${CMAKE_COMMAND} --build .
+ RESULT_VARIABLE result
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download)
+if(result)
+ message(FATAL_ERROR "Build step for googletest failed: ${result}")
+endif()
+
+# Prevent overriding the parent project's compiler/linker settings on Windows
+set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
+
+# Add googletest directly to our build. This defines the gtest and gtest_main
+# targets.
+add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src
+ ${CMAKE_BINARY_DIR}/googletest-build
+ EXCLUDE_FROM_ALL)
diff --git a/CMake/README.md b/CMake/README.md
index e99340c..79bbe24 100644
--- a/CMake/README.md
+++ b/CMake/README.md
@@ -52,14 +52,42 @@ if(MSVC)
add_definitions(/DNOMINMAX /DWIN32_LEAN_AND_MEAN=1 /D_CRT_SECURE_NO_WARNINGS)
endif()
-add_subdirectory(googletest)
-add_subdirectory(cctz)
add_subdirectory(abseil-cpp)
add_executable(my_exe source.cpp)
target_link_libraries(my_exe absl::base absl::synchronization absl::strings)
```
+### Running Abseil Tests with CMake
+
+Use the `-DABSL_RUN_TESTS=ON` flag to run Abseil tests. Note that if the `-DBUILD_TESTING=OFF` flag is passed then Abseil tests will not be run.
+
+You will need to provide Abseil with a Googletest dependency. There are two
+options for how to do this:
+
+* Use `-DABSL_USE_GOOGLETEST_HEAD`. This will automatically download the latest
+Googletest source into the build directory at configure time. Googletest will
+then be compiled directly alongside Abseil's tests.
+* Manually integrate Googletest with your build. See
+https://github.com/google/googletest/blob/master/googletest/README.md#using-cmake
+for more information on using Googletest in a CMake project.
+
+For example, to run just the Abseil tests, you could use this script:
+
+```
+cd path/to/abseil-cpp
+mkdir build
+cd build
+cmake -DABSL_USE_GOOGLETEST_HEAD=ON -DABSL_RUN_TESTS=ON ..
+make -j
+ctest
+```
+
+Currently, we only run our tests with CMake in a Linux environment, but we are
+working on the rest of our supported platforms. See
+https://github.com/abseil/abseil-cpp/projects/1 and
+https://github.com/abseil/abseil-cpp/issues/109 for more information.
+
### Available Abseil CMake Public Targets
Here's a non-exhaustive list of Abseil CMake public targets:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e46bdf4..1a8f862 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -16,9 +16,6 @@
cmake_minimum_required(VERSION 2.8.12)
project(absl)
-# enable ctest
-include(CTest)
-
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMake)
include(GNUInstallDirs)
@@ -65,12 +62,25 @@ set(ABSL_EXCEPTIONS_FLAG "${CMAKE_CXX_EXCEPTIONS}")
## pthread
find_package(Threads REQUIRED)
-# commented: used only for standalone test
-# Don't remove these or else CMake CI will break
-#add_subdirectory(googletest)
+option(ABSL_USE_GOOGLETEST_HEAD
+ "If ON, abseil will download HEAD from googletest at config time." OFF)
+
+option(ABSL_RUN_TESTS "If ON, Abseil tests will be run." OFF)
+
+if(${ABSL_RUN_TESTS})
+ # enable CTest. This will set BUILD_TESTING to ON unless otherwise specified
+ # on the command line
+ include(CTest)
+ enable_testing()
+endif()
## check targets
if(BUILD_TESTING)
+
+ if(${ABSL_USE_GOOGLETEST_HEAD})
+ include(CMake/DownloadGTest.cmake)
+ endif()
+
check_target(gtest)
check_target(gtest_main)
check_target(gmock)
diff --git a/WORKSPACE b/WORKSPACE
index 0546573..0644593 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -3,11 +3,11 @@ workspace(name = "com_google_absl")
http_archive(
name = "bazel_toolchains",
urls = [
- "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/archive/r324073.tar.gz",
- "https://github.com/bazelbuild/bazel-toolchains/archive/r324073.tar.gz",
+ "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/archive/f8847f64e6950e8ab9fde1c0aba768550b0d9ab2.tar.gz",
+ "https://github.com/bazelbuild/bazel-toolchains/archive/f8847f64e6950e8ab9fde1c0aba768550b0d9ab2.tar.gz",
],
- strip_prefix = "bazel-toolchains-r324073",
- sha256 = "71548c0d6cd53eddebbde4fa9962f5395e82645fb9992719e0890505b177f245",
+ strip_prefix = "bazel-toolchains-f8847f64e6950e8ab9fde1c0aba768550b0d9ab2",
+ sha256 = "794366f51fea224b3656a0b0f8f1518e739748646523a572fcd3d68614a0e670",
)
# GoogleTest/GoogleMock framework. Used by most unit-tests.
@@ -17,6 +17,13 @@ http_archive(
strip_prefix = "googletest-master",
)
+# Google benchmark.
+http_archive(
+ name = "com_github_google_benchmark",
+ urls = ["https://github.com/google/benchmark/archive/master.zip"],
+ strip_prefix = "benchmark-master",
+)
+
# RE2 regular-expression framework. Used by some unit-tests.
http_archive(
name = "com_googlesource_code_re2",
diff --git a/absl/algorithm/BUILD.bazel b/absl/algorithm/BUILD.bazel
index 255b986..5cd5496 100644
--- a/absl/algorithm/BUILD.bazel
+++ b/absl/algorithm/BUILD.bazel
@@ -41,6 +41,18 @@ cc_test(
],
)
+cc_binary(
+ name = "algorithm_benchmark",
+ testonly = 1,
+ srcs = ["equal_benchmark.cc"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":algorithm",
+ "//absl/base:core_headers",
+ "@com_github_google_benchmark//:benchmark",
+ ],
+)
+
cc_library(
name = "container",
hdrs = [
diff --git a/absl/algorithm/equal_benchmark.cc b/absl/algorithm/equal_benchmark.cc
new file mode 100644
index 0000000..fea6d13
--- /dev/null
+++ b/absl/algorithm/equal_benchmark.cc
@@ -0,0 +1,128 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstdint>
+#include <cstring>
+
+#include "absl/algorithm/algorithm.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+
+// The range of sequence sizes to benchmark.
+constexpr int kMinBenchmarkSize = 1024;
+constexpr int kMaxBenchmarkSize = 8 * 1024 * 1024;
+
+// A user-defined type for use in equality benchmarks. Note that we expect
+// std::memcmp to win for this type: libstdc++'s std::equal only defers to
+// memcmp for integral types. This is because it is not straightforward to
+// guarantee that std::memcmp would produce a result "as-if" compared by
+// operator== for other types (example gotchas: NaN floats, structs with
+// padding).
+struct EightBits {
+ explicit EightBits(int /* unused */) : data(0) {}
+ bool operator==(const EightBits& rhs) const { return data == rhs.data; }
+ uint8_t data;
+};
+
+template <typename T>
+void BM_absl_equal_benchmark(benchmark::State& state) {
+ std::vector<T> xs(state.range(0), T(0));
+ std::vector<T> ys = xs;
+ while (state.KeepRunning()) {
+ const bool same = absl::equal(xs.begin(), xs.end(), ys.begin(), ys.end());
+ benchmark::DoNotOptimize(same);
+ }
+}
+
+template <typename T>
+void BM_std_equal_benchmark(benchmark::State& state) {
+ std::vector<T> xs(state.range(0), T(0));
+ std::vector<T> ys = xs;
+ while (state.KeepRunning()) {
+ const bool same = std::equal(xs.begin(), xs.end(), ys.begin());
+ benchmark::DoNotOptimize(same);
+ }
+}
+
+template <typename T>
+void BM_memcmp_benchmark(benchmark::State& state) {
+ std::vector<T> xs(state.range(0), T(0));
+ std::vector<T> ys = xs;
+ while (state.KeepRunning()) {
+ const bool same =
+ std::memcmp(xs.data(), ys.data(), xs.size() * sizeof(T)) == 0;
+ benchmark::DoNotOptimize(same);
+ }
+}
+
+// The expectation is that the compiler should be able to elide the equality
+// comparison altogether for sufficiently simple types.
+template <typename T>
+void BM_absl_equal_self_benchmark(benchmark::State& state) {
+ std::vector<T> xs(state.range(0), T(0));
+ while (state.KeepRunning()) {
+ const bool same = absl::equal(xs.begin(), xs.end(), xs.begin(), xs.end());
+ benchmark::DoNotOptimize(same);
+ }
+}
+
+BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint8_t)
+ ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
+BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint8_t)
+ ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
+BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint8_t)
+ ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
+BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint8_t)
+ ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
+
+BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint16_t)
+ ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
+BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint16_t)
+ ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
+BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint16_t)
+ ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
+BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint16_t)
+ ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
+
+BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint32_t)
+ ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
+BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint32_t)
+ ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
+BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint32_t)
+ ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
+BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint32_t)
+ ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
+
+BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint64_t)
+ ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
+BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint64_t)
+ ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
+BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint64_t)
+ ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
+BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint64_t)
+ ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
+
+BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, EightBits)
+ ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
+BENCHMARK_TEMPLATE(BM_std_equal_benchmark, EightBits)
+ ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
+BENCHMARK_TEMPLATE(BM_memcmp_benchmark, EightBits)
+ ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
+BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, EightBits)
+ ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
+
+} // namespace
+
+BENCHMARK_MAIN();
diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel
index f9aac5a..bb7a598 100644
--- a/absl/base/BUILD.bazel
+++ b/absl/base/BUILD.bazel
@@ -148,6 +148,18 @@ cc_library(
)
cc_test(
+ name = "atomic_hook_test",
+ size = "small",
+ srcs = ["internal/atomic_hook_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":base",
+ ":core_headers",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
name = "bit_cast_test",
size = "small",
srcs = [
@@ -393,3 +405,16 @@ cc_test(
"@com_google_googletest//:gtest_main",
],
)
+
+cc_binary(
+ name = "thread_identity_benchmark",
+ testonly = 1,
+ srcs = ["internal/thread_identity_benchmark.cc"],
+ copts = ABSL_TEST_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":base",
+ "//absl/synchronization",
+ "@com_github_google_benchmark//:benchmark",
+ ],
+)
diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt
index 329a3d0..4564056 100644
--- a/absl/base/CMakeLists.txt
+++ b/absl/base/CMakeLists.txt
@@ -99,14 +99,18 @@ absl_library(
if(BUILD_TESTING)
# exception-safety testing library
- set(EXCEPTION_SAFETY_TESTING_SRC "internal/exception_safety_testing.cc")
+ set(EXCEPTION_SAFETY_TESTING_SRC
+ "internal/exception_safety_testing.h"
+ "internal/exception_safety_testing.cc"
+ )
set(EXCEPTION_SAFETY_TESTING_PUBLIC_LIBRARIES
${ABSL_TEST_COMMON_LIBRARIES}
absl::base
absl::memory
absl::meta
absl::strings
- absl::types
+ absl::optional
+ gtest
)
absl_library(
@@ -116,6 +120,8 @@ absl_library(
${EXCEPTION_SAFETY_TESTING_SRC}
PUBLIC_LIBRARIES
${EXCEPTION_SAFETY_TESTING_PUBLIC_LIBRARIES}
+ PRIVATE_COMPILE_FLAGS
+ ${ABSL_EXCEPTIONS_FLAG}
)
endif()
@@ -163,6 +169,20 @@ absl_library(
#
# call once test
+set(ATOMIC_HOOK_TEST_SRC "internal/atomic_hook_test.cc")
+set(ATOMIC_HOOK_TEST_PUBLIC_LIBRARIES absl::base)
+
+absl_test(
+ TARGET
+ atomic_hook_test
+ SOURCES
+ ${ATOMIC_HOOK_TEST_SRC}
+ PUBLIC_LIBRARIES
+ ${ATOMIC_HOOK_TEST_PUBLIC_LIBRARIES}
+)
+
+
+# call once test
set(CALL_ONCE_TEST_SRC "call_once_test.cc")
set(CALL_ONCE_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization)
@@ -344,7 +364,14 @@ absl_test(
#test exceptions_safety_testing_test
set(EXCEPTION_SAFETY_TESTING_TEST_SRC "exception_safety_testing_test.cc")
-set(EXCEPTION_SAFETY_TESTING_TEST_PUBLIC_LIBRARIES absl::base absl::memory absl::meta absl::strings absl::optional)
+set(EXCEPTION_SAFETY_TESTING_TEST_PUBLIC_LIBRARIES
+ absl::base
+ absl_base_internal_exception_safety_testing
+ absl::memory
+ absl::meta
+ absl::strings
+ absl::optional
+)
absl_test(
TARGET
diff --git a/absl/base/exception_safety_testing_test.cc b/absl/base/exception_safety_testing_test.cc
index 9bd8b9d..4507b94 100644
--- a/absl/base/exception_safety_testing_test.cc
+++ b/absl/base/exception_safety_testing_test.cc
@@ -83,6 +83,27 @@ TEST(ThrowingValueTest, ThrowingAssignment) {
TestOp([&]() { bomb = bomb1; });
TestOp([&]() { bomb = std::move(bomb1); });
+
+ // Test that when assignment throws, the assignment should fail (lhs != rhs)
+ // and strong guarantee fails (lhs != lhs_copy).
+ {
+ ThrowingValue<> lhs(39), rhs(42);
+ ThrowingValue<> lhs_copy(lhs);
+ SetCountdown();
+ EXPECT_THROW(lhs = rhs, TestException);
+ UnsetCountdown();
+ EXPECT_NE(lhs, rhs);
+ EXPECT_NE(lhs_copy, lhs);
+ }
+ {
+ ThrowingValue<> lhs(39), rhs(42);
+ ThrowingValue<> lhs_copy(lhs), rhs_copy(rhs);
+ SetCountdown();
+ EXPECT_THROW(lhs = std::move(rhs), TestException);
+ UnsetCountdown();
+ EXPECT_NE(lhs, rhs_copy);
+ EXPECT_NE(lhs_copy, lhs);
+ }
}
TEST(ThrowingValueTest, ThrowingComparisons) {
diff --git a/absl/base/internal/atomic_hook.h b/absl/base/internal/atomic_hook.h
index 47d4013..b458511 100644
--- a/absl/base/internal/atomic_hook.h
+++ b/absl/base/internal/atomic_hook.h
@@ -21,6 +21,12 @@
#include <cstdint>
#include <utility>
+#ifdef _MSC_FULL_VER
+#define ABSL_HAVE_WORKING_ATOMIC_POINTER 0
+#else
+#define ABSL_HAVE_WORKING_ATOMIC_POINTER 1
+#endif
+
namespace absl {
namespace base_internal {
@@ -29,9 +35,15 @@ class AtomicHook;
// AtomicHook is a helper class, templatized on a raw function pointer type, for
// implementing Abseil customization hooks. It is a callable object that
-// dispatches to the registered hook, or performs a no-op (and returns a default
+// dispatches to the registered hook.
+//
+// A default constructed object performs a no-op (and returns a default
// constructed object) if no hook has been registered.
//
+// Hooks can be pre-registered via constant initialization, for example,
+// ABSL_CONST_INIT static AtomicHook<void(*)()> my_hook(DefaultAction);
+// and then changed at runtime via a call to Store().
+//
// Reads and writes guarantee memory_order_acquire/memory_order_release
// semantics.
template <typename ReturnType, typename... Args>
@@ -39,7 +51,19 @@ class AtomicHook<ReturnType (*)(Args...)> {
public:
using FnPtr = ReturnType (*)(Args...);
- constexpr AtomicHook() : hook_(kInitialValue) {}
+ // Constructs an object that by default performs a no-op (and
+ // returns a default constructed object) when no hook as been registered.
+ constexpr AtomicHook() : AtomicHook(DummyFunction) {}
+
+ // Constructs an object that by default dispatches to/returns the
+ // pre-registered default_fn when no hook has been registered at runtime.
+#if ABSL_HAVE_WORKING_ATOMIC_POINTER
+ explicit constexpr AtomicHook(FnPtr default_fn)
+ : hook_(default_fn), default_fn_(default_fn) {}
+#else
+ explicit constexpr AtomicHook(FnPtr default_fn)
+ : hook_(kUninitialized), default_fn_(default_fn) {}
+#endif
// Stores the provided function pointer as the value for this hook.
//
@@ -86,16 +110,7 @@ class AtomicHook<ReturnType (*)(Args...)> {
//
// This causes an issue when building with LLVM under Windows. To avoid this,
// we use a less-efficient, intptr_t-based implementation on Windows.
-
-#ifdef _MSC_FULL_VER
-#define ABSL_HAVE_WORKING_ATOMIC_POINTER 0
-#else
-#define ABSL_HAVE_WORKING_ATOMIC_POINTER 1
-#endif
-
#if ABSL_HAVE_WORKING_ATOMIC_POINTER
- static constexpr FnPtr kInitialValue = &DummyFunction;
-
// Return the stored value, or DummyFunction if no value has been stored.
FnPtr DoLoad() const { return hook_.load(std::memory_order_acquire); }
@@ -103,10 +118,9 @@ class AtomicHook<ReturnType (*)(Args...)> {
// stored to this object.
bool DoStore(FnPtr fn) {
assert(fn);
- FnPtr expected = DummyFunction;
- hook_.compare_exchange_strong(expected, fn, std::memory_order_acq_rel,
- std::memory_order_acquire);
- const bool store_succeeded = (expected == DummyFunction);
+ FnPtr expected = default_fn_;
+ const bool store_succeeded = hook_.compare_exchange_strong(
+ expected, fn, std::memory_order_acq_rel, std::memory_order_acquire);
const bool same_value_already_stored = (expected == fn);
return store_succeeded || same_value_already_stored;
}
@@ -114,15 +128,15 @@ class AtomicHook<ReturnType (*)(Args...)> {
std::atomic<FnPtr> hook_;
#else // !ABSL_HAVE_WORKING_ATOMIC_POINTER
// Use a sentinel value unlikely to be the address of an actual function.
- static constexpr intptr_t kInitialValue = 0;
+ static constexpr intptr_t kUninitialized = 0;
static_assert(sizeof(intptr_t) >= sizeof(FnPtr),
"intptr_t can't contain a function pointer");
FnPtr DoLoad() const {
const intptr_t value = hook_.load(std::memory_order_acquire);
- if (value == 0) {
- return DummyFunction;
+ if (value == kUninitialized) {
+ return default_fn_;
}
return reinterpret_cast<FnPtr>(value);
}
@@ -130,16 +144,17 @@ class AtomicHook<ReturnType (*)(Args...)> {
bool DoStore(FnPtr fn) {
assert(fn);
const auto value = reinterpret_cast<intptr_t>(fn);
- intptr_t expected = 0;
- hook_.compare_exchange_strong(expected, value, std::memory_order_acq_rel,
- std::memory_order_acquire);
- const bool store_succeeded = (expected == 0);
+ intptr_t expected = kUninitialized;
+ const bool store_succeeded = hook_.compare_exchange_strong(
+ expected, value, std::memory_order_acq_rel, std::memory_order_acquire);
const bool same_value_already_stored = (expected == value);
return store_succeeded || same_value_already_stored;
}
std::atomic<intptr_t> hook_;
#endif
+
+ const FnPtr default_fn_;
};
#undef ABSL_HAVE_WORKING_ATOMIC_POINTER
diff --git a/absl/base/internal/atomic_hook_test.cc b/absl/base/internal/atomic_hook_test.cc
new file mode 100644
index 0000000..cf74075
--- /dev/null
+++ b/absl/base/internal/atomic_hook_test.cc
@@ -0,0 +1,70 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/base/internal/atomic_hook.h"
+
+#include "gtest/gtest.h"
+#include "absl/base/attributes.h"
+
+namespace {
+
+int value = 0;
+void TestHook(int x) { value = x; }
+
+TEST(AtomicHookTest, NoDefaultFunction) {
+ ABSL_CONST_INIT static absl::base_internal::AtomicHook<void(*)(int)> hook;
+ value = 0;
+
+ // Test the default DummyFunction.
+ EXPECT_TRUE(hook.Load() == nullptr);
+ EXPECT_EQ(value, 0);
+ hook(1);
+ EXPECT_EQ(value, 0);
+
+ // Test a stored hook.
+ hook.Store(TestHook);
+ EXPECT_TRUE(hook.Load() == TestHook);
+ EXPECT_EQ(value, 0);
+ hook(1);
+ EXPECT_EQ(value, 1);
+
+ // Calling Store() with the same hook should not crash.
+ hook.Store(TestHook);
+ EXPECT_TRUE(hook.Load() == TestHook);
+ EXPECT_EQ(value, 1);
+ hook(2);
+ EXPECT_EQ(value, 2);
+}
+
+TEST(AtomicHookTest, WithDefaultFunction) {
+ // Set the default value to TestHook at compile-time.
+ ABSL_CONST_INIT static absl::base_internal::AtomicHook<void (*)(int)> hook(
+ TestHook);
+ value = 0;
+
+ // Test the default value is TestHook.
+ EXPECT_TRUE(hook.Load() == TestHook);
+ EXPECT_EQ(value, 0);
+ hook(1);
+ EXPECT_EQ(value, 1);
+
+ // Calling Store() with the same hook should not crash.
+ hook.Store(TestHook);
+ EXPECT_TRUE(hook.Load() == TestHook);
+ EXPECT_EQ(value, 1);
+ hook(2);
+ EXPECT_EQ(value, 2);
+}
+
+} // namespace
diff --git a/absl/base/internal/exception_safety_testing.h b/absl/base/internal/exception_safety_testing.h
index 3245046..bec3ab3 100644
--- a/absl/base/internal/exception_safety_testing.h
+++ b/absl/base/internal/exception_safety_testing.h
@@ -164,7 +164,7 @@ class ConstructorTracker {
template <typename Factory, typename Operation, typename Invariant>
absl::optional<testing::AssertionResult> TestSingleInvariantAtCountdownImpl(
- const Factory& factory, const Operation& operation, int count,
+ const Factory& factory, Operation operation, int count,
const Invariant& invariant) {
auto t_ptr = factory();
absl::optional<testing::AssertionResult> current_res;
@@ -277,10 +277,12 @@ enum class TypeSpec {
*/
template <TypeSpec Spec = TypeSpec::kEverythingThrows>
class ThrowingValue : private exceptions_internal::TrackedObject {
- constexpr static bool IsSpecified(TypeSpec spec) {
+ static constexpr bool IsSpecified(TypeSpec spec) {
return static_cast<bool>(Spec & spec);
}
+ static constexpr int kBadValue = 938550620;
+
public:
ThrowingValue() : TrackedObject(ABSL_PRETTY_FUNCTION) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
@@ -318,6 +320,7 @@ class ThrowingValue : private exceptions_internal::TrackedObject {
ThrowingValue& operator=(const ThrowingValue& other) noexcept(
IsSpecified(TypeSpec::kNoThrowCopy)) {
+ dummy_ = kBadValue;
if (!IsSpecified(TypeSpec::kNoThrowCopy)) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
}
@@ -327,6 +330,7 @@ class ThrowingValue : private exceptions_internal::TrackedObject {
ThrowingValue& operator=(ThrowingValue&& other) noexcept(
IsSpecified(TypeSpec::kNoThrowMove)) {
+ dummy_ = kBadValue;
if (!IsSpecified(TypeSpec::kNoThrowMove)) {
exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
}
@@ -630,7 +634,7 @@ enum class AllocSpec {
*/
template <typename T, AllocSpec Spec = AllocSpec::kEverythingThrows>
class ThrowingAllocator : private exceptions_internal::TrackedObject {
- constexpr static bool IsSpecified(AllocSpec spec) {
+ static constexpr bool IsSpecified(AllocSpec spec) {
return static_cast<bool>(Spec & spec);
}
@@ -1030,6 +1034,12 @@ MakeExceptionSafetyTester() {
return {};
}
+// Always return false, intended to be used as a checker with
+// TestExceptionSafety() to check that no exception is thrown.
+inline bool nothrow_guarantee(const void*) {
+ return ::testing::AssertionFailure() << "Violating NoThrowGuarantee";
+}
+
} // namespace testing
#endif // ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_
diff --git a/absl/base/internal/thread_identity_benchmark.cc b/absl/base/internal/thread_identity_benchmark.cc
new file mode 100644
index 0000000..3ae5731
--- /dev/null
+++ b/absl/base/internal/thread_identity_benchmark.cc
@@ -0,0 +1,40 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/base/internal/thread_identity.h"
+#include "absl/synchronization/internal/create_thread_identity.h"
+#include "absl/synchronization/internal/per_thread_sem.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+
+void BM_SafeCurrentThreadIdentity(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(
+ absl::synchronization_internal::GetOrCreateCurrentThreadIdentity());
+ }
+}
+BENCHMARK(BM_SafeCurrentThreadIdentity);
+
+void BM_UnsafeCurrentThreadIdentity(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(
+ absl::base_internal::CurrentThreadIdentityIfPresent());
+ }
+}
+BENCHMARK(BM_UnsafeCurrentThreadIdentity);
+
+} // namespace
+
+BENCHMARK_MAIN();
diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel
index 8bdf631..69cd519 100644
--- a/absl/container/BUILD.bazel
+++ b/absl/container/BUILD.bazel
@@ -62,6 +62,17 @@ cc_test(
],
)
+cc_binary(
+ name = "fixed_array_benchmark",
+ testonly = 1,
+ srcs = ["fixed_array_benchmark.cc"],
+ copts = ABSL_TEST_COPTS + ["$(STACK_FRAME_UNLIMITED)"],
+ deps = [
+ ":fixed_array",
+ "@com_github_google_benchmark//:benchmark",
+ ],
+)
+
cc_library(
name = "inlined_vector",
hdrs = ["inlined_vector.h"],
@@ -106,6 +117,19 @@ cc_test(
],
)
+cc_binary(
+ name = "inlined_vector_benchmark",
+ testonly = 1,
+ srcs = ["inlined_vector_benchmark.cc"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":inlined_vector",
+ "//absl/base",
+ "//absl/strings",
+ "@com_github_google_benchmark//:benchmark",
+ ],
+)
+
cc_library(
name = "test_instance_tracker",
testonly = 1,
diff --git a/absl/container/fixed_array_benchmark.cc b/absl/container/fixed_array_benchmark.cc
new file mode 100644
index 0000000..2d39898
--- /dev/null
+++ b/absl/container/fixed_array_benchmark.cc
@@ -0,0 +1,68 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/container/fixed_array.h"
+
+#include <stddef.h>
+#include <string>
+
+#include "benchmark/benchmark.h"
+
+namespace {
+
+// For benchmarking -- simple class with constructor and destructor that
+// set an int to a constant..
+class SimpleClass {
+ public:
+ SimpleClass() : i(3) { }
+ ~SimpleClass() { i = 0; }
+ private:
+ int i;
+};
+
+template <typename C, size_t stack_size>
+void BM_FixedArray(benchmark::State& state) {
+ const int size = state.range(0);
+ for (auto _ : state) {
+ absl::FixedArray<C, stack_size> fa(size);
+ benchmark::DoNotOptimize(fa.data());
+ }
+}
+BENCHMARK_TEMPLATE(BM_FixedArray, char, absl::kFixedArrayUseDefault)
+ ->Range(0, 1 << 16);
+BENCHMARK_TEMPLATE(BM_FixedArray, char, 0)->Range(0, 1 << 16);
+BENCHMARK_TEMPLATE(BM_FixedArray, char, 1)->Range(0, 1 << 16);
+BENCHMARK_TEMPLATE(BM_FixedArray, char, 16)->Range(0, 1 << 16);
+BENCHMARK_TEMPLATE(BM_FixedArray, char, 256)->Range(0, 1 << 16);
+BENCHMARK_TEMPLATE(BM_FixedArray, char, 65536)->Range(0, 1 << 16);
+
+BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, absl::kFixedArrayUseDefault)
+ ->Range(0, 1 << 16);
+BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, 0)->Range(0, 1 << 16);
+BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, 1)->Range(0, 1 << 16);
+BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, 16)->Range(0, 1 << 16);
+BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, 256)->Range(0, 1 << 16);
+BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, 65536)->Range(0, 1 << 16);
+
+BENCHMARK_TEMPLATE(BM_FixedArray, std::string, absl::kFixedArrayUseDefault)
+ ->Range(0, 1 << 16);
+BENCHMARK_TEMPLATE(BM_FixedArray, std::string, 0)->Range(0, 1 << 16);
+BENCHMARK_TEMPLATE(BM_FixedArray, std::string, 1)->Range(0, 1 << 16);
+BENCHMARK_TEMPLATE(BM_FixedArray, std::string, 16)->Range(0, 1 << 16);
+BENCHMARK_TEMPLATE(BM_FixedArray, std::string, 256)->Range(0, 1 << 16);
+BENCHMARK_TEMPLATE(BM_FixedArray, std::string, 65536)->Range(0, 1 << 16);
+
+} // namespace
+
+BENCHMARK_MAIN();
diff --git a/absl/container/inlined_vector_benchmark.cc b/absl/container/inlined_vector_benchmark.cc
new file mode 100644
index 0000000..a2035e3
--- /dev/null
+++ b/absl/container/inlined_vector_benchmark.cc
@@ -0,0 +1,376 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/container/inlined_vector.h"
+
+#include <string>
+#include <vector>
+
+#include "absl/base/internal/raw_logging.h"
+#include "absl/strings/str_cat.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+
+using IntVec = absl::InlinedVector<int, 8>;
+
+void BM_InlinedVectorFill(benchmark::State& state) {
+ const int len = state.range(0);
+ for (auto _ : state) {
+ IntVec v;
+ for (int i = 0; i < len; i++) {
+ v.push_back(i);
+ }
+ }
+ state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len);
+}
+BENCHMARK(BM_InlinedVectorFill)->Range(0, 1024);
+
+void BM_InlinedVectorFillRange(benchmark::State& state) {
+ const int len = state.range(0);
+ std::unique_ptr<int[]> ia(new int[len]);
+ for (int i = 0; i < len; i++) {
+ ia[i] = i;
+ }
+ for (auto _ : state) {
+ IntVec v(ia.get(), ia.get() + len);
+ benchmark::DoNotOptimize(v);
+ }
+ state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len);
+}
+BENCHMARK(BM_InlinedVectorFillRange)->Range(0, 1024);
+
+void BM_StdVectorFill(benchmark::State& state) {
+ const int len = state.range(0);
+ for (auto _ : state) {
+ std::vector<int> v;
+ for (int i = 0; i < len; i++) {
+ v.push_back(i);
+ }
+ }
+ state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len);
+}
+BENCHMARK(BM_StdVectorFill)->Range(0, 1024);
+
+bool StringRepresentedInline(std::string s) {
+ const char* chars = s.data();
+ std::string s1 = std::move(s);
+ return s1.data() != chars;
+}
+
+void BM_InlinedVectorFillString(benchmark::State& state) {
+ const int len = state.range(0);
+ std::string strings[4] = {"a quite long string",
+ "another long string",
+ "012345678901234567",
+ "to cause allocation"};
+ for (auto _ : state) {
+ absl::InlinedVector<std::string, 8> v;
+ for (int i = 0; i < len; i++) {
+ v.push_back(strings[i & 3]);
+ }
+ }
+ state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len);
+}
+BENCHMARK(BM_InlinedVectorFillString)->Range(0, 1024);
+
+void BM_StdVectorFillString(benchmark::State& state) {
+ const int len = state.range(0);
+ std::string strings[4] = {"a quite long string",
+ "another long string",
+ "012345678901234567",
+ "to cause allocation"};
+ for (auto _ : state) {
+ std::vector<std::string> v;
+ for (int i = 0; i < len; i++) {
+ v.push_back(strings[i & 3]);
+ }
+ }
+ state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len);
+ // The purpose of the benchmark is to verify that inlined vector is
+ // efficient when moving is more efficent than copying. To do so, we
+ // use strings that are larger than the small std::string optimization.
+ ABSL_RAW_CHECK(!StringRepresentedInline(strings[0]),
+ "benchmarked with strings that are too small");
+}
+BENCHMARK(BM_StdVectorFillString)->Range(0, 1024);
+
+struct Buffer { // some arbitrary structure for benchmarking.
+ char* base;
+ int length;
+ int capacity;
+ void* user_data;
+};
+
+void BM_InlinedVectorTenAssignments(benchmark::State& state) {
+ const int len = state.range(0);
+ using BufferVec = absl::InlinedVector<Buffer, 2>;
+
+ BufferVec src;
+ src.resize(len);
+
+ BufferVec dst;
+ for (auto _ : state) {
+ for (int i = 0; i < 10; ++i) {
+ dst = src;
+ }
+ }
+}
+BENCHMARK(BM_InlinedVectorTenAssignments)
+ ->Arg(0)->Arg(1)->Arg(2)->Arg(3)->Arg(4)->Arg(20);
+
+void BM_CreateFromContainer(benchmark::State& state) {
+ for (auto _ : state) {
+ absl::InlinedVector<int, 4> x(absl::InlinedVector<int, 4>{1, 2, 3});
+ benchmark::DoNotOptimize(x);
+ }
+}
+BENCHMARK(BM_CreateFromContainer);
+
+struct LargeCopyableOnly {
+ LargeCopyableOnly() : d(1024, 17) {}
+ LargeCopyableOnly(const LargeCopyableOnly& o) = default;
+ LargeCopyableOnly& operator=(const LargeCopyableOnly& o) = default;
+
+ std::vector<int> d;
+};
+
+struct LargeCopyableSwappable {
+ LargeCopyableSwappable() : d(1024, 17) {}
+ LargeCopyableSwappable(const LargeCopyableSwappable& o) = default;
+ LargeCopyableSwappable(LargeCopyableSwappable&& o) = delete;
+
+ LargeCopyableSwappable& operator=(LargeCopyableSwappable o) {
+ using std::swap;
+ swap(*this, o);
+ return *this;
+ }
+ LargeCopyableSwappable& operator=(LargeCopyableSwappable&& o) = delete;
+
+ friend void swap(LargeCopyableSwappable& a, LargeCopyableSwappable& b) {
+ using std::swap;
+ swap(a.d, b.d);
+ }
+
+ std::vector<int> d;
+};
+
+struct LargeCopyableMovable {
+ LargeCopyableMovable() : d(1024, 17) {}
+ // Use implicitly defined copy and move.
+
+ std::vector<int> d;
+};
+
+struct LargeCopyableMovableSwappable {
+ LargeCopyableMovableSwappable() : d(1024, 17) {}
+ LargeCopyableMovableSwappable(const LargeCopyableMovableSwappable& o) =
+ default;
+ LargeCopyableMovableSwappable(LargeCopyableMovableSwappable&& o) = default;
+
+ LargeCopyableMovableSwappable& operator=(LargeCopyableMovableSwappable o) {
+ using std::swap;
+ swap(*this, o);
+ return *this;
+ }
+ LargeCopyableMovableSwappable& operator=(LargeCopyableMovableSwappable&& o) =
+ default;
+
+ friend void swap(LargeCopyableMovableSwappable& a,
+ LargeCopyableMovableSwappable& b) {
+ using std::swap;
+ swap(a.d, b.d);
+ }
+
+ std::vector<int> d;
+};
+
+template <typename ElementType>
+void BM_SwapElements(benchmark::State& state) {
+ const int len = state.range(0);
+ using Vec = absl::InlinedVector<ElementType, 32>;
+ Vec a(len);
+ Vec b;
+ for (auto _ : state) {
+ using std::swap;
+ swap(a, b);
+ }
+}
+BENCHMARK_TEMPLATE(BM_SwapElements, LargeCopyableOnly)->Range(0, 1024);
+BENCHMARK_TEMPLATE(BM_SwapElements, LargeCopyableSwappable)->Range(0, 1024);
+BENCHMARK_TEMPLATE(BM_SwapElements, LargeCopyableMovable)->Range(0, 1024);
+BENCHMARK_TEMPLATE(BM_SwapElements, LargeCopyableMovableSwappable)
+ ->Range(0, 1024);
+
+// The following benchmark is meant to track the efficiency of the vector size
+// as a function of stored type via the benchmark label. It is not meant to
+// output useful sizeof operator performance. The loop is a dummy operation
+// to fulfill the requirement of running the benchmark.
+template <typename VecType>
+void BM_Sizeof(benchmark::State& state) {
+ int size = 0;
+ for (auto _ : state) {
+ VecType vec;
+ size = sizeof(vec);
+ }
+ state.SetLabel(absl::StrCat("sz=", size));
+}
+BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<char, 1>);
+BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<char, 4>);
+BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<char, 7>);
+BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<char, 8>);
+
+BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<int, 1>);
+BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<int, 4>);
+BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<int, 7>);
+BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<int, 8>);
+
+BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<void*, 1>);
+BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<void*, 4>);
+BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<void*, 7>);
+BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<void*, 8>);
+
+BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<std::string, 1>);
+BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<std::string, 4>);
+BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<std::string, 7>);
+BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<std::string, 8>);
+
+void BM_InlinedVectorIndexInlined(benchmark::State& state) {
+ absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7};
+ for (auto _ : state) {
+ for (int i = 0; i < 1000; ++i) {
+ benchmark::DoNotOptimize(v);
+ benchmark::DoNotOptimize(v[4]);
+ }
+ }
+ state.SetItemsProcessed(1000 * static_cast<int64_t>(state.iterations()));
+}
+BENCHMARK(BM_InlinedVectorIndexInlined);
+
+void BM_InlinedVectorIndexExternal(benchmark::State& state) {
+ absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ for (auto _ : state) {
+ for (int i = 0; i < 1000; ++i) {
+ benchmark::DoNotOptimize(v);
+ benchmark::DoNotOptimize(v[4]);
+ }
+ }
+ state.SetItemsProcessed(1000 * static_cast<int64_t>(state.iterations()));
+}
+BENCHMARK(BM_InlinedVectorIndexExternal);
+
+void BM_StdVectorIndex(benchmark::State& state) {
+ std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ for (auto _ : state) {
+ for (int i = 0; i < 1000; ++i) {
+ benchmark::DoNotOptimize(v);
+ benchmark::DoNotOptimize(v[4]);
+ }
+ }
+ state.SetItemsProcessed(1000 * static_cast<int64_t>(state.iterations()));
+}
+BENCHMARK(BM_StdVectorIndex);
+
+#define UNROLL_2(x) \
+ benchmark::DoNotOptimize(x); \
+ benchmark::DoNotOptimize(x);
+
+#define UNROLL_4(x) UNROLL_2(x) UNROLL_2(x)
+#define UNROLL_8(x) UNROLL_4(x) UNROLL_4(x)
+#define UNROLL_16(x) UNROLL_8(x) UNROLL_8(x);
+
+void BM_InlinedVectorDataInlined(benchmark::State& state) {
+ absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7};
+ for (auto _ : state) {
+ UNROLL_16(v.data());
+ }
+ state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
+}
+BENCHMARK(BM_InlinedVectorDataInlined);
+
+void BM_InlinedVectorDataExternal(benchmark::State& state) {
+ absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ for (auto _ : state) {
+ UNROLL_16(v.data());
+ }
+ state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
+}
+BENCHMARK(BM_InlinedVectorDataExternal);
+
+void BM_StdVectorData(benchmark::State& state) {
+ std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ for (auto _ : state) {
+ UNROLL_16(v.data());
+ }
+ state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
+}
+BENCHMARK(BM_StdVectorData);
+
+void BM_InlinedVectorSizeInlined(benchmark::State& state) {
+ absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7};
+ for (auto _ : state) {
+ UNROLL_16(v.size());
+ }
+ state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
+}
+BENCHMARK(BM_InlinedVectorSizeInlined);
+
+void BM_InlinedVectorSizeExternal(benchmark::State& state) {
+ absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ for (auto _ : state) {
+ UNROLL_16(v.size());
+ }
+ state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
+}
+BENCHMARK(BM_InlinedVectorSizeExternal);
+
+void BM_StdVectorSize(benchmark::State& state) {
+ std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ for (auto _ : state) {
+ UNROLL_16(v.size());
+ }
+ state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
+}
+BENCHMARK(BM_StdVectorSize);
+
+void BM_InlinedVectorEmptyInlined(benchmark::State& state) {
+ absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7};
+ for (auto _ : state) {
+ UNROLL_16(v.empty());
+ }
+ state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
+}
+BENCHMARK(BM_InlinedVectorEmptyInlined);
+
+void BM_InlinedVectorEmptyExternal(benchmark::State& state) {
+ absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ for (auto _ : state) {
+ UNROLL_16(v.empty());
+ }
+ state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
+}
+BENCHMARK(BM_InlinedVectorEmptyExternal);
+
+void BM_StdVectorEmpty(benchmark::State& state) {
+ std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ for (auto _ : state) {
+ UNROLL_16(v.empty());
+ }
+ state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
+}
+BENCHMARK(BM_StdVectorEmpty);
+
+} // namespace
+
+BENCHMARK_MAIN();
diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt
index 03a0a61..4af2ec8 100644
--- a/absl/debugging/CMakeLists.txt
+++ b/absl/debugging/CMakeLists.txt
@@ -21,7 +21,8 @@ list(APPEND DEBUGGING_PUBLIC_HEADERS
"symbolize.h"
)
-
+# TODO(cohenjon) The below is all kinds of wrong. Make this match what we do in
+# Bazel
list(APPEND DEBUGGING_INTERNAL_HEADERS
"internal/address_is_readable.h"
"internal/demangle.h"
@@ -32,12 +33,16 @@ list(APPEND DEBUGGING_INTERNAL_HEADERS
"internal/vdso_support.h"
)
-
-list(APPEND STACKTRACE_SRC
- "stacktrace.cc"
+list(APPEND DEBUGGING_INTERNAL_SRC
"internal/address_is_readable.cc"
"internal/elf_mem_image.cc"
"internal/vdso_support.cc"
+)
+
+
+list(APPEND STACKTRACE_SRC
+ "stacktrace.cc"
+ ${DEBUGGING_INTERNAL_SRC}
${DEBUGGING_PUBLIC_HEADERS}
${DEBUGGING_INTERNAL_HEADERS}
)
@@ -50,6 +55,7 @@ list(APPEND SYMBOLIZE_SRC
"internal/demangle.cc"
${DEBUGGING_PUBLIC_HEADERS}
${DEBUGGING_INTERNAL_HEADERS}
+ ${DEBUGGING_INTERNAL_SRC}
)
list(APPEND FAILURE_SIGNAL_HANDLER_SRC
@@ -77,6 +83,9 @@ absl_library(
absl_symbolize
SOURCES
${SYMBOLIZE_SRC}
+ PUBLIC_LIBRARIES
+ absl::base
+ absl_malloc_internal
EXPORT_NAME
symbolize
)
@@ -87,7 +96,7 @@ absl_library(
SOURCES
${FAILURE_SIGNAL_HANDLER_SRC}
PUBLIC_LIBRARIES
- absl_base absl_synchronization
+ absl_base absl::examine_stack absl::stacktrace absl_synchronization
EXPORT_NAME
failure_signal_handler
)
@@ -135,13 +144,9 @@ absl_header_library(
## TESTS
#
-list(APPEND DEBUGGING_INTERNAL_TEST_HEADERS
- "internal/stack_consumption.h"
-)
-
list(APPEND STACK_CONSUMPTION_SRC
"internal/stack_consumption.cc"
- ${DEBUGGING_INTERNAL_TEST_HEADERS}
+ "internal/stack_consumption.h"
)
absl_library(
@@ -155,10 +160,13 @@ absl_test(
TARGET
absl_stack_consumption_test
SOURCES
- ${STACK_CONSUMPTION_SRC}
+ "internal/stack_consumption_test.cc"
+ PUBLIC_LIBRARIES
+ absl_stack_consumption
+ absl::base
)
-list(APPEND DEMANGLE_TEST_SRC "demangle_test.cc")
+list(APPEND DEMANGLE_TEST_SRC "internal/demangle_test.cc")
absl_test(
TARGET
@@ -177,7 +185,7 @@ absl_test(
SOURCES
${SYMBOLIZE_TEST_SRC}
PUBLIC_LIBRARIES
- absl_symbolize absl_stack_consumption
+ absl::base absl::memory absl_symbolize absl_stack_consumption
)
list(APPEND FAILURE_SIGNAL_HANDLER_TEST_SRC "failure_signal_handler_test.cc")
@@ -188,7 +196,12 @@ absl_test(
SOURCES
${FAILURE_SIGNAL_HANDLER_TEST_SRC}
PUBLIC_LIBRARIES
- absl_examine_stack absl_stacktrace absl_symbolize
+ absl_examine_stack
+ absl_failure_signal_handler
+ absl_stacktrace
+ absl_symbolize
+ absl::base
+ absl::strings
)
# test leak_check_test
diff --git a/absl/debugging/failure_signal_handler.cc b/absl/debugging/failure_signal_handler.cc
index 597ad14..3de45f0 100644
--- a/absl/debugging/failure_signal_handler.cc
+++ b/absl/debugging/failure_signal_handler.cc
@@ -32,6 +32,7 @@
#include <atomic>
#include <cerrno>
#include <csignal>
+#include <cstdio>
#include <cstring>
#include <ctime>
diff --git a/absl/meta/CMakeLists.txt b/absl/meta/CMakeLists.txt
index d56fced..adb0ceb 100644
--- a/absl/meta/CMakeLists.txt
+++ b/absl/meta/CMakeLists.txt
@@ -32,6 +32,8 @@ list(APPEND TYPE_TRAITS_TEST_SRC
absl_header_library(
TARGET
absl_meta
+ PUBLIC_LIBRARIES
+ absl::base
EXPORT_NAME
meta
)
@@ -42,7 +44,8 @@ absl_test(
SOURCES
${TYPE_TRAITS_TEST_SRC}
PUBLIC_LIBRARIES
- ${TYPE_TRAITS_TEST_PUBLIC_LIBRARIES} absl::meta
+ absl::base
+ absl::meta
)
diff --git a/absl/strings/str_join_test.cc b/absl/strings/str_join_test.cc
index 03b60f0..c941f9c 100644
--- a/absl/strings/str_join_test.cc
+++ b/absl/strings/str_join_test.cc
@@ -24,7 +24,6 @@
#include <map>
#include <memory>
#include <ostream>
-#include <set>
#include <tuple>
#include <type_traits>
#include <vector>
diff --git a/absl/time/internal/cctz/BUILD.bazel b/absl/time/internal/cctz/BUILD.bazel
index fe17b3e..468470b 100644
--- a/absl/time/internal/cctz/BUILD.bazel
+++ b/absl/time/internal/cctz/BUILD.bazel
@@ -80,6 +80,7 @@ cc_test(
name = "time_zone_format_test",
size = "small",
srcs = ["src/time_zone_format_test.cc"],
+ data = [":zoneinfo"],
deps = [
":civil_time",
":time_zone",
@@ -91,6 +92,7 @@ cc_test(
name = "time_zone_lookup_test",
size = "small",
srcs = ["src/time_zone_lookup_test.cc"],
+ data = [":zoneinfo"],
deps = [
":civil_time",
":time_zone",
@@ -103,3 +105,8 @@ cc_test(
### examples
### binaries
+
+filegroup(
+ name = "zoneinfo",
+ srcs = glob(["testdata/zoneinfo/**"]),
+)
diff --git a/absl/time/internal/cctz/include/cctz/civil_time_detail.h b/absl/time/internal/cctz/include/cctz/civil_time_detail.h
index 4c39c7d..d52eddc 100644
--- a/absl/time/internal/cctz/include/cctz/civil_time_detail.h
+++ b/absl/time/internal/cctz/include/cctz/civil_time_detail.h
@@ -20,8 +20,8 @@
#include <ostream>
#include <type_traits>
-// Disable constexpr support unless we are using clang in C++14 mode.
-#if __clang__ && __cpp_constexpr >= 201304
+// Disable constexpr support unless we are in C++14 mode.
+#if __cpp_constexpr >= 201304 || _MSC_VER >= 1910
#define CONSTEXPR_D constexpr // data
#define CONSTEXPR_F constexpr // function
#define CONSTEXPR_M constexpr // member
diff --git a/absl/time/internal/cctz/src/civil_time_test.cc b/absl/time/internal/cctz/src/civil_time_test.cc
index 6df0395..f6648c8 100644
--- a/absl/time/internal/cctz/src/civil_time_test.cc
+++ b/absl/time/internal/cctz/src/civil_time_test.cc
@@ -37,7 +37,7 @@ std::string Format(const T& t) {
} // namespace
-#if __clang__ && __cpp_constexpr >= 201304
+#if __cpp_constexpr >= 201304 || _MSC_VER >= 1910
// Construction constexpr tests
TEST(CivilTime, Normal) {
@@ -319,7 +319,7 @@ TEST(CivilTime, YearDay) {
constexpr int yd = get_yearday(cd);
static_assert(yd == 28, "YearDay");
}
-#endif // __clang__ && __cpp_constexpr >= 201304
+#endif // __cpp_constexpr >= 201304 || _MSC_VER >= 1910
// The remaining tests do not use constexpr.
diff --git a/absl/time/internal/cctz/src/time_zone_fixed.cc b/absl/time/internal/cctz/src/time_zone_fixed.cc
index 8d3b144..65eba35 100644
--- a/absl/time/internal/cctz/src/time_zone_fixed.cc
+++ b/absl/time/internal/cctz/src/time_zone_fixed.cc
@@ -27,7 +27,7 @@ namespace cctz {
namespace {
// The prefix used for the internal names of fixed-offset zones.
-const char kFixedOffsetPrefix[] = "Fixed/";
+const char kFixedOffsetPrefix[] = "Fixed/UTC";
int Parse02d(const char* p) {
static const char kDigits[] = "0123456789";
@@ -50,13 +50,11 @@ bool FixedOffsetFromName(const std::string& name, sys_seconds* offset) {
const std::size_t prefix_len = sizeof(kFixedOffsetPrefix) - 1;
const char* const ep = kFixedOffsetPrefix + prefix_len;
- if (name.size() != prefix_len + 12) // "<prefix>UTC+99:99:99"
+ if (name.size() != prefix_len + 9) // <prefix>+99:99:99
return false;
if (!std::equal(kFixedOffsetPrefix, ep, name.begin()))
return false;
const char* np = name.data() + prefix_len;
- if (*np++ != 'U' || *np++ != 'T' || *np++ != 'C')
- return false;
if (np[0] != '+' && np[0] != '-')
return false;
if (np[3] != ':' || np[6] != ':') // see note below about large offsets
@@ -97,8 +95,8 @@ std::string FixedOffsetToName(const sys_seconds& offset) {
}
int hours = minutes / 60;
minutes %= 60;
- char buf[sizeof(kFixedOffsetPrefix) + sizeof("UTC-24:00:00")];
- snprintf(buf, sizeof(buf), "%sUTC%c%02d:%02d:%02d",
+ char buf[sizeof(kFixedOffsetPrefix) + sizeof("-24:00:00")];
+ snprintf(buf, sizeof(buf), "%s%c%02d:%02d:%02d",
kFixedOffsetPrefix, sign, hours, minutes, seconds);
return buf;
}
@@ -106,22 +104,14 @@ std::string FixedOffsetToName(const sys_seconds& offset) {
std::string FixedOffsetToAbbr(const sys_seconds& offset) {
std::string abbr = FixedOffsetToName(offset);
const std::size_t prefix_len = sizeof(kFixedOffsetPrefix) - 1;
- const char* const ep = kFixedOffsetPrefix + prefix_len;
- if (abbr.size() >= prefix_len) {
- if (std::equal(kFixedOffsetPrefix, ep, abbr.begin())) {
- abbr.erase(0, prefix_len);
- if (abbr.size() == 12) { // UTC+99:99:99
- abbr.erase(9, 1); // UTC+99:9999
- abbr.erase(6, 1); // UTC+999999
- if (abbr[8] == '0' && abbr[9] == '0') { // UTC+999900
- abbr.erase(8, 2); // UTC+9999
- if (abbr[6] == '0' && abbr[7] == '0') { // UTC+9900
- abbr.erase(6, 2); // UTC+99
- if (abbr[4] == '0') { // UTC+09
- abbr.erase(4, 1); // UTC+9
- }
- }
- }
+ if (abbr.size() == prefix_len + 9) { // <prefix>+99:99:99
+ abbr.erase(0, prefix_len); // +99:99:99
+ abbr.erase(6, 1); // +99:9999
+ abbr.erase(3, 1); // +999999
+ if (abbr[5] == '0' && abbr[6] == '0') { // +999900
+ abbr.erase(5, 2); // +9999
+ if (abbr[3] == '0' && abbr[4] == '0') { // +9900
+ abbr.erase(3, 2); // +99
}
}
}
diff --git a/absl/time/internal/cctz/src/time_zone_lookup.cc b/absl/time/internal/cctz/src/time_zone_lookup.cc
index fbd86e1..d549d86 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup.cc
@@ -134,6 +134,9 @@ time_zone local_time_zone() {
time_zone tz;
load_time_zone(name, &tz); // Falls back to UTC.
+ // TODO: Follow the RFC3339 "Unknown Local Offset Convention" and
+ // arrange for %z to generate "-0000" when we don't know the local
+ // offset because the load_time_zone() failed and we're using UTC.
return tz;
}
diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
index a5d73d5..2dfe53b 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
@@ -1119,18 +1119,6 @@ TEST(TimeZoneEdgeCase, AfricaMonrovia) {
auto tp = convert(civil_second(1972, 1, 6, 23, 59, 59), tz);
ExpectTime(tp, tz, 1972, 1, 6, 23, 59, 59, -44.5 * 60, false, "MMT");
tp += seconds(1);
-#ifndef TZDATA_2017B_IS_UBIQUITOUS
- // The 2017b tzdata release moved the shift from -004430 to +00
- // from 1972-05-01 to 1972-01-07, so we temporarily accept both
- // outcomes until 2017b is ubiquitous.
- if (tz.lookup(tp).offset == -44.5 * 60) {
- tp = convert(civil_second(1972, 4, 30, 23, 59, 59), tz);
- ExpectTime(tp, tz, 1972, 4, 30, 23, 59, 59, -44.5 * 60, false, "LRT");
- tp += seconds(1);
- ExpectTime(tp, tz, 1972, 5, 1, 0, 44, 30, 0 * 60, false, "GMT");
- return;
- }
-#endif
ExpectTime(tp, tz, 1972, 1, 7, 0, 44, 30, 0 * 60, false, "GMT");
}
diff --git a/absl/time/internal/test_util.cc b/absl/time/internal/test_util.cc
index 419d859..bbbef7d 100644
--- a/absl/time/internal/test_util.cc
+++ b/absl/time/internal/test_util.cc
@@ -26,8 +26,7 @@ namespace cctz = absl::time_internal::cctz;
namespace absl {
namespace time_internal {
-// TODO(bww): Reinstate when the FixedTimeZone() abbreviations are updated.
-#if 1 || GTEST_USES_SIMPLE_RE
+#if GTEST_USES_SIMPLE_RE
extern const char kZoneAbbrRE[] = ".*"; // just punt
#else
extern const char kZoneAbbrRE[] = "[A-Za-z]{3,4}|[-+][0-9]{2}([0-9]{2})?";
diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel
index 0bdb2f7..f8d53c2 100644
--- a/absl/types/BUILD.bazel
+++ b/absl/types/BUILD.bazel
@@ -223,3 +223,18 @@ cc_test(
"@com_google_googletest//:gtest_main",
],
)
+
+cc_test(
+ name = "variant_exception_safety_test",
+ size = "small",
+ srcs = [
+ "variant_exception_safety_test.cc",
+ ],
+ copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
+ deps = [
+ ":variant",
+ "//absl/base:exception_safety_testing",
+ "//absl/memory",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt
index f51d126..fbd8374 100644
--- a/absl/types/CMakeLists.txt
+++ b/absl/types/CMakeLists.txt
@@ -29,6 +29,9 @@ absl_header_library(
TARGET
absl_any
PUBLIC_LIBRARIES
+ absl::bad_any_cast
+ absl::base
+ absl::meta
absl::utility
PRIVATE_COMPILE_FLAGS
${ABSL_EXCEPTIONS_FLAG}
@@ -59,7 +62,6 @@ absl_library(
SOURCES
${BAD_ANY_CAST_SRC}
PUBLIC_LIBRARIES
- absl::base absl::any
EXPORT_NAME
bad_any_cast
)
@@ -76,7 +78,11 @@ absl_library(
SOURCES
${OPTIONAL_SRC}
PUBLIC_LIBRARIES
+ absl::bad_optional_access
absl::base
+ absl::memory
+ absl::meta
+ absl::utility
EXPORT_NAME
optional
)
@@ -143,7 +149,11 @@ absl_test(
# test any_exception_safety_test
set(ANY_EXCEPTION_SAFETY_TEST_SRC "any_exception_safety_test.cc")
-set(ANY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES absl::any absl::base absl::base_internal_exception_safety_testing)
+set(ANY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES
+ absl::any
+ absl::base
+ absl_base_internal_exception_safety_testing
+)
absl_test(
TARGET
diff --git a/absl/types/optional.h b/absl/types/optional.h
index 98b29e5..4dcf479 100644
--- a/absl/types/optional.h
+++ b/absl/types/optional.h
@@ -958,7 +958,8 @@ constexpr auto operator==(const optional<T>& x, const optional<U>& y)
-> decltype(optional_internal::convertible_to_bool(*x == *y)) {
return static_cast<bool>(x) != static_cast<bool>(y)
? false
- : static_cast<bool>(x) == false ? true : *x == *y;
+ : static_cast<bool>(x) == false ? true
+ : static_cast<bool>(*x == *y);
}
// Returns: If bool(x) != bool(y), true; otherwise, if bool(x) == false, false;
@@ -968,31 +969,32 @@ constexpr auto operator!=(const optional<T>& x, const optional<U>& y)
-> decltype(optional_internal::convertible_to_bool(*x != *y)) {
return static_cast<bool>(x) != static_cast<bool>(y)
? true
- : static_cast<bool>(x) == false ? false : *x != *y;
+ : static_cast<bool>(x) == false ? false
+ : static_cast<bool>(*x != *y);
}
// Returns: If !y, false; otherwise, if !x, true; otherwise *x < *y.
template <typename T, typename U>
constexpr auto operator<(const optional<T>& x, const optional<U>& y)
-> decltype(optional_internal::convertible_to_bool(*x < *y)) {
- return !y ? false : !x ? true : *x < *y;
+ return !y ? false : !x ? true : static_cast<bool>(*x < *y);
}
// Returns: If !x, false; otherwise, if !y, true; otherwise *x > *y.
template <typename T, typename U>
constexpr auto operator>(const optional<T>& x, const optional<U>& y)
-> decltype(optional_internal::convertible_to_bool(*x > *y)) {
- return !x ? false : !y ? true : *x > *y;
+ return !x ? false : !y ? true : static_cast<bool>(*x > *y);
}
// Returns: If !x, true; otherwise, if !y, false; otherwise *x <= *y.
template <typename T, typename U>
constexpr auto operator<=(const optional<T>& x, const optional<U>& y)
-> decltype(optional_internal::convertible_to_bool(*x <= *y)) {
- return !x ? true : !y ? false : *x <= *y;
+ return !x ? true : !y ? false : static_cast<bool>(*x <= *y);
}
// Returns: If !y, true; otherwise, if !x, false; otherwise *x >= *y.
template <typename T, typename U>
constexpr auto operator>=(const optional<T>& x, const optional<U>& y)
-> decltype(optional_internal::convertible_to_bool(*x >= *y)) {
- return !y ? true : !x ? false : *x >= *y;
+ return !y ? true : !x ? false : static_cast<bool>(*x >= *y);
}
// Comparison with nullopt [optional.nullops]
@@ -1054,62 +1056,62 @@ constexpr bool operator>=(nullopt_t, const optional<T>& x) noexcept {
template <typename T, typename U>
constexpr auto operator==(const optional<T>& x, const U& v)
-> decltype(optional_internal::convertible_to_bool(*x == v)) {
- return static_cast<bool>(x) ? *x == v : false;
+ return static_cast<bool>(x) ? static_cast<bool>(*x == v) : false;
}
template <typename T, typename U>
constexpr auto operator==(const U& v, const optional<T>& x)
-> decltype(optional_internal::convertible_to_bool(v == *x)) {
- return static_cast<bool>(x) ? v == *x : false;
+ return static_cast<bool>(x) ? static_cast<bool>(v == *x) : false;
}
template <typename T, typename U>
constexpr auto operator!=(const optional<T>& x, const U& v)
-> decltype(optional_internal::convertible_to_bool(*x != v)) {
- return static_cast<bool>(x) ? *x != v : true;
+ return static_cast<bool>(x) ? static_cast<bool>(*x != v) : true;
}
template <typename T, typename U>
constexpr auto operator!=(const U& v, const optional<T>& x)
-> decltype(optional_internal::convertible_to_bool(v != *x)) {
- return static_cast<bool>(x) ? v != *x : true;
+ return static_cast<bool>(x) ? static_cast<bool>(v != *x) : true;
}
template <typename T, typename U>
constexpr auto operator<(const optional<T>& x, const U& v)
-> decltype(optional_internal::convertible_to_bool(*x < v)) {
- return static_cast<bool>(x) ? *x < v : true;
+ return static_cast<bool>(x) ? static_cast<bool>(*x < v) : true;
}
template <typename T, typename U>
constexpr auto operator<(const U& v, const optional<T>& x)
-> decltype(optional_internal::convertible_to_bool(v < *x)) {
- return static_cast<bool>(x) ? v < *x : false;
+ return static_cast<bool>(x) ? static_cast<bool>(v < *x) : false;
}
template <typename T, typename U>
constexpr auto operator<=(const optional<T>& x, const U& v)
-> decltype(optional_internal::convertible_to_bool(*x <= v)) {
- return static_cast<bool>(x) ? *x <= v : true;
+ return static_cast<bool>(x) ? static_cast<bool>(*x <= v) : true;
}
template <typename T, typename U>
constexpr auto operator<=(const U& v, const optional<T>& x)
-> decltype(optional_internal::convertible_to_bool(v <= *x)) {
- return static_cast<bool>(x) ? v <= *x : false;
+ return static_cast<bool>(x) ? static_cast<bool>(v <= *x) : false;
}
template <typename T, typename U>
constexpr auto operator>(const optional<T>& x, const U& v)
-> decltype(optional_internal::convertible_to_bool(*x > v)) {
- return static_cast<bool>(x) ? *x > v : false;
+ return static_cast<bool>(x) ? static_cast<bool>(*x > v) : false;
}
template <typename T, typename U>
constexpr auto operator>(const U& v, const optional<T>& x)
-> decltype(optional_internal::convertible_to_bool(v > *x)) {
- return static_cast<bool>(x) ? v > *x : true;
+ return static_cast<bool>(x) ? static_cast<bool>(v > *x) : true;
}
template <typename T, typename U>
constexpr auto operator>=(const optional<T>& x, const U& v)
-> decltype(optional_internal::convertible_to_bool(*x >= v)) {
- return static_cast<bool>(x) ? *x >= v : false;
+ return static_cast<bool>(x) ? static_cast<bool>(*x >= v) : false;
}
template <typename T, typename U>
constexpr auto operator>=(const U& v, const optional<T>& x)
-> decltype(optional_internal::convertible_to_bool(v >= *x)) {
- return static_cast<bool>(x) ? v >= *x : true;
+ return static_cast<bool>(x) ? static_cast<bool>(v >= *x) : true;
}
} // namespace absl
diff --git a/absl/types/variant.h b/absl/types/variant.h
index 52a311e..7ae65ab 100644
--- a/absl/types/variant.h
+++ b/absl/types/variant.h
@@ -350,7 +350,7 @@ constexpr const variant_alternative_t<I, variant<Types...>>&& get(
// get_if()
//
// Returns a pointer to the value currently stored within a given variant, if
-// present, using either a unique alternative type amonst the variant's set of
+// present, using either a unique alternative type amongst the variant's set of
// alternative types, or the variant's index value. If such a value does not
// exist, returns `nullptr`.
//
diff --git a/absl/types/variant_exception_safety_test.cc b/absl/types/variant_exception_safety_test.cc
new file mode 100644
index 0000000..377e4af
--- /dev/null
+++ b/absl/types/variant_exception_safety_test.cc
@@ -0,0 +1,519 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "absl/types/variant.h"
+
+#include <iostream>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/internal/exception_safety_testing.h"
+#include "absl/memory/memory.h"
+
+namespace absl {
+namespace {
+
+using ::testing::MakeExceptionSafetyTester;
+using ::testing::nothrow_guarantee;
+using ::testing::strong_guarantee;
+using ::testing::TestThrowingCtor;
+
+using Thrower = testing::ThrowingValue<>;
+using CopyNothrow = testing::ThrowingValue<testing::TypeSpec::kNoThrowCopy>;
+using MoveNothrow = testing::ThrowingValue<testing::TypeSpec::kNoThrowMove>;
+using ThrowingAlloc = testing::ThrowingAllocator<Thrower>;
+using ThrowerVec = std::vector<Thrower, ThrowingAlloc>;
+using ThrowingVariant =
+ absl::variant<Thrower, CopyNothrow, MoveNothrow, ThrowerVec>;
+
+struct ConversionException {};
+
+template <class T>
+struct ExceptionOnConversion {
+ operator T() const { // NOLINT
+ throw ConversionException();
+ }
+};
+
+// Forces a variant into the valueless by exception state.
+void ToValuelessByException(ThrowingVariant& v) { // NOLINT
+ try {
+ v.emplace<Thrower>();
+ v.emplace<Thrower>(ExceptionOnConversion<Thrower>());
+ } catch (ConversionException& /*e*/) {
+ // This space intentionally left blank.
+ }
+}
+
+// Check that variant is still in a usable state after an exception is thrown.
+testing::AssertionResult CheckInvariants(ThrowingVariant* v) {
+ using testing::AssertionFailure;
+ using testing::AssertionSuccess;
+
+ // Try using the active alternative
+ if (absl::holds_alternative<Thrower>(*v)) {
+ auto& t = absl::get<Thrower>(*v);
+ t = Thrower{-100};
+ if (t.Get() != -100) {
+ return AssertionFailure() << "Thrower should be assigned -100";
+ }
+ } else if (absl::holds_alternative<ThrowerVec>(*v)) {
+ auto& tv = absl::get<ThrowerVec>(*v);
+ tv.clear();
+ tv.emplace_back(-100);
+ if (tv.size() != 1 || tv[0].Get() != -100) {
+ return AssertionFailure() << "ThrowerVec should be {Thrower{-100}}";
+ }
+ } else if (absl::holds_alternative<CopyNothrow>(*v)) {
+ auto& t = absl::get<CopyNothrow>(*v);
+ t = CopyNothrow{-100};
+ if (t.Get() != -100) {
+ return AssertionFailure() << "CopyNothrow should be assigned -100";
+ }
+ } else if (absl::holds_alternative<MoveNothrow>(*v)) {
+ auto& t = absl::get<MoveNothrow>(*v);
+ t = MoveNothrow{-100};
+ if (t.Get() != -100) {
+ return AssertionFailure() << "MoveNothrow should be assigned -100";
+ }
+ }
+
+ // Try making variant valueless_by_exception
+ if (!v->valueless_by_exception()) ToValuelessByException(*v);
+ if (!v->valueless_by_exception()) {
+ return AssertionFailure() << "Variant should be valueless_by_exception";
+ }
+ try {
+ auto unused = absl::get<Thrower>(*v);
+ static_cast<void>(unused);
+ return AssertionFailure() << "Variant should not contain Thrower";
+ } catch (absl::bad_variant_access) {
+ } catch (...) {
+ return AssertionFailure() << "Unexpected exception throw from absl::get";
+ }
+
+ // Try using the variant
+ v->emplace<Thrower>(100);
+ if (!absl::holds_alternative<Thrower>(*v) ||
+ absl::get<Thrower>(*v) != Thrower(100)) {
+ return AssertionFailure() << "Variant should contain Thrower(100)";
+ }
+ v->emplace<ThrowerVec>({Thrower(100)});
+ if (!absl::holds_alternative<ThrowerVec>(*v) ||
+ absl::get<ThrowerVec>(*v)[0] != Thrower(100)) {
+ return AssertionFailure()
+ << "Variant should contain ThrowerVec{Thrower(100)}";
+ }
+ return AssertionSuccess();
+}
+
+Thrower ExpectedThrower() { return Thrower(42); }
+ThrowerVec ExpectedThrowerVec() { return {Thrower(100), Thrower(200)}; }
+ThrowingVariant ValuelessByException() {
+ ThrowingVariant v;
+ ToValuelessByException(v);
+ return v;
+}
+ThrowingVariant WithThrower() { return Thrower(39); }
+ThrowingVariant WithThrowerVec() {
+ return ThrowerVec{Thrower(1), Thrower(2), Thrower(3)};
+}
+ThrowingVariant WithCopyNoThrow() { return CopyNothrow(39); }
+ThrowingVariant WithMoveNoThrow() { return MoveNothrow(39); }
+
+TEST(VariantExceptionSafetyTest, DefaultConstructor) {
+ TestThrowingCtor<ThrowingVariant>();
+}
+
+TEST(VariantExceptionSafetyTest, CopyConstructor) {
+ {
+ ThrowingVariant v(ExpectedThrower());
+ TestThrowingCtor<ThrowingVariant>(v);
+ }
+ {
+ ThrowingVariant v(ExpectedThrowerVec());
+ TestThrowingCtor<ThrowingVariant>(v);
+ }
+ {
+ ThrowingVariant v(ValuelessByException());
+ TestThrowingCtor<ThrowingVariant>(v);
+ }
+}
+
+TEST(VariantExceptionSafetyTest, MoveConstructor) {
+ {
+ ThrowingVariant v(ExpectedThrower());
+ TestThrowingCtor<ThrowingVariant>(std::move(v));
+ }
+ {
+ ThrowingVariant v(ExpectedThrowerVec());
+ TestThrowingCtor<ThrowingVariant>(std::move(v));
+ }
+ {
+ ThrowingVariant v(ValuelessByException());
+ TestThrowingCtor<ThrowingVariant>(std::move(v));
+ }
+}
+
+TEST(VariantExceptionSafetyTest, ValueConstructor) {
+ TestThrowingCtor<ThrowingVariant>(ExpectedThrower());
+ TestThrowingCtor<ThrowingVariant>(ExpectedThrowerVec());
+}
+
+TEST(VariantExceptionSafetyTest, InPlaceTypeConstructor) {
+ TestThrowingCtor<ThrowingVariant>(absl::in_place_type_t<Thrower>{},
+ ExpectedThrower());
+ TestThrowingCtor<ThrowingVariant>(absl::in_place_type_t<ThrowerVec>{},
+ ExpectedThrowerVec());
+}
+
+TEST(VariantExceptionSafetyTest, InPlaceIndexConstructor) {
+ TestThrowingCtor<ThrowingVariant>(absl::in_place_index_t<0>{},
+ ExpectedThrower());
+ TestThrowingCtor<ThrowingVariant>(absl::in_place_index_t<3>{},
+ ExpectedThrowerVec());
+}
+
+TEST(VariantExceptionSafetyTest, CopyAssign) {
+ // variant& operator=(const variant& rhs);
+ // Let j be rhs.index()
+ {
+ // - neither *this nor rhs holds a value
+ const ThrowingVariant rhs = ValuelessByException();
+ EXPECT_TRUE(MakeExceptionSafetyTester()
+ .WithInitialValue(ValuelessByException())
+ .WithInvariants(nothrow_guarantee)
+ .Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }));
+ }
+ {
+ // - *this holds a value but rhs does not
+ const ThrowingVariant rhs = ValuelessByException();
+ EXPECT_TRUE(MakeExceptionSafetyTester()
+ .WithInitialValue(WithThrower())
+ .WithInvariants(nothrow_guarantee)
+ .Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }));
+ }
+ // - index() == j
+ {
+ const ThrowingVariant rhs(ExpectedThrower());
+ auto tester =
+ MakeExceptionSafetyTester()
+ .WithInitialValue(WithThrower())
+ .WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; });
+ EXPECT_TRUE(tester.WithInvariants(CheckInvariants).Test());
+ EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test());
+ }
+ {
+ const ThrowingVariant rhs(ExpectedThrowerVec());
+ auto tester =
+ MakeExceptionSafetyTester()
+ .WithInitialValue(WithThrowerVec())
+ .WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; });
+ EXPECT_TRUE(tester.WithInvariants(CheckInvariants).Test());
+ EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test());
+ }
+ // libstdc++ std::variant has bugs on copy assignment regarding exception
+ // safety.
+#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
+ // index() != j
+ // if is_nothrow_copy_constructible_v<Tj> or
+ // !is_nothrow_move_constructible<Tj> is true, equivalent to
+ // emplace<j>(get<j>(rhs))
+ {
+ // is_nothrow_copy_constructible_v<Tj> == true
+ // should not throw because emplace() invokes Tj's copy ctor
+ // which should not throw.
+ const ThrowingVariant rhs(CopyNothrow{});
+ EXPECT_TRUE(MakeExceptionSafetyTester()
+ .WithInitialValue(WithThrower())
+ .WithInvariants(nothrow_guarantee)
+ .Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }));
+ }
+ {
+ // is_nothrow_copy_constructible<Tj> == false &&
+ // is_nothrow_move_constructible<Tj> == false
+ // should provide basic guarantee because emplace() invokes Tj's copy ctor
+ // which may throw.
+ const ThrowingVariant rhs(ExpectedThrower());
+ auto tester =
+ MakeExceptionSafetyTester()
+ .WithInitialValue(WithCopyNoThrow())
+ .WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; });
+ EXPECT_TRUE(tester
+ .WithInvariants(CheckInvariants,
+ [](ThrowingVariant* lhs) {
+ return lhs->valueless_by_exception();
+ })
+ .Test());
+ EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test());
+ }
+#endif // !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
+ {
+ // is_nothrow_copy_constructible_v<Tj> == false &&
+ // is_nothrow_move_constructible_v<Tj> == true
+ // should provide strong guarantee because it is equivalent to
+ // operator=(variant(rhs)) which creates a temporary then invoke the move
+ // ctor which shouldn't throw.
+ const ThrowingVariant rhs(MoveNothrow{});
+ EXPECT_TRUE(MakeExceptionSafetyTester()
+ .WithInitialValue(WithThrower())
+ .WithInvariants(CheckInvariants, strong_guarantee)
+ .Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }));
+ }
+}
+
+TEST(VariantExceptionSafetyTest, MoveAssign) {
+ // variant& operator=(variant&& rhs);
+ // Let j be rhs.index()
+ {
+ // - neither *this nor rhs holds a value
+ ThrowingVariant rhs = ValuelessByException();
+
+ EXPECT_TRUE(MakeExceptionSafetyTester()
+ .WithInitialValue(ValuelessByException())
+ .WithInvariants(nothrow_guarantee)
+ .Test([rhs](ThrowingVariant* lhs) mutable {
+ *lhs = std::move(rhs);
+ }));
+ }
+ {
+ // - *this holds a value but rhs does not
+ ThrowingVariant rhs = ValuelessByException();
+ EXPECT_TRUE(MakeExceptionSafetyTester()
+ .WithInitialValue(WithThrower())
+ .WithInvariants(nothrow_guarantee)
+ .Test([rhs](ThrowingVariant* lhs) mutable {
+ *lhs = std::move(rhs);
+ }));
+ }
+ {
+ // - index() == j
+ // assign get<j>(std::move(rhs)) to the value contained in *this.
+ // If an exception is thrown during call to Tj's move assignment, the state
+ // of the contained value is as defined by the exception safety guarantee of
+ // Tj's move assignment; index() will be j.
+ ThrowingVariant rhs(ExpectedThrower());
+ size_t j = rhs.index();
+ // Since Thrower's move assignment has basic guarantee, so should variant's.
+ auto tester = MakeExceptionSafetyTester()
+ .WithInitialValue(WithThrower())
+ .WithOperation([rhs](ThrowingVariant* lhs) mutable {
+ *lhs = std::move(rhs);
+ });
+ EXPECT_TRUE(tester
+ .WithInvariants(
+ CheckInvariants,
+ [j](ThrowingVariant* lhs) { return lhs->index() == j; })
+ .Test());
+ EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test());
+ }
+ {
+ // - otherwise (index() != j), equivalent to
+ // emplace<j>(get<j>(std::move(rhs)))
+ // - If an exception is thrown during the call to Tj's move construction
+ // (with j being rhs.index()), the variant will hold no value.
+ ThrowingVariant rhs(CopyNothrow{});
+ EXPECT_TRUE(MakeExceptionSafetyTester()
+ .WithInitialValue(WithThrower())
+ .WithInvariants(CheckInvariants,
+ [](ThrowingVariant* lhs) {
+ return lhs->valueless_by_exception();
+ })
+ .Test([rhs](ThrowingVariant* lhs) mutable {
+ *lhs = std::move(rhs);
+ }));
+ }
+}
+
+TEST(VariantExceptionSafetyTest, ValueAssign) {
+ // template<class T> variant& operator=(T&& t);
+ // Let Tj be the type that is selected by overload resolution to be assigned.
+ {
+ // If *this holds a Tj, assigns std::forward<T>(t) to the value contained in
+ // *this. If an exception is thrown during the assignment of
+ // std::forward<T>(t) to the value contained in *this, the state of the
+ // contained value and t are as defined by the exception safety guarantee of
+ // the assignment expression; valueless_by_exception() will be false.
+ // Since Thrower's copy/move assignment has basic guarantee, so should
+ // variant's.
+ Thrower rhs = ExpectedThrower();
+ // copy assign
+ auto copy_tester =
+ MakeExceptionSafetyTester()
+ .WithInitialValue(WithThrower())
+ .WithOperation([rhs](ThrowingVariant* lhs) { *lhs = rhs; });
+ EXPECT_TRUE(copy_tester
+ .WithInvariants(CheckInvariants,
+ [](ThrowingVariant* lhs) {
+ return !lhs->valueless_by_exception();
+ })
+ .Test());
+ EXPECT_FALSE(copy_tester.WithInvariants(strong_guarantee).Test());
+ // move assign
+ auto move_tester = MakeExceptionSafetyTester()
+ .WithInitialValue(WithThrower())
+ .WithOperation([rhs](ThrowingVariant* lhs) mutable {
+ *lhs = std::move(rhs);
+ });
+ EXPECT_TRUE(move_tester
+ .WithInvariants(CheckInvariants,
+ [](ThrowingVariant* lhs) {
+ return !lhs->valueless_by_exception();
+ })
+ .Test());
+
+ EXPECT_FALSE(move_tester.WithInvariants(strong_guarantee).Test());
+ }
+ // Otherwise (*this holds something else), if is_nothrow_constructible_v<Tj,
+ // T> || !is_nothrow_move_constructible_v<Tj> is true, equivalent to
+ // emplace<j>(std::forward<T>(t)).
+ // We simplify the test by letting T = `const Tj&` or `Tj&&`, so we can reuse
+ // the CopyNothrow and MoveNothrow types.
+
+ // if is_nothrow_constructible_v<Tj, T>
+ // (i.e. is_nothrow_copy/move_constructible_v<Tj>) is true, emplace() just
+ // invokes the copy/move constructor and it should not throw.
+ {
+ const CopyNothrow rhs;
+ EXPECT_TRUE(MakeExceptionSafetyTester()
+ .WithInitialValue(WithThrower())
+ .WithInvariants(nothrow_guarantee)
+ .Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }));
+ }
+ {
+ MoveNothrow rhs;
+ EXPECT_TRUE(MakeExceptionSafetyTester()
+ .WithInitialValue(WithThrower())
+ .WithInvariants(nothrow_guarantee)
+ .Test([rhs](ThrowingVariant* lhs) mutable {
+ *lhs = std::move(rhs);
+ }));
+ }
+ // if is_nothrow_constructible_v<Tj, T> == false &&
+ // is_nothrow_move_constructible<Tj> == false
+ // emplace() invokes the copy/move constructor which may throw so it should
+ // provide basic guarantee and variant object might not hold a value.
+ {
+ Thrower rhs = ExpectedThrower();
+ // copy
+ auto copy_tester =
+ MakeExceptionSafetyTester()
+ .WithInitialValue(WithCopyNoThrow())
+ .WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; });
+ EXPECT_TRUE(copy_tester
+ .WithInvariants(CheckInvariants,
+ [](ThrowingVariant* lhs) {
+ return lhs->valueless_by_exception();
+ })
+ .Test());
+ EXPECT_FALSE(copy_tester.WithInvariants(strong_guarantee).Test());
+ // move
+ auto move_tester = MakeExceptionSafetyTester()
+ .WithInitialValue(WithCopyNoThrow())
+ .WithOperation([rhs](ThrowingVariant* lhs) mutable {
+ *lhs = std::move(rhs);
+ });
+ EXPECT_TRUE(move_tester
+ .WithInvariants(CheckInvariants,
+ [](ThrowingVariant* lhs) {
+ return lhs->valueless_by_exception();
+ })
+ .Test());
+ EXPECT_FALSE(move_tester.WithInvariants(strong_guarantee).Test());
+ }
+ // Otherwise (if is_nothrow_constructible_v<Tj, T> == false &&
+ // is_nothrow_move_constructible<Tj> == true),
+ // equivalent to operator=(variant(std::forward<T>(t)))
+ // This should have strong guarantee because it creates a temporary variant
+ // and operator=(variant&&) invokes Tj's move ctor which doesn't throw.
+ // libstdc++ std::variant has bugs on conversion assignment regarding
+ // exception safety.
+#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
+ {
+ MoveNothrow rhs;
+ EXPECT_TRUE(MakeExceptionSafetyTester()
+ .WithInitialValue(WithThrower())
+ .WithInvariants(CheckInvariants, strong_guarantee)
+ .Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }));
+ }
+#endif // !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
+}
+
+TEST(VariantExceptionSafetyTest, Emplace) {
+ // If an exception during the initialization of the contained value, the
+ // variant might not hold a value. The standard requires emplace() to provide
+ // only basic guarantee.
+ {
+ Thrower args = ExpectedThrower();
+ auto tester = MakeExceptionSafetyTester()
+ .WithInitialValue(WithThrower())
+ .WithOperation([&args](ThrowingVariant* v) {
+ v->emplace<Thrower>(args);
+ });
+ EXPECT_TRUE(tester
+ .WithInvariants(CheckInvariants,
+ [](ThrowingVariant* v) {
+ return v->valueless_by_exception();
+ })
+ .Test());
+ EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test());
+ }
+}
+
+TEST(VariantExceptionSafetyTest, Swap) {
+ // if both are valueless_by_exception(), no effect
+ {
+ ThrowingVariant rhs = ValuelessByException();
+ EXPECT_TRUE(
+ MakeExceptionSafetyTester()
+ .WithInitialValue(ValuelessByException())
+ .WithInvariants(nothrow_guarantee)
+ .Test([rhs](ThrowingVariant* lhs) mutable { lhs->swap(rhs); }));
+ }
+ // if index() == rhs.index(), calls swap(get<i>(*this), get<i>(rhs))
+ // where i is index().
+ {
+ ThrowingVariant rhs = ExpectedThrower();
+ EXPECT_TRUE(
+ MakeExceptionSafetyTester()
+ .WithInitialValue(WithThrower())
+ .WithInvariants(CheckInvariants)
+ .Test([rhs](ThrowingVariant* lhs) mutable { lhs->swap(rhs); }));
+ }
+ // Otherwise, exchanges the value of rhs and *this. The exception safety
+ // involves variant in moved-from state which is not specified in the
+ // standard, and since swap is 3-step it's impossible for it to provide a
+ // overall strong guarantee. So, we are only checking basic guarantee here.
+ {
+ ThrowingVariant rhs = ExpectedThrower();
+ EXPECT_TRUE(
+ MakeExceptionSafetyTester()
+ .WithInitialValue(WithCopyNoThrow())
+ .WithInvariants(CheckInvariants)
+ .Test([rhs](ThrowingVariant* lhs) mutable { lhs->swap(rhs); }));
+ }
+ {
+ ThrowingVariant rhs = ExpectedThrower();
+ EXPECT_TRUE(
+ MakeExceptionSafetyTester()
+ .WithInitialValue(WithCopyNoThrow())
+ .WithInvariants(CheckInvariants)
+ .Test([rhs](ThrowingVariant* lhs) mutable { rhs.swap(*lhs); }));
+ }
+}
+
+} // namespace
+} // namespace absl
diff --git a/absl/utility/CMakeLists.txt b/absl/utility/CMakeLists.txt
index df21b85..dc3a631 100644
--- a/absl/utility/CMakeLists.txt
+++ b/absl/utility/CMakeLists.txt
@@ -22,6 +22,8 @@ list(APPEND UTILITY_PUBLIC_HEADERS
absl_header_library(
TARGET
absl_utility
+ PUBLIC_LIBRARIES
+ absl::base
EXPORT_NAME
utility
)
@@ -33,7 +35,12 @@ absl_header_library(
# test utility_test
set(UTILITY_TEST_SRC "utility_test.cc")
-set(UTILITY_TEST_PUBLIC_LIBRARIES absl::utility)
+set(UTILITY_TEST_PUBLIC_LIBRARIES
+ absl::base
+ absl::memory
+ absl::strings
+ absl::utility
+)
absl_test(
TARGET