- 07191b0f52301e1e4a790e236f7b7c2fd90561ae Disambiguates computed return type of absl::optional logi... by Abseil Team <absl-team@google.com>
- acd95f8ec4e6ec1587cb198c7f40af3c81094d92 Release container benchmarks. by Alex Strelnikov <strel@google.com> - 80f596b6b7c5e06453e778c16527d5a0e85f8413 Allow absl::base_internal::AtomicHook to have a default v... by Derek Mauro <dmauro@google.com> - 8402631546af8bcbd4acdf897d0cdfb805ad544a Release thread_identity benchmark. by Alex Strelnikov <strel@google.com> - 6dcb1e90fefb8556ce4654983d3a73c7585b4b99 Fix spelling error in variant.h by Abseil Team <absl-team@google.com> - faa8a81e1442018c0d400b09a595a5be55074715 Run tests from CMake. The CI is currently Linux only, fo... by Jon Cohen <cohenjon@google.com> - 745ed6db574f931f2ec3a88e964fb03a5f22f816 Internal change. by Derek Mauro <dmauro@google.com> - 23facd7d1c5f43ac8181b016ee4acc5955f048c1 absl::variant exception safety test. by Xiaoyi Zhang <zhangxy@google.com> - c18e21e7cf8f6e83ae9d90e536e886409dd6cf68 Reinstate the syntax check on time-zone abbreviations now... by Abseil Team <absl-team@google.com> - da469f4314f0c820665a2b5b9477af9462b23e42 Import CCTZ changes to internal copy. by Shaindel Schwartz <shaindel@google.com> - 44ea35843517be03ab256b69449ccfea64352621 Import CCTZ changes to internal copy. by Abseil Team <absl-team@google.com> - 55d1105312687c6093950fac831c7540f49045b5 Import CCTZ changes to internal copy. by Greg Falcon <gfalcon@google.com> - 58d7965ad274406410b6d833213eca04d41c6867 Add zoneinfo as a data dependency to the //absl/time tests. by Shaindel Schwartz <shaindel@google.com> - 6acc50146f9ff29015bfaaa5bf9900691f839da5 Change benchmark target type from cc_test to cc_binary. by Alex Strelnikov <strel@google.com> - db3fbdae8f9f285a466f7a070326b1ce43b6a0dd Update WORKSPACE for C++ microbenchmarks and release algo... by Alex Strelnikov <strel@google.com> - 0869ae168255242af651853ed01719166d8cebf6 Update to Bazel version 0.13.0. by Abseil Team <absl-team@google.com> - e507dd53ab788964207fdf27d31b72a33c296fab Add missing include of cstdio by Abseil Team <absl-team@google.com> GitOrigin-RevId: 07191b0f52301e1e4a790e236f7b7c2fd90561ae Change-Id: I90994cf2b438fbec894724dcd9b90882281eef56
This commit is contained in:
parent
9613678332
commit
26b789f9a5
35 changed files with 1583 additions and 119 deletions
|
@ -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()
|
||||
|
|
15
CMake/CMakeLists.txt.in
Normal file
15
CMake/CMakeLists.txt.in
Normal file
|
@ -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 ""
|
||||
)
|
32
CMake/DownloadGTest.cmake
Normal file
32
CMake/DownloadGTest.cmake
Normal file
|
@ -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)
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
15
WORKSPACE
15
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",
|
||||
|
|
|
@ -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 = [
|
||||
|
|
128
absl/algorithm/equal_benchmark.cc
Normal file
128
absl/algorithm/equal_benchmark.cc
Normal file
|
@ -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();
|
|
@ -147,6 +147,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",
|
||||
|
@ -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",
|
||||
],
|
||||
)
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
@ -162,6 +168,20 @@ absl_library(
|
|||
## TESTS
|
||||
#
|
||||
|
||||
# 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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
70
absl/base/internal/atomic_hook_test.cc
Normal file
70
absl/base/internal/atomic_hook_test.cc
Normal file
|
@ -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
|
|
@ -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_
|
||||
|
|
40
absl/base/internal/thread_identity_benchmark.cc
Normal file
40
absl/base/internal/thread_identity_benchmark.cc
Normal file
|
@ -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();
|
|
@ -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,
|
||||
|
|
68
absl/container/fixed_array_benchmark.cc
Normal file
68
absl/container/fixed_array_benchmark.cc
Normal file
|
@ -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();
|
376
absl/container/inlined_vector_benchmark.cc
Normal file
376
absl/container/inlined_vector_benchmark.cc
Normal file
|
@ -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();
|
|
@ -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
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <atomic>
|
||||
#include <cerrno>
|
||||
#include <csignal>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include <map>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <set>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
|
|
@ -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/**"]),
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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})?";
|
||||
|
|
|
@ -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",
|
||||
],
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`.
|
||||
//
|
||||
|
|
519
absl/types/variant_exception_safety_test.cc
Normal file
519
absl/types/variant_exception_safety_test.cc
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue