merge(3p/abseil_cpp): Merge upstream at 'ccdbb5941'

Change-Id: I6e85fc7b5f76bba1f1eef15e600a8acb64e97ef5
This commit is contained in:
Vincent Ambo 2020-06-17 14:53:11 +01:00
commit 543379ce45
97 changed files with 3546 additions and 2316 deletions

25
third_party/abseil_cpp/BUILD.bazel vendored Normal file
View file

@ -0,0 +1,25 @@
#
# Copyright 2020 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
#
# https://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.
#
package(default_visibility = ["//visibility:public"])
licenses(["notice"]) # Apache 2.0
# Expose license for external usage through bazel.
exports_files([
"AUTHORS",
"LICENSE",
])

View file

@ -15,6 +15,7 @@ set(ABSL_INTERNAL_DLL_FILES
"base/internal/cycleclock.cc" "base/internal/cycleclock.cc"
"base/internal/cycleclock.h" "base/internal/cycleclock.h"
"base/internal/direct_mmap.h" "base/internal/direct_mmap.h"
"base/internal/dynamic_annotations.h"
"base/internal/endian.h" "base/internal/endian.h"
"base/internal/errno_saver.h" "base/internal/errno_saver.h"
"base/internal/exponential_biased.cc" "base/internal/exponential_biased.cc"
@ -135,19 +136,18 @@ set(ABSL_INTERNAL_DLL_FILES
"random/exponential_distribution.h" "random/exponential_distribution.h"
"random/gaussian_distribution.cc" "random/gaussian_distribution.cc"
"random/gaussian_distribution.h" "random/gaussian_distribution.h"
"random/internal/distributions.h"
"random/internal/distribution_caller.h" "random/internal/distribution_caller.h"
"random/internal/fast_uniform_bits.h"
"random/internal/fastmath.h" "random/internal/fastmath.h"
"random/internal/fast_uniform_bits.h"
"random/internal/gaussian_distribution_gentables.cc" "random/internal/gaussian_distribution_gentables.cc"
"random/internal/generate_real.h" "random/internal/generate_real.h"
"random/internal/iostream_state_saver.h" "random/internal/iostream_state_saver.h"
"random/internal/mock_helpers.h"
"random/internal/nonsecure_base.h" "random/internal/nonsecure_base.h"
"random/internal/pcg_engine.h" "random/internal/pcg_engine.h"
"random/internal/platform.h" "random/internal/platform.h"
"random/internal/pool_urbg.cc" "random/internal/pool_urbg.cc"
"random/internal/pool_urbg.h" "random/internal/pool_urbg.h"
"random/internal/randen_round_keys.cc"
"random/internal/randen.cc" "random/internal/randen.cc"
"random/internal/randen.h" "random/internal/randen.h"
"random/internal/randen_detect.cc" "random/internal/randen_detect.cc"
@ -155,6 +155,7 @@ set(ABSL_INTERNAL_DLL_FILES
"random/internal/randen_engine.h" "random/internal/randen_engine.h"
"random/internal/randen_hwaes.cc" "random/internal/randen_hwaes.cc"
"random/internal/randen_hwaes.h" "random/internal/randen_hwaes.h"
"random/internal/randen_round_keys.cc"
"random/internal/randen_slow.cc" "random/internal/randen_slow.cc"
"random/internal/randen_slow.h" "random/internal/randen_slow.h"
"random/internal/randen_traits.h" "random/internal/randen_traits.h"

View file

@ -102,10 +102,8 @@ if(${ABSL_RUN_TESTS})
# on the command line # on the command line
include(CTest) include(CTest)
enable_testing() enable_testing()
endif()
## check targets ## check targets
if(BUILD_TESTING)
if (NOT ABSL_USE_EXTERNAL_GOOGLETEST) if (NOT ABSL_USE_EXTERNAL_GOOGLETEST)
set(absl_gtest_build_dir ${CMAKE_BINARY_DIR}/googletest-build) set(absl_gtest_build_dir ${CMAKE_BINARY_DIR}/googletest-build)
if(${ABSL_USE_GOOGLETEST_HEAD}) if(${ABSL_USE_GOOGLETEST_HEAD})
@ -177,5 +175,7 @@ if(ABSL_ENABLE_INSTALL)
FILES_MATCHING FILES_MATCHING
PATTERN "*.inc" PATTERN "*.inc"
PATTERN "*.h" PATTERN "*.h"
) PATTERN "copts" EXCLUDE
PATTERN "testdata" EXCLUDE
)
endif() # ABSL_ENABLE_INSTALL endif() # ABSL_ENABLE_INSTALL

View file

@ -19,10 +19,10 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# GoogleTest/GoogleMock framework. Used by most unit-tests. # GoogleTest/GoogleMock framework. Used by most unit-tests.
http_archive( http_archive(
name = "com_google_googletest", name = "com_google_googletest",
urls = ["https://github.com/google/googletest/archive/b6cd405286ed8635ece71c72f118e659f4ade3fb.zip"], # 2019-01-07 urls = ["https://github.com/google/googletest/archive/011959aafddcd30611003de96cfd8d7a7685c700.zip"], # 2020-05-14T00:36:05Z
strip_prefix = "googletest-b6cd405286ed8635ece71c72f118e659f4ade3fb", strip_prefix = "googletest-011959aafddcd30611003de96cfd8d7a7685c700",
sha256 = "ff7a82736e158c077e76188232eac77913a15dac0b22508c390ab3f88e6d6d86", sha256 = "6a5d7d63cd6e0ad2a7130471105a3b83799a7a2b14ef7ec8d742b54f01a4833c",
) )
# Google benchmark. # Google benchmark.

View file

@ -943,9 +943,10 @@ void c_partial_sort(
// c_partial_sort_copy() // c_partial_sort_copy()
// //
// Container-based version of the <algorithm> `std::partial_sort_copy()` // Container-based version of the <algorithm> `std::partial_sort_copy()`
// function to sort elements within a container such that elements before // function to sort the elements in the given range `result` within the larger
// `middle` are sorted in ascending order, and return the result within an // `sequence` in ascending order (and using `result` as the output parameter).
// iterator. // At most min(result.last - result.first, sequence.last - sequence.first)
// elements from the sequence will be stored in the result.
template <typename C, typename RandomAccessContainer> template <typename C, typename RandomAccessContainer>
container_algorithm_internal::ContainerIter<RandomAccessContainer> container_algorithm_internal::ContainerIter<RandomAccessContainer>
c_partial_sort_copy(const C& sequence, RandomAccessContainer& result) { c_partial_sort_copy(const C& sequence, RandomAccessContainer& result) {

View file

@ -115,10 +115,18 @@ cc_library(
cc_library( cc_library(
name = "dynamic_annotations", name = "dynamic_annotations",
srcs = ["dynamic_annotations.cc"], srcs = [
hdrs = ["dynamic_annotations.h"], "dynamic_annotations.cc",
"internal/dynamic_annotations.h",
],
hdrs = [
"dynamic_annotations.h",
],
copts = ABSL_DEFAULT_COPTS, copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS, linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":config",
],
) )
cc_library( cc_library(
@ -791,3 +799,16 @@ cc_test(
"@com_google_googletest//:gtest_main", "@com_google_googletest//:gtest_main",
], ],
) )
cc_test(
name = "optimization_test",
size = "small",
srcs = ["optimization_test.cc"],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":core_headers",
"//absl/types:optional",
"@com_google_googletest//:gtest_main",
],
)

View file

@ -106,8 +106,11 @@ absl_cc_library(
"dynamic_annotations.h" "dynamic_annotations.h"
SRCS SRCS
"dynamic_annotations.cc" "dynamic_annotations.cc"
"internal/dynamic_annotations.h"
COPTS COPTS
${ABSL_DEFAULT_COPTS} ${ABSL_DEFAULT_COPTS}
DEPS
absl::config
PUBLIC PUBLIC
) )
@ -698,3 +701,16 @@ absl_cc_test(
absl::fast_type_id absl::fast_type_id
gtest_main gtest_main
) )
absl_cc_test(
NAME
optimization_test
SRCS
"optimization_test.cc"
COPTS
${ABSL_TEST_COPTS}
DEPS
absl::core_headers
absl::optional
gtest_main
)

View file

@ -17,72 +17,17 @@
#include "absl/base/dynamic_annotations.h" #include "absl/base/dynamic_annotations.h"
#ifndef __has_feature // Compiler-based ThreadSanitizer defines
#define __has_feature(x) 0 // DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL = 1
#endif // and provides its own definitions of the functions.
/* Compiler-based ThreadSanitizer defines
DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL = 1
and provides its own definitions of the functions. */
#ifndef DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL #ifndef DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL
# define DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL 0 # define DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL 0
#endif #endif
/* Each function is empty and called (via a macro) only in debug mode.
The arguments are captured by dynamic tools at runtime. */
#if DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL == 0 && !defined(__native_client__) #if DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL == 0 && !defined(__native_client__)
#if __has_feature(memory_sanitizer)
#include <sanitizer/msan_interface.h>
#endif
#ifdef __cplusplus
extern "C" { extern "C" {
#endif
void AnnotateRWLockCreate(const char *, int,
const volatile void *){}
void AnnotateRWLockDestroy(const char *, int,
const volatile void *){}
void AnnotateRWLockAcquired(const char *, int,
const volatile void *, long){}
void AnnotateRWLockReleased(const char *, int,
const volatile void *, long){}
void AnnotateBenignRace(const char *, int,
const volatile void *,
const char *){}
void AnnotateBenignRaceSized(const char *, int,
const volatile void *,
size_t,
const char *) {}
void AnnotateThreadName(const char *, int,
const char *){}
void AnnotateIgnoreReadsBegin(const char *, int){}
void AnnotateIgnoreReadsEnd(const char *, int){}
void AnnotateIgnoreWritesBegin(const char *, int){}
void AnnotateIgnoreWritesEnd(const char *, int){}
void AnnotateEnableRaceDetection(const char *, int, int){}
void AnnotateMemoryIsInitialized(const char *, int,
const volatile void *mem, size_t size) {
#if __has_feature(memory_sanitizer)
__msan_unpoison(mem, size);
#else
(void)mem;
(void)size;
#endif
}
void AnnotateMemoryIsUninitialized(const char *, int,
const volatile void *mem, size_t size) {
#if __has_feature(memory_sanitizer)
__msan_allocated_memory(mem, size);
#else
(void)mem;
(void)size;
#endif
}
static int GetRunningOnValgrind(void) { static int GetRunningOnValgrind(void) {
#ifdef RUNNING_ON_VALGRIND #ifdef RUNNING_ON_VALGRIND
@ -95,21 +40,21 @@ static int GetRunningOnValgrind(void) {
return 0; return 0;
} }
/* See the comments in dynamic_annotations.h */ // See the comments in dynamic_annotations.h
int RunningOnValgrind(void) { int RunningOnValgrind(void) {
static volatile int running_on_valgrind = -1; static volatile int running_on_valgrind = -1;
int local_running_on_valgrind = running_on_valgrind; int local_running_on_valgrind = running_on_valgrind;
/* C doesn't have thread-safe initialization of statics, and we // C doesn't have thread-safe initialization of statics, and we
don't want to depend on pthread_once here, so hack it. */ // don't want to depend on pthread_once here, so hack it.
ANNOTATE_BENIGN_RACE(&running_on_valgrind, "safe hack"); ANNOTATE_BENIGN_RACE(&running_on_valgrind, "safe hack");
if (local_running_on_valgrind == -1) if (local_running_on_valgrind == -1)
running_on_valgrind = local_running_on_valgrind = GetRunningOnValgrind(); running_on_valgrind = local_running_on_valgrind = GetRunningOnValgrind();
return local_running_on_valgrind; return local_running_on_valgrind;
} }
/* See the comments in dynamic_annotations.h */ // See the comments in dynamic_annotations.h
double ValgrindSlowdown(void) { double ValgrindSlowdown(void) {
/* Same initialization hack as in RunningOnValgrind(). */ // Same initialization hack as in RunningOnValgrind().
static volatile double slowdown = 0.0; static volatile double slowdown = 0.0;
double local_slowdown = slowdown; double local_slowdown = slowdown;
ANNOTATE_BENIGN_RACE(&slowdown, "safe hack"); ANNOTATE_BENIGN_RACE(&slowdown, "safe hack");
@ -123,7 +68,5 @@ double ValgrindSlowdown(void) {
return local_slowdown; return local_slowdown;
} }
#ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif // DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL == 0
#endif /* DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL == 0 */

View file

@ -1,386 +1,504 @@
/* // Copyright 2017 The Abseil Authors.
* Copyright 2017 The Abseil Authors. //
* // Licensed under the Apache License, Version 2.0 (the "License");
* Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License.
* you may not use this file except in compliance with the License. // You may obtain a copy of the License at
* You may obtain a copy of the License at //
* // https://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0 //
* // Unless required by applicable law or agreed to in writing, software
* Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS,
* distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and
* See the License for the specific language governing permissions and // limitations under the License.
* limitations under the License.
*/
/* This file defines dynamic annotations for use with dynamic analysis
tool such as valgrind, PIN, etc.
Dynamic annotation is a source code annotation that affects // This file defines dynamic annotations for use with dynamic analysis tool
the generated code (that is, the annotation is not a comment). // such as valgrind, PIN, etc.
Each such annotation is attached to a particular //
instruction and/or to a particular object (address) in the program. // Dynamic annotation is a source code annotation that affects the generated
// code (that is, the annotation is not a comment). Each such annotation is
The annotations that should be used by users are macros in all upper-case // attached to a particular instruction and/or to a particular object (address)
(e.g., ANNOTATE_THREAD_NAME). // in the program.
//
Actual implementation of these macros may differ depending on the // The annotations that should be used by users are macros in all upper-case
dynamic analysis tool being used. // (e.g., ABSL_ANNOTATE_THREAD_NAME).
//
This file supports the following configurations: // Actual implementation of these macros may differ depending on the dynamic
- Dynamic Annotations enabled (with static thread-safety warnings disabled). // analysis tool being used.
In this case, macros expand to functions implemented by Thread Sanitizer, //
when building with TSan. When not provided an external implementation, // This file supports the following configurations:
dynamic_annotations.cc provides no-op implementations. // - Dynamic Annotations enabled (with static thread-safety warnings disabled).
// In this case, macros expand to functions implemented by Thread Sanitizer,
- Static Clang thread-safety warnings enabled. // when building with TSan. When not provided an external implementation,
When building with a Clang compiler that supports thread-safety warnings, // dynamic_annotations.cc provides no-op implementations.
a subset of annotations can be statically-checked at compile-time. We //
expand these macros to static-inline functions that can be analyzed for // - Static Clang thread-safety warnings enabled.
thread-safety, but afterwards elided when building the final binary. // When building with a Clang compiler that supports thread-safety warnings,
// a subset of annotations can be statically-checked at compile-time. We
- All annotations are disabled. // expand these macros to static-inline functions that can be analyzed for
If neither Dynamic Annotations nor Clang thread-safety warnings are // thread-safety, but afterwards elided when building the final binary.
enabled, then all annotation-macros expand to empty. */ //
// - All annotations are disabled.
// If neither Dynamic Annotations nor Clang thread-safety warnings are
// enabled, then all annotation-macros expand to empty.
#ifndef ABSL_BASE_DYNAMIC_ANNOTATIONS_H_ #ifndef ABSL_BASE_DYNAMIC_ANNOTATIONS_H_
#define ABSL_BASE_DYNAMIC_ANNOTATIONS_H_ #define ABSL_BASE_DYNAMIC_ANNOTATIONS_H_
#include <stddef.h>
#include "absl/base/config.h"
// TODO(rogeeff): Remove after the backward compatibility period.
#include "absl/base/internal/dynamic_annotations.h" // IWYU pragma: export
// -------------------------------------------------------------------------
// Decide which features are enabled
#ifndef DYNAMIC_ANNOTATIONS_ENABLED #ifndef DYNAMIC_ANNOTATIONS_ENABLED
# define DYNAMIC_ANNOTATIONS_ENABLED 0 #define DYNAMIC_ANNOTATIONS_ENABLED 0
#endif
#if defined(__clang__) && !defined(SWIG)
#define ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED 1
#else
#define ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED 0
#endif #endif
#if DYNAMIC_ANNOTATIONS_ENABLED != 0 #if DYNAMIC_ANNOTATIONS_ENABLED != 0
/* ------------------------------------------------------------- #define ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED 1
Annotations that suppress errors. It is usually better to express the #define ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED 1
program's synchronization using the other annotations, but these can #define ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED 1
be used when all else fails. */ #define ABSL_INTERNAL_ANNOTALYSIS_ENABLED 0
#define ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED 1
/* Report that we may have a benign race at "pointer", with size
"sizeof(*(pointer))". "pointer" must be a non-void* pointer. Insert at the
point where "pointer" has been allocated, preferably close to the point
where the race happens. See also ANNOTATE_BENIGN_RACE_STATIC. */
#define ANNOTATE_BENIGN_RACE(pointer, description) \
AnnotateBenignRaceSized(__FILE__, __LINE__, pointer, \
sizeof(*(pointer)), description)
/* Same as ANNOTATE_BENIGN_RACE(address, description), but applies to
the memory range [address, address+size). */
#define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \
AnnotateBenignRaceSized(__FILE__, __LINE__, address, size, description)
/* Enable (enable!=0) or disable (enable==0) race detection for all threads.
This annotation could be useful if you want to skip expensive race analysis
during some period of program execution, e.g. during initialization. */
#define ANNOTATE_ENABLE_RACE_DETECTION(enable) \
AnnotateEnableRaceDetection(__FILE__, __LINE__, enable)
/* -------------------------------------------------------------
Annotations useful for debugging. */
/* Report the current thread name to a race detector. */
#define ANNOTATE_THREAD_NAME(name) \
AnnotateThreadName(__FILE__, __LINE__, name)
/* -------------------------------------------------------------
Annotations useful when implementing locks. They are not
normally needed by modules that merely use locks.
The "lock" argument is a pointer to the lock object. */
/* Report that a lock has been created at address "lock". */
#define ANNOTATE_RWLOCK_CREATE(lock) \
AnnotateRWLockCreate(__FILE__, __LINE__, lock)
/* Report that a linker initialized lock has been created at address "lock".
*/
#ifdef THREAD_SANITIZER
#define ANNOTATE_RWLOCK_CREATE_STATIC(lock) \
AnnotateRWLockCreateStatic(__FILE__, __LINE__, lock)
#else #else
#define ANNOTATE_RWLOCK_CREATE_STATIC(lock) ANNOTATE_RWLOCK_CREATE(lock)
#define ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED 0
#define ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED 0
#define ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED 0
// Clang provides limited support for static thread-safety analysis through a
// feature called Annotalysis. We configure macro-definitions according to
// whether Annotalysis support is available. When running in opt-mode, GCC
// will issue a warning, if these attributes are compiled. Only include them
// when compiling using Clang.
// ANNOTALYSIS_ENABLED == 1 when IGNORE_READ_ATTRIBUTE_ENABLED == 1
#define ABSL_INTERNAL_ANNOTALYSIS_ENABLED \
ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED
// Read/write annotations are enabled in Annotalysis mode; disabled otherwise.
#define ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED \
ABSL_INTERNAL_ANNOTALYSIS_ENABLED
#endif #endif
/* Report that the lock at address "lock" is about to be destroyed. */ // Memory annotations are also made available to LLVM's Memory Sanitizer
#define ANNOTATE_RWLOCK_DESTROY(lock) \ #if defined(MEMORY_SANITIZER) && defined(__has_feature) && \
AnnotateRWLockDestroy(__FILE__, __LINE__, lock) !defined(__native_client__)
#if __has_feature(memory_sanitizer)
/* Report that the lock at address "lock" has been acquired. #define ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED 1
is_w=1 for writer lock, is_w=0 for reader lock. */ #endif
#define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \
AnnotateRWLockAcquired(__FILE__, __LINE__, lock, is_w)
/* Report that the lock at address "lock" is about to be released. */
#define ANNOTATE_RWLOCK_RELEASED(lock, is_w) \
AnnotateRWLockReleased(__FILE__, __LINE__, lock, is_w)
#else /* DYNAMIC_ANNOTATIONS_ENABLED == 0 */
#define ANNOTATE_RWLOCK_CREATE(lock) /* empty */
#define ANNOTATE_RWLOCK_CREATE_STATIC(lock) /* empty */
#define ANNOTATE_RWLOCK_DESTROY(lock) /* empty */
#define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) /* empty */
#define ANNOTATE_RWLOCK_RELEASED(lock, is_w) /* empty */
#define ANNOTATE_BENIGN_RACE(address, description) /* empty */
#define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) /* empty */
#define ANNOTATE_THREAD_NAME(name) /* empty */
#define ANNOTATE_ENABLE_RACE_DETECTION(enable) /* empty */
#endif /* DYNAMIC_ANNOTATIONS_ENABLED */
/* These annotations are also made available to LLVM's Memory Sanitizer */
#if DYNAMIC_ANNOTATIONS_ENABLED == 1 || defined(MEMORY_SANITIZER)
#define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \
AnnotateMemoryIsInitialized(__FILE__, __LINE__, address, size)
#define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \
AnnotateMemoryIsUninitialized(__FILE__, __LINE__, address, size)
#else
#define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) /* empty */
#define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) /* empty */
#endif /* DYNAMIC_ANNOTATIONS_ENABLED || MEMORY_SANITIZER */
#if defined(__clang__) && !defined(SWIG)
#if DYNAMIC_ANNOTATIONS_ENABLED == 0
#define ANNOTALYSIS_ENABLED
#endif
/* When running in opt-mode, GCC will issue a warning, if these attributes are
compiled. Only include them when compiling using Clang. */
#define ATTRIBUTE_IGNORE_READS_BEGIN \
__attribute((exclusive_lock_function("*")))
#define ATTRIBUTE_IGNORE_READS_END \
__attribute((unlock_function("*")))
#else
#define ATTRIBUTE_IGNORE_READS_BEGIN /* empty */
#define ATTRIBUTE_IGNORE_READS_END /* empty */
#endif /* defined(__clang__) && ... */
#if (DYNAMIC_ANNOTATIONS_ENABLED != 0) || defined(ANNOTALYSIS_ENABLED)
#define ANNOTATIONS_ENABLED
#endif #endif
#if (DYNAMIC_ANNOTATIONS_ENABLED != 0) #ifndef ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED
#define ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED 0
/* Request the analysis tool to ignore all reads in the current thread
until ANNOTATE_IGNORE_READS_END is called.
Useful to ignore intentional racey reads, while still checking
other reads and all writes.
See also ANNOTATE_UNPROTECTED_READ. */
#define ANNOTATE_IGNORE_READS_BEGIN() \
AnnotateIgnoreReadsBegin(__FILE__, __LINE__)
/* Stop ignoring reads. */
#define ANNOTATE_IGNORE_READS_END() \
AnnotateIgnoreReadsEnd(__FILE__, __LINE__)
/* Similar to ANNOTATE_IGNORE_READS_BEGIN, but ignore writes instead. */
#define ANNOTATE_IGNORE_WRITES_BEGIN() \
AnnotateIgnoreWritesBegin(__FILE__, __LINE__)
/* Stop ignoring writes. */
#define ANNOTATE_IGNORE_WRITES_END() \
AnnotateIgnoreWritesEnd(__FILE__, __LINE__)
/* Clang provides limited support for static thread-safety analysis
through a feature called Annotalysis. We configure macro-definitions
according to whether Annotalysis support is available. */
#elif defined(ANNOTALYSIS_ENABLED)
#define ANNOTATE_IGNORE_READS_BEGIN() \
StaticAnnotateIgnoreReadsBegin(__FILE__, __LINE__)
#define ANNOTATE_IGNORE_READS_END() \
StaticAnnotateIgnoreReadsEnd(__FILE__, __LINE__)
#define ANNOTATE_IGNORE_WRITES_BEGIN() \
StaticAnnotateIgnoreWritesBegin(__FILE__, __LINE__)
#define ANNOTATE_IGNORE_WRITES_END() \
StaticAnnotateIgnoreWritesEnd(__FILE__, __LINE__)
#else
#define ANNOTATE_IGNORE_READS_BEGIN() /* empty */
#define ANNOTATE_IGNORE_READS_END() /* empty */
#define ANNOTATE_IGNORE_WRITES_BEGIN() /* empty */
#define ANNOTATE_IGNORE_WRITES_END() /* empty */
#endif #endif
/* Implement the ANNOTATE_IGNORE_READS_AND_WRITES_* annotations using the more
primitive annotations defined above. */
#if defined(ANNOTATIONS_ENABLED)
/* Start ignoring all memory accesses (both reads and writes). */
#define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \
do { \
ANNOTATE_IGNORE_READS_BEGIN(); \
ANNOTATE_IGNORE_WRITES_BEGIN(); \
}while (0)
/* Stop ignoring both reads and writes. */
#define ANNOTATE_IGNORE_READS_AND_WRITES_END() \
do { \
ANNOTATE_IGNORE_WRITES_END(); \
ANNOTATE_IGNORE_READS_END(); \
}while (0)
#else
#define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() /* empty */
#define ANNOTATE_IGNORE_READS_AND_WRITES_END() /* empty */
#endif
/* Use the macros above rather than using these functions directly. */
#include <stddef.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { #define ABSL_INTERNAL_BEGIN_EXTERN_C extern "C" {
#endif #define ABSL_INTERNAL_END_EXTERN_C } // extern "C"
void AnnotateRWLockCreate(const char *file, int line, #define ABSL_INTERNAL_GLOBAL_SCOPED(F) ::F
const volatile void *lock); #define ABSL_INTERNAL_STATIC_INLINE inline
void AnnotateRWLockCreateStatic(const char *file, int line, #else
const volatile void *lock); #define ABSL_INTERNAL_BEGIN_EXTERN_C // empty
void AnnotateRWLockDestroy(const char *file, int line, #define ABSL_INTERNAL_END_EXTERN_C // empty
const volatile void *lock); #define ABSL_INTERNAL_GLOBAL_SCOPED(F) F
void AnnotateRWLockAcquired(const char *file, int line, #define ABSL_INTERNAL_STATIC_INLINE static inline
const volatile void *lock, long is_w); /* NOLINT */
void AnnotateRWLockReleased(const char *file, int line,
const volatile void *lock, long is_w); /* NOLINT */
void AnnotateBenignRace(const char *file, int line,
const volatile void *address,
const char *description);
void AnnotateBenignRaceSized(const char *file, int line,
const volatile void *address,
size_t size,
const char *description);
void AnnotateThreadName(const char *file, int line,
const char *name);
void AnnotateEnableRaceDetection(const char *file, int line, int enable);
void AnnotateMemoryIsInitialized(const char *file, int line,
const volatile void *mem, size_t size);
void AnnotateMemoryIsUninitialized(const char *file, int line,
const volatile void *mem, size_t size);
/* Annotations expand to these functions, when Dynamic Annotations are enabled.
These functions are either implemented as no-op calls, if no Sanitizer is
attached, or provided with externally-linked implementations by a library
like ThreadSanitizer. */
void AnnotateIgnoreReadsBegin(const char *file, int line)
ATTRIBUTE_IGNORE_READS_BEGIN;
void AnnotateIgnoreReadsEnd(const char *file, int line)
ATTRIBUTE_IGNORE_READS_END;
void AnnotateIgnoreWritesBegin(const char *file, int line);
void AnnotateIgnoreWritesEnd(const char *file, int line);
#if defined(ANNOTALYSIS_ENABLED)
/* When Annotalysis is enabled without Dynamic Annotations, the use of
static-inline functions allows the annotations to be read at compile-time,
while still letting the compiler elide the functions from the final build.
TODO(delesley) -- The exclusive lock here ignores writes as well, but
allows IGNORE_READS_AND_WRITES to work properly. */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
static inline void StaticAnnotateIgnoreReadsBegin(const char *file, int line)
ATTRIBUTE_IGNORE_READS_BEGIN { (void)file; (void)line; }
static inline void StaticAnnotateIgnoreReadsEnd(const char *file, int line)
ATTRIBUTE_IGNORE_READS_END { (void)file; (void)line; }
static inline void StaticAnnotateIgnoreWritesBegin(
const char *file, int line) { (void)file; (void)line; }
static inline void StaticAnnotateIgnoreWritesEnd(
const char *file, int line) { (void)file; (void)line; }
#pragma GCC diagnostic pop
#endif #endif
/* Return non-zero value if running under valgrind. // -------------------------------------------------------------------------
// Define race annotations.
If "valgrind.h" is included into dynamic_annotations.cc, #if ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 1
the regular valgrind mechanism will be used.
See http://valgrind.org/docs/manual/manual-core-adv.html about
RUNNING_ON_VALGRIND and other valgrind "client requests".
The file "valgrind.h" may be obtained by doing
svn co svn://svn.valgrind.org/valgrind/trunk/include
If for some reason you can't use "valgrind.h" or want to fake valgrind, // -------------------------------------------------------------
there are two ways to make this function return non-zero: // Annotations that suppress errors. It is usually better to express the
- Use environment variable: export RUNNING_ON_VALGRIND=1 // program's synchronization using the other annotations, but these can be used
- Make your tool intercept the function RunningOnValgrind() and // when all else fails.
change its return value.
*/ // Report that we may have a benign race at `pointer`, with size
// "sizeof(*(pointer))". `pointer` must be a non-void* pointer. Insert at the
// point where `pointer` has been allocated, preferably close to the point
// where the race happens. See also ABSL_ANNOTATE_BENIGN_RACE_STATIC.
#define ABSL_ANNOTATE_BENIGN_RACE(pointer, description) \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateBenignRaceSized) \
(__FILE__, __LINE__, pointer, sizeof(*(pointer)), description)
// Same as ABSL_ANNOTATE_BENIGN_RACE(`address`, `description`), but applies to
// the memory range [`address`, `address`+`size`).
#define ABSL_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateBenignRaceSized) \
(__FILE__, __LINE__, address, size, description)
// Enable (`enable`!=0) or disable (`enable`==0) race detection for all threads.
// This annotation could be useful if you want to skip expensive race analysis
// during some period of program execution, e.g. during initialization.
#define ABSL_ANNOTATE_ENABLE_RACE_DETECTION(enable) \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateEnableRaceDetection) \
(__FILE__, __LINE__, enable)
// -------------------------------------------------------------
// Annotations useful for debugging.
// Report the current thread `name` to a race detector.
#define ABSL_ANNOTATE_THREAD_NAME(name) \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateThreadName)(__FILE__, __LINE__, name)
// -------------------------------------------------------------
// Annotations useful when implementing locks. They are not normally needed by
// modules that merely use locks. The `lock` argument is a pointer to the lock
// object.
// Report that a lock has been created at address `lock`.
#define ABSL_ANNOTATE_RWLOCK_CREATE(lock) \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockCreate)(__FILE__, __LINE__, lock)
// Report that a linker initialized lock has been created at address `lock`.
#ifdef THREAD_SANITIZER
#define ABSL_ANNOTATE_RWLOCK_CREATE_STATIC(lock) \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockCreateStatic) \
(__FILE__, __LINE__, lock)
#else
#define ABSL_ANNOTATE_RWLOCK_CREATE_STATIC(lock) \
ABSL_ANNOTATE_RWLOCK_CREATE(lock)
#endif
// Report that the lock at address `lock` is about to be destroyed.
#define ABSL_ANNOTATE_RWLOCK_DESTROY(lock) \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockDestroy)(__FILE__, __LINE__, lock)
// Report that the lock at address `lock` has been acquired.
// `is_w`=1 for writer lock, `is_w`=0 for reader lock.
#define ABSL_ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockAcquired) \
(__FILE__, __LINE__, lock, is_w)
// Report that the lock at address `lock` is about to be released.
// `is_w`=1 for writer lock, `is_w`=0 for reader lock.
#define ABSL_ANNOTATE_RWLOCK_RELEASED(lock, is_w) \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockReleased) \
(__FILE__, __LINE__, lock, is_w)
// Apply ABSL_ANNOTATE_BENIGN_RACE_SIZED to a static variable `static_var`.
#define ABSL_ANNOTATE_BENIGN_RACE_STATIC(static_var, description) \
namespace { \
class static_var##_annotator { \
public: \
static_var##_annotator() { \
ABSL_ANNOTATE_BENIGN_RACE_SIZED(&static_var, sizeof(static_var), \
#static_var ": " description); \
} \
}; \
static static_var##_annotator the##static_var##_annotator; \
} // namespace
// Function prototypes of annotations provided by the compiler-based sanitizer
// implementation.
ABSL_INTERNAL_BEGIN_EXTERN_C
void AnnotateRWLockCreate(const char* file, int line,
const volatile void* lock);
void AnnotateRWLockCreateStatic(const char* file, int line,
const volatile void* lock);
void AnnotateRWLockDestroy(const char* file, int line,
const volatile void* lock);
void AnnotateRWLockAcquired(const char* file, int line,
const volatile void* lock, long is_w); // NOLINT
void AnnotateRWLockReleased(const char* file, int line,
const volatile void* lock, long is_w); // NOLINT
void AnnotateBenignRace(const char* file, int line,
const volatile void* address, const char* description);
void AnnotateBenignRaceSized(const char* file, int line,
const volatile void* address, size_t size,
const char* description);
void AnnotateThreadName(const char* file, int line, const char* name);
void AnnotateEnableRaceDetection(const char* file, int line, int enable);
ABSL_INTERNAL_END_EXTERN_C
#else // ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 0
#define ABSL_ANNOTATE_RWLOCK_CREATE(lock) // empty
#define ABSL_ANNOTATE_RWLOCK_CREATE_STATIC(lock) // empty
#define ABSL_ANNOTATE_RWLOCK_DESTROY(lock) // empty
#define ABSL_ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) // empty
#define ABSL_ANNOTATE_RWLOCK_RELEASED(lock, is_w) // empty
#define ABSL_ANNOTATE_BENIGN_RACE(address, description) // empty
#define ABSL_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) // empty
#define ABSL_ANNOTATE_THREAD_NAME(name) // empty
#define ABSL_ANNOTATE_ENABLE_RACE_DETECTION(enable) // empty
#define ABSL_ANNOTATE_BENIGN_RACE_STATIC(static_var, description) // empty
#endif // ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED
// -------------------------------------------------------------------------
// Define memory annotations.
#if ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED == 1
#include <sanitizer/msan_interface.h>
#define ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \
__msan_unpoison(address, size)
#define ABSL_ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \
__msan_allocated_memory(address, size)
#else // ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED == 0
#if DYNAMIC_ANNOTATIONS_ENABLED == 1
#define ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \
do { \
(void)(address); \
(void)(size); \
} while (0)
#define ABSL_ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \
do { \
(void)(address); \
(void)(size); \
} while (0)
#else
#define ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) // empty
#define ABSL_ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) // empty
#endif
#endif // ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED
// -------------------------------------------------------------------------
// Define IGNORE_READS_BEGIN/_END attributes.
#if ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED == 1
#define ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE \
__attribute((exclusive_lock_function("*")))
#define ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE \
__attribute((unlock_function("*")))
#else // ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED == 0
#define ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE // empty
#define ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE // empty
#endif // ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED
// -------------------------------------------------------------------------
// Define IGNORE_READS_BEGIN/_END annotations.
#if ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED == 1
// Request the analysis tool to ignore all reads in the current thread until
// ABSL_ANNOTATE_IGNORE_READS_END is called. Useful to ignore intentional racey
// reads, while still checking other reads and all writes.
// See also ABSL_ANNOTATE_UNPROTECTED_READ.
#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsBegin)(__FILE__, __LINE__)
// Stop ignoring reads.
#define ABSL_ANNOTATE_IGNORE_READS_END() \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsEnd)(__FILE__, __LINE__)
// Function prototypes of annotations provided by the compiler-based sanitizer
// implementation.
ABSL_INTERNAL_BEGIN_EXTERN_C
void AnnotateIgnoreReadsBegin(const char* file, int line)
ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE;
void AnnotateIgnoreReadsEnd(const char* file,
int line) ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE;
ABSL_INTERNAL_END_EXTERN_C
#elif defined(ABSL_INTERNAL_ANNOTALYSIS_ENABLED)
// When Annotalysis is enabled without Dynamic Annotations, the use of
// static-inline functions allows the annotations to be read at compile-time,
// while still letting the compiler elide the functions from the final build.
//
// TODO(delesley) -- The exclusive lock here ignores writes as well, but
// allows IGNORE_READS_AND_WRITES to work properly.
#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \
ABSL_INTERNAL_GLOBAL_SCOPED(AbslInternalAnnotateIgnoreReadsBegin)()
#define ABSL_ANNOTATE_IGNORE_READS_END() \
ABSL_INTERNAL_GLOBAL_SCOPED(AbslInternalAnnotateIgnoreReadsEnd)()
ABSL_INTERNAL_STATIC_INLINE void AbslInternalAnnotateIgnoreReadsBegin()
ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE {}
ABSL_INTERNAL_STATIC_INLINE void AbslInternalAnnotateIgnoreReadsEnd()
ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE {}
#else
#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() // empty
#define ABSL_ANNOTATE_IGNORE_READS_END() // empty
#endif
// -------------------------------------------------------------------------
// Define IGNORE_WRITES_BEGIN/_END annotations.
#if ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED == 1
// Similar to ABSL_ANNOTATE_IGNORE_READS_BEGIN, but ignore writes instead.
#define ABSL_ANNOTATE_IGNORE_WRITES_BEGIN() \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreWritesBegin)(__FILE__, __LINE__)
// Stop ignoring writes.
#define ABSL_ANNOTATE_IGNORE_WRITES_END() \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreWritesEnd)(__FILE__, __LINE__)
// Function prototypes of annotations provided by the compiler-based sanitizer
// implementation.
ABSL_INTERNAL_BEGIN_EXTERN_C
void AnnotateIgnoreWritesBegin(const char* file, int line);
void AnnotateIgnoreWritesEnd(const char* file, int line);
ABSL_INTERNAL_END_EXTERN_C
#else
#define ABSL_ANNOTATE_IGNORE_WRITES_BEGIN() // empty
#define ABSL_ANNOTATE_IGNORE_WRITES_END() // empty
#endif
// -------------------------------------------------------------------------
// Define the ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_* annotations using the more
// primitive annotations defined above.
//
// Instead of doing
// ABSL_ANNOTATE_IGNORE_READS_BEGIN();
// ... = x;
// ABSL_ANNOTATE_IGNORE_READS_END();
// one can use
// ... = ABSL_ANNOTATE_UNPROTECTED_READ(x);
#if defined(ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED)
// Start ignoring all memory accesses (both reads and writes).
#define ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \
do { \
ABSL_ANNOTATE_IGNORE_READS_BEGIN(); \
ABSL_ANNOTATE_IGNORE_WRITES_BEGIN(); \
} while (0)
// Stop ignoring both reads and writes.
#define ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_END() \
do { \
ABSL_ANNOTATE_IGNORE_WRITES_END(); \
ABSL_ANNOTATE_IGNORE_READS_END(); \
} while (0)
#ifdef __cplusplus
// ABSL_ANNOTATE_UNPROTECTED_READ is the preferred way to annotate racey reads.
#define ABSL_ANNOTATE_UNPROTECTED_READ(x) \
absl::base_internal::AnnotateUnprotectedRead(x)
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace base_internal {
template <typename T>
inline T AnnotateUnprotectedRead(const volatile T& x) { // NOLINT
ABSL_ANNOTATE_IGNORE_READS_BEGIN();
T res = x;
ABSL_ANNOTATE_IGNORE_READS_END();
return res;
}
} // namespace base_internal
ABSL_NAMESPACE_END
} // namespace absl
#endif
#else
#define ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() // empty
#define ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_END() // empty
#define ABSL_ANNOTATE_UNPROTECTED_READ(x) (x)
#endif
ABSL_INTERNAL_BEGIN_EXTERN_C
// -------------------------------------------------------------------------
// Return non-zero value if running under valgrind.
//
// If "valgrind.h" is included into dynamic_annotations.cc,
// the regular valgrind mechanism will be used.
// See http://valgrind.org/docs/manual/manual-core-adv.html about
// RUNNING_ON_VALGRIND and other valgrind "client requests".
// The file "valgrind.h" may be obtained by doing
// svn co svn://svn.valgrind.org/valgrind/trunk/include
//
// If for some reason you can't use "valgrind.h" or want to fake valgrind,
// there are two ways to make this function return non-zero:
// - Use environment variable: export RUNNING_ON_VALGRIND=1
// - Make your tool intercept the function RunningOnValgrind() and
// change its return value.
//
int RunningOnValgrind(void); int RunningOnValgrind(void);
/* ValgrindSlowdown returns: // ValgrindSlowdown returns:
* 1.0, if (RunningOnValgrind() == 0) // * 1.0, if (RunningOnValgrind() == 0)
* 50.0, if (RunningOnValgrind() != 0 && getenv("VALGRIND_SLOWDOWN") == NULL) // * 50.0, if (RunningOnValgrind() != 0 && getenv("VALGRIND_SLOWDOWN") ==
* atof(getenv("VALGRIND_SLOWDOWN")) otherwise // NULL)
This function can be used to scale timeout values: // * atof(getenv("VALGRIND_SLOWDOWN")) otherwise
EXAMPLE: // This function can be used to scale timeout values:
for (;;) { // EXAMPLE:
DoExpensiveBackgroundTask(); // for (;;) {
SleepForSeconds(5 * ValgrindSlowdown()); // DoExpensiveBackgroundTask();
} // SleepForSeconds(5 * ValgrindSlowdown());
*/ // }
//
double ValgrindSlowdown(void); double ValgrindSlowdown(void);
#ifdef __cplusplus ABSL_INTERNAL_END_EXTERN_C
}
#endif
/* ANNOTATE_UNPROTECTED_READ is the preferred way to annotate racey reads. // -------------------------------------------------------------------------
// Address sanitizer annotations
Instead of doing
ANNOTATE_IGNORE_READS_BEGIN();
... = x;
ANNOTATE_IGNORE_READS_END();
one can use
... = ANNOTATE_UNPROTECTED_READ(x); */
#if defined(__cplusplus) && defined(ANNOTATIONS_ENABLED)
template <typename T>
inline T ANNOTATE_UNPROTECTED_READ(const volatile T &x) { /* NOLINT */
ANNOTATE_IGNORE_READS_BEGIN();
T res = x;
ANNOTATE_IGNORE_READS_END();
return res;
}
#else
#define ANNOTATE_UNPROTECTED_READ(x) (x)
#endif
#if DYNAMIC_ANNOTATIONS_ENABLED != 0 && defined(__cplusplus)
/* Apply ANNOTATE_BENIGN_RACE_SIZED to a static variable. */
#define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) \
namespace { \
class static_var ## _annotator { \
public: \
static_var ## _annotator() { \
ANNOTATE_BENIGN_RACE_SIZED(&static_var, \
sizeof(static_var), \
# static_var ": " description); \
} \
}; \
static static_var ## _annotator the ## static_var ## _annotator;\
} // namespace
#else /* DYNAMIC_ANNOTATIONS_ENABLED == 0 */
#define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) /* empty */
#endif /* DYNAMIC_ANNOTATIONS_ENABLED */
#ifdef ADDRESS_SANITIZER #ifdef ADDRESS_SANITIZER
/* Describe the current state of a contiguous container such as e.g. // Describe the current state of a contiguous container such as e.g.
* std::vector or std::string. For more details see // std::vector or std::string. For more details see
* sanitizer/common_interface_defs.h, which is provided by the compiler. */ // sanitizer/common_interface_defs.h, which is provided by the compiler.
#include <sanitizer/common_interface_defs.h> #include <sanitizer/common_interface_defs.h>
#define ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) \
#define ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) \
__sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid) __sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid)
#define ADDRESS_SANITIZER_REDZONE(name) \ #define ABSL_ADDRESS_SANITIZER_REDZONE(name) \
struct { char x[8] __attribute__ ((aligned (8))); } name struct { \
char x[8] __attribute__((aligned(8))); \
} name
#else #else
#define ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid)
#define ADDRESS_SANITIZER_REDZONE(name) static_assert(true, "") #define ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid)
#define ABSL_ADDRESS_SANITIZER_REDZONE(name) static_assert(true, "")
#endif // ADDRESS_SANITIZER #endif // ADDRESS_SANITIZER
/* Undefine the macros intended only in this file. */ // -------------------------------------------------------------------------
#undef ANNOTALYSIS_ENABLED // Undefine the macros intended only for this file.
#undef ANNOTATIONS_ENABLED
#undef ATTRIBUTE_IGNORE_READS_BEGIN
#undef ATTRIBUTE_IGNORE_READS_END
#endif /* ABSL_BASE_DYNAMIC_ANNOTATIONS_H_ */ #undef ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED
#undef ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED
#undef ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED
#undef ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED
#undef ABSL_INTERNAL_ANNOTALYSIS_ENABLED
#undef ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED
#undef ABSL_INTERNAL_BEGIN_EXTERN_C
#undef ABSL_INTERNAL_END_EXTERN_C
#undef ABSL_INTERNAL_STATIC_INLINE
#endif // ABSL_BASE_DYNAMIC_ANNOTATIONS_H_

View file

@ -0,0 +1,403 @@
// 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
//
// https://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.
// This file defines dynamic annotations for use with dynamic analysis tool
// such as valgrind, PIN, etc.
//
// Dynamic annotation is a source code annotation that affects the generated
// code (that is, the annotation is not a comment). Each such annotation is
// attached to a particular instruction and/or to a particular object (address)
// in the program.
//
// The annotations that should be used by users are macros in all upper-case
// (e.g., ANNOTATE_THREAD_NAME).
//
// Actual implementation of these macros may differ depending on the dynamic
// analysis tool being used.
//
// This file supports the following configurations:
// - Dynamic Annotations enabled (with static thread-safety warnings disabled).
// In this case, macros expand to functions implemented by Thread Sanitizer,
// when building with TSan. When not provided an external implementation,
// dynamic_annotations.cc provides no-op implementations.
//
// - Static Clang thread-safety warnings enabled.
// When building with a Clang compiler that supports thread-safety warnings,
// a subset of annotations can be statically-checked at compile-time. We
// expand these macros to static-inline functions that can be analyzed for
// thread-safety, but afterwards elided when building the final binary.
//
// - All annotations are disabled.
// If neither Dynamic Annotations nor Clang thread-safety warnings are
// enabled, then all annotation-macros expand to empty.
#ifndef ABSL_BASE_INTERNAL_DYNAMIC_ANNOTATIONS_H_
#define ABSL_BASE_INTERNAL_DYNAMIC_ANNOTATIONS_H_
#include <stddef.h>
#include "absl/base/config.h"
// -------------------------------------------------------------------------
// Decide which features are enabled
#ifndef DYNAMIC_ANNOTATIONS_ENABLED
#define DYNAMIC_ANNOTATIONS_ENABLED 0
#endif
#if defined(__clang__) && !defined(SWIG)
#define ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED 1
#else
#define ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED 0
#endif
#if DYNAMIC_ANNOTATIONS_ENABLED != 0
#define ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED 1
#define ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED 1
#define ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED 1
#define ABSL_INTERNAL_ANNOTALYSIS_ENABLED 0
#define ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED 1
#else
#define ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED 0
#define ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED 0
#define ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED 0
// Clang provides limited support for static thread-safety analysis through a
// feature called Annotalysis. We configure macro-definitions according to
// whether Annotalysis support is available. When running in opt-mode, GCC
// will issue a warning, if these attributes are compiled. Only include them
// when compiling using Clang.
// ANNOTALYSIS_ENABLED == 1 when IGNORE_READ_ATTRIBUTE_ENABLED == 1
#define ABSL_INTERNAL_ANNOTALYSIS_ENABLED \
ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED
// Read/write annotations are enabled in Annotalysis mode; disabled otherwise.
#define ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED \
ABSL_INTERNAL_ANNOTALYSIS_ENABLED
#endif
// Memory annotations are also made available to LLVM's Memory Sanitizer
#if defined(MEMORY_SANITIZER) && defined(__has_feature) && \
!defined(__native_client__)
#if __has_feature(memory_sanitizer)
#define ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED 1
#endif
#endif
#ifndef ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED
#define ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED 0
#endif
#ifdef __cplusplus
#define ABSL_INTERNAL_BEGIN_EXTERN_C extern "C" {
#define ABSL_INTERNAL_END_EXTERN_C } // extern "C"
#define ABSL_INTERNAL_GLOBAL_SCOPED(F) ::F
#define ABSL_INTERNAL_STATIC_INLINE inline
#else
#define ABSL_INTERNAL_BEGIN_EXTERN_C // empty
#define ABSL_INTERNAL_END_EXTERN_C // empty
#define ABSL_INTERNAL_GLOBAL_SCOPED(F) F
#define ABSL_INTERNAL_STATIC_INLINE static inline
#endif
// -------------------------------------------------------------------------
// Define race annotations.
#if ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 1
// -------------------------------------------------------------
// Annotations that suppress errors. It is usually better to express the
// program's synchronization using the other annotations, but these can be used
// when all else fails.
// Report that we may have a benign race at `pointer`, with size
// "sizeof(*(pointer))". `pointer` must be a non-void* pointer. Insert at the
// point where `pointer` has been allocated, preferably close to the point
// where the race happens. See also ANNOTATE_BENIGN_RACE_STATIC.
#define ANNOTATE_BENIGN_RACE(pointer, description) \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateBenignRaceSized) \
(__FILE__, __LINE__, pointer, sizeof(*(pointer)), description)
// Same as ANNOTATE_BENIGN_RACE(`address`, `description`), but applies to
// the memory range [`address`, `address`+`size`).
#define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateBenignRaceSized) \
(__FILE__, __LINE__, address, size, description)
// Enable (`enable`!=0) or disable (`enable`==0) race detection for all threads.
// This annotation could be useful if you want to skip expensive race analysis
// during some period of program execution, e.g. during initialization.
#define ANNOTATE_ENABLE_RACE_DETECTION(enable) \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateEnableRaceDetection) \
(__FILE__, __LINE__, enable)
// -------------------------------------------------------------
// Annotations useful for debugging.
// Report the current thread `name` to a race detector.
#define ANNOTATE_THREAD_NAME(name) \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateThreadName)(__FILE__, __LINE__, name)
// -------------------------------------------------------------
// Annotations useful when implementing locks. They are not normally needed by
// modules that merely use locks. The `lock` argument is a pointer to the lock
// object.
// Report that a lock has been created at address `lock`.
#define ANNOTATE_RWLOCK_CREATE(lock) \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockCreate)(__FILE__, __LINE__, lock)
// Report that a linker initialized lock has been created at address `lock`.
#ifdef THREAD_SANITIZER
#define ANNOTATE_RWLOCK_CREATE_STATIC(lock) \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockCreateStatic) \
(__FILE__, __LINE__, lock)
#else
#define ANNOTATE_RWLOCK_CREATE_STATIC(lock) ANNOTATE_RWLOCK_CREATE(lock)
#endif
// Report that the lock at address `lock` is about to be destroyed.
#define ANNOTATE_RWLOCK_DESTROY(lock) \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockDestroy)(__FILE__, __LINE__, lock)
// Report that the lock at address `lock` has been acquired.
// `is_w`=1 for writer lock, `is_w`=0 for reader lock.
#define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockAcquired) \
(__FILE__, __LINE__, lock, is_w)
// Report that the lock at address `lock` is about to be released.
// `is_w`=1 for writer lock, `is_w`=0 for reader lock.
#define ANNOTATE_RWLOCK_RELEASED(lock, is_w) \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockReleased) \
(__FILE__, __LINE__, lock, is_w)
// Apply ANNOTATE_BENIGN_RACE_SIZED to a static variable `static_var`.
#define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) \
namespace { \
class static_var##_annotator { \
public: \
static_var##_annotator() { \
ANNOTATE_BENIGN_RACE_SIZED(&static_var, sizeof(static_var), \
#static_var ": " description); \
} \
}; \
static static_var##_annotator the##static_var##_annotator; \
} // namespace
#else // ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 0
#define ANNOTATE_RWLOCK_CREATE(lock) // empty
#define ANNOTATE_RWLOCK_CREATE_STATIC(lock) // empty
#define ANNOTATE_RWLOCK_DESTROY(lock) // empty
#define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) // empty
#define ANNOTATE_RWLOCK_RELEASED(lock, is_w) // empty
#define ANNOTATE_BENIGN_RACE(address, description) // empty
#define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) // empty
#define ANNOTATE_THREAD_NAME(name) // empty
#define ANNOTATE_ENABLE_RACE_DETECTION(enable) // empty
#define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) // empty
#endif // ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED
// -------------------------------------------------------------------------
// Define memory annotations.
#if ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED == 1
#include <sanitizer/msan_interface.h>
#define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \
__msan_unpoison(address, size)
#define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \
__msan_allocated_memory(address, size)
#else // ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED == 0
#if DYNAMIC_ANNOTATIONS_ENABLED == 1
#define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \
do { \
(void)(address); \
(void)(size); \
} while (0)
#define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \
do { \
(void)(address); \
(void)(size); \
} while (0)
#else
#define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) // empty
#define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) // empty
#endif
#endif // ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED
// -------------------------------------------------------------------------
// Define IGNORE_READS_BEGIN/_END attributes.
#if ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED == 1
#define ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE \
__attribute((exclusive_lock_function("*")))
#define ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE \
__attribute((unlock_function("*")))
#else // ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED == 0
#define ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE // empty
#define ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE // empty
#endif // ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED
// -------------------------------------------------------------------------
// Define IGNORE_READS_BEGIN/_END annotations.
#if ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED == 1
// Request the analysis tool to ignore all reads in the current thread until
// ANNOTATE_IGNORE_READS_END is called. Useful to ignore intentional racey
// reads, while still checking other reads and all writes.
// See also ANNOTATE_UNPROTECTED_READ.
#define ANNOTATE_IGNORE_READS_BEGIN() \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsBegin)(__FILE__, __LINE__)
// Stop ignoring reads.
#define ANNOTATE_IGNORE_READS_END() \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsEnd)(__FILE__, __LINE__)
#elif defined(ABSL_INTERNAL_ANNOTALYSIS_ENABLED)
// When Annotalysis is enabled without Dynamic Annotations, the use of
// static-inline functions allows the annotations to be read at compile-time,
// while still letting the compiler elide the functions from the final build.
//
// TODO(delesley) -- The exclusive lock here ignores writes as well, but
// allows IGNORE_READS_AND_WRITES to work properly.
#define ANNOTATE_IGNORE_READS_BEGIN() \
ABSL_INTERNAL_GLOBAL_SCOPED(AbslInternalAnnotateIgnoreReadsBegin)()
#define ANNOTATE_IGNORE_READS_END() \
ABSL_INTERNAL_GLOBAL_SCOPED(AbslInternalAnnotateIgnoreReadsEnd)()
#else
#define ANNOTATE_IGNORE_READS_BEGIN() // empty
#define ANNOTATE_IGNORE_READS_END() // empty
#endif
// -------------------------------------------------------------------------
// Define IGNORE_WRITES_BEGIN/_END annotations.
#if ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED == 1
// Similar to ANNOTATE_IGNORE_READS_BEGIN, but ignore writes instead.
#define ANNOTATE_IGNORE_WRITES_BEGIN() \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreWritesBegin)(__FILE__, __LINE__)
// Stop ignoring writes.
#define ANNOTATE_IGNORE_WRITES_END() \
ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreWritesEnd)(__FILE__, __LINE__)
#else
#define ANNOTATE_IGNORE_WRITES_BEGIN() // empty
#define ANNOTATE_IGNORE_WRITES_END() // empty
#endif
// -------------------------------------------------------------------------
// Define the ANNOTATE_IGNORE_READS_AND_WRITES_* annotations using the more
// primitive annotations defined above.
//
// Instead of doing
// ANNOTATE_IGNORE_READS_BEGIN();
// ... = x;
// ANNOTATE_IGNORE_READS_END();
// one can use
// ... = ANNOTATE_UNPROTECTED_READ(x);
#if defined(ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED)
// Start ignoring all memory accesses (both reads and writes).
#define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \
do { \
ANNOTATE_IGNORE_READS_BEGIN(); \
ANNOTATE_IGNORE_WRITES_BEGIN(); \
} while (0)
// Stop ignoring both reads and writes.
#define ANNOTATE_IGNORE_READS_AND_WRITES_END() \
do { \
ANNOTATE_IGNORE_WRITES_END(); \
ANNOTATE_IGNORE_READS_END(); \
} while (0)
#ifdef __cplusplus
// ANNOTATE_UNPROTECTED_READ is the preferred way to annotate racey reads.
#define ANNOTATE_UNPROTECTED_READ(x) \
absl::base_internal::AnnotateUnprotectedRead(x)
#endif
#else
#define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() // empty
#define ANNOTATE_IGNORE_READS_AND_WRITES_END() // empty
#define ANNOTATE_UNPROTECTED_READ(x) (x)
#endif
// -------------------------------------------------------------------------
// Address sanitizer annotations
#ifdef ADDRESS_SANITIZER
// Describe the current state of a contiguous container such as e.g.
// std::vector or std::string. For more details see
// sanitizer/common_interface_defs.h, which is provided by the compiler.
#include <sanitizer/common_interface_defs.h>
#define ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) \
__sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid)
#define ADDRESS_SANITIZER_REDZONE(name) \
struct { \
char x[8] __attribute__((aligned(8))); \
} name
#else
#define ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid)
#define ADDRESS_SANITIZER_REDZONE(name) static_assert(true, "")
#endif // ADDRESS_SANITIZER
// -------------------------------------------------------------------------
// Undefine the macros intended only for this file.
#undef ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED
#undef ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED
#undef ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED
#undef ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED
#undef ABSL_INTERNAL_ANNOTALYSIS_ENABLED
#undef ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED
#undef ABSL_INTERNAL_BEGIN_EXTERN_C
#undef ABSL_INTERNAL_END_EXTERN_C
#undef ABSL_INTERNAL_STATIC_INLINE
#endif // ABSL_BASE_INTERNAL_DYNAMIC_ANNOTATIONS_H_

View file

@ -0,0 +1,132 @@
// Copyright 2020 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
//
// https://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/optimization.h"
#include "gtest/gtest.h"
#include "absl/types/optional.h"
namespace {
// Tests for the ABSL_PREDICT_TRUE and ABSL_PREDICT_FALSE macros.
// The tests only verify that the macros are functionally correct - i.e. code
// behaves as if they weren't used. They don't try to check their impact on
// optimization.
TEST(PredictTest, PredictTrue) {
EXPECT_TRUE(ABSL_PREDICT_TRUE(true));
EXPECT_FALSE(ABSL_PREDICT_TRUE(false));
EXPECT_TRUE(ABSL_PREDICT_TRUE(1 == 1));
EXPECT_FALSE(ABSL_PREDICT_TRUE(1 == 2));
if (ABSL_PREDICT_TRUE(false)) ADD_FAILURE();
if (!ABSL_PREDICT_TRUE(true)) ADD_FAILURE();
EXPECT_TRUE(ABSL_PREDICT_TRUE(true) && true);
EXPECT_TRUE(ABSL_PREDICT_TRUE(true) || false);
}
TEST(PredictTest, PredictFalse) {
EXPECT_TRUE(ABSL_PREDICT_FALSE(true));
EXPECT_FALSE(ABSL_PREDICT_FALSE(false));
EXPECT_TRUE(ABSL_PREDICT_FALSE(1 == 1));
EXPECT_FALSE(ABSL_PREDICT_FALSE(1 == 2));
if (ABSL_PREDICT_FALSE(false)) ADD_FAILURE();
if (!ABSL_PREDICT_FALSE(true)) ADD_FAILURE();
EXPECT_TRUE(ABSL_PREDICT_FALSE(true) && true);
EXPECT_TRUE(ABSL_PREDICT_FALSE(true) || false);
}
TEST(PredictTest, OneEvaluation) {
// Verify that the expression is only evaluated once.
int x = 0;
if (ABSL_PREDICT_TRUE((++x) == 0)) ADD_FAILURE();
EXPECT_EQ(x, 1);
if (ABSL_PREDICT_FALSE((++x) == 0)) ADD_FAILURE();
EXPECT_EQ(x, 2);
}
TEST(PredictTest, OperatorOrder) {
// Verify that operator order inside and outside the macro behaves well.
// These would fail for a naive '#define ABSL_PREDICT_TRUE(x) x'
EXPECT_TRUE(ABSL_PREDICT_TRUE(1 && 2) == true);
EXPECT_TRUE(ABSL_PREDICT_FALSE(1 && 2) == true);
EXPECT_TRUE(!ABSL_PREDICT_TRUE(1 == 2));
EXPECT_TRUE(!ABSL_PREDICT_FALSE(1 == 2));
}
TEST(PredictTest, Pointer) {
const int x = 3;
const int *good_intptr = &x;
const int *null_intptr = nullptr;
EXPECT_TRUE(ABSL_PREDICT_TRUE(good_intptr));
EXPECT_FALSE(ABSL_PREDICT_TRUE(null_intptr));
// The following doesn't compile:
// EXPECT_TRUE(ABSL_PREDICT_FALSE(good_intptr));
// EXPECT_FALSE(ABSL_PREDICT_FALSE(null_intptr));
}
TEST(PredictTest, Optional) {
// Note: An optional's truth value is the value's existence, not its truth.
absl::optional<bool> has_value(false);
absl::optional<bool> no_value;
EXPECT_TRUE(ABSL_PREDICT_TRUE(has_value));
EXPECT_FALSE(ABSL_PREDICT_TRUE(no_value));
// The following doesn't compile:
// EXPECT_TRUE(ABSL_PREDICT_FALSE(has_value));
// EXPECT_FALSE(ABSL_PREDICT_FALSE(no_value));
}
class ImplictlyConvertibleToBool {
public:
explicit ImplictlyConvertibleToBool(bool value) : value_(value) {}
operator bool() const { // NOLINT(google-explicit-constructor)
return value_;
}
private:
bool value_;
};
TEST(PredictTest, ImplicitBoolConversion) {
const ImplictlyConvertibleToBool is_true(true);
const ImplictlyConvertibleToBool is_false(false);
if (!ABSL_PREDICT_TRUE(is_true)) ADD_FAILURE();
if (ABSL_PREDICT_TRUE(is_false)) ADD_FAILURE();
if (!ABSL_PREDICT_FALSE(is_true)) ADD_FAILURE();
if (ABSL_PREDICT_FALSE(is_false)) ADD_FAILURE();
}
class ExplictlyConvertibleToBool {
public:
explicit ExplictlyConvertibleToBool(bool value) : value_(value) {}
explicit operator bool() const { return value_; }
private:
bool value_;
};
TEST(PredictTest, ExplicitBoolConversion) {
const ExplictlyConvertibleToBool is_true(true);
const ExplictlyConvertibleToBool is_false(false);
if (!ABSL_PREDICT_TRUE(is_true)) ADD_FAILURE();
if (ABSL_PREDICT_TRUE(is_false)) ADD_FAILURE();
// The following doesn't compile:
// if (!ABSL_PREDICT_FALSE(is_true)) ADD_FAILURE();
// if (ABSL_PREDICT_FALSE(is_false)) ADD_FAILURE();
}
} // namespace

View file

@ -817,33 +817,52 @@ class btree_node {
absl::container_internal::SanitizerPoisonObject(slot(i)); absl::container_internal::SanitizerPoisonObject(slot(i));
} }
// Transfers value from slot `src_i` in `src` to slot `dest_i` in `this`. // Transfers value from slot `src_i` in `src_node` to slot `dest_i` in `this`.
void transfer(const size_type dest_i, const size_type src_i, btree_node *src, void transfer(const size_type dest_i, const size_type src_i,
allocator_type *alloc) { btree_node *src_node, allocator_type *alloc) {
absl::container_internal::SanitizerUnpoisonObject(slot(dest_i)); absl::container_internal::SanitizerUnpoisonObject(slot(dest_i));
params_type::transfer(alloc, slot(dest_i), src->slot(src_i)); params_type::transfer(alloc, slot(dest_i), src_node->slot(src_i));
absl::container_internal::SanitizerPoisonObject(src->slot(src_i)); absl::container_internal::SanitizerPoisonObject(src_node->slot(src_i));
} }
// Move n values starting at value i in this node into the values starting at // Transfers `n` values starting at value `src_i` in `src_node` into the
// value j in dest_node. // values starting at value `dest_i` in `this`.
void uninitialized_move_n(const size_type n, const size_type i, void transfer_n(const size_type n, const size_type dest_i,
const size_type j, btree_node *dest_node, const size_type src_i, btree_node *src_node,
allocator_type *alloc) { allocator_type *alloc) {
absl::container_internal::SanitizerUnpoisonMemoryRegion( absl::container_internal::SanitizerUnpoisonMemoryRegion(
dest_node->slot(j), n * sizeof(slot_type)); slot(dest_i), n * sizeof(slot_type));
for (slot_type *src = slot(i), *end = src + n, *dest = dest_node->slot(j); for (slot_type *src = src_node->slot(src_i), *end = src + n,
*dest = slot(dest_i);
src != end; ++src, ++dest) { src != end; ++src, ++dest) {
params_type::construct(alloc, dest, src); params_type::transfer(alloc, dest, src);
} }
// We take care to avoid poisoning transferred-to nodes in case of overlap.
const size_type overlap =
this == src_node ? (std::max)(src_i, dest_i + n) - src_i : 0;
assert(n >= overlap);
absl::container_internal::SanitizerPoisonMemoryRegion(
src_node->slot(src_i + overlap), (n - overlap) * sizeof(slot_type));
} }
// Destroys a range of n values, starting at index i. // Same as above, except that we start at the end and work our way to the
void value_destroy_n(const size_type i, const size_type n, // beginning.
allocator_type *alloc) { void transfer_n_backward(const size_type n, const size_type dest_i,
for (int j = 0; j < n; ++j) { const size_type src_i, btree_node *src_node,
value_destroy(i + j, alloc); allocator_type *alloc) {
absl::container_internal::SanitizerUnpoisonMemoryRegion(
slot(dest_i), n * sizeof(slot_type));
for (slot_type *src = src_node->slot(src_i + n - 1), *end = src - n,
*dest = slot(dest_i + n - 1);
src != end; --src, --dest) {
params_type::transfer(alloc, dest, src);
} }
// We take care to avoid poisoning transferred-to nodes in case of overlap.
assert(this != src_node || dest_i >= src_i);
const size_type num_to_poison =
this == src_node ? (std::min)(n, dest_i - src_i) : n;
absl::container_internal::SanitizerPoisonMemoryRegion(
src_node->slot(src_i), num_to_poison * sizeof(slot_type));
} }
template <typename P> template <typename P>
@ -1531,10 +1550,8 @@ inline void btree_node<P>::emplace_value(const size_type i,
// Shift old values to create space for new value and then construct it in // Shift old values to create space for new value and then construct it in
// place. // place.
if (i < finish()) { if (i < finish()) {
value_init(finish(), alloc, slot(finish() - 1)); transfer_n_backward(finish() - i, /*dest_i=*/i + 1, /*src_i=*/i, this,
for (size_type j = finish() - 1; j > i; --j) alloc);
params_type::move(alloc, slot(j - 1), slot(j));
value_destroy(i, alloc);
} }
value_init(i, alloc, std::forward<Args>(args)...); value_init(i, alloc, std::forward<Args>(args)...);
set_finish(finish() + 1); set_finish(finish() + 1);
@ -1564,7 +1581,9 @@ template <typename P>
inline void btree_node<P>::remove_values_ignore_children( inline void btree_node<P>::remove_values_ignore_children(
const int i, const int to_erase, allocator_type *alloc) { const int i, const int to_erase, allocator_type *alloc) {
params_type::move(alloc, slot(i + to_erase), finish_slot(), slot(i)); params_type::move(alloc, slot(i + to_erase), finish_slot(), slot(i));
value_destroy_n(finish() - to_erase, to_erase, alloc); for (int j = finish() - to_erase; j < finish(); ++j) {
value_destroy(j, alloc);
}
set_finish(finish() - to_erase); set_finish(finish() - to_erase);
} }
@ -1579,22 +1598,17 @@ void btree_node<P>::rebalance_right_to_left(const int to_move,
assert(to_move <= right->count()); assert(to_move <= right->count());
// 1) Move the delimiting value in the parent to the left node. // 1) Move the delimiting value in the parent to the left node.
value_init(finish(), alloc, parent()->slot(position())); transfer(finish(), position(), parent(), alloc);
// 2) Move the (to_move - 1) values from the right node to the left node. // 2) Move the (to_move - 1) values from the right node to the left node.
right->uninitialized_move_n(to_move - 1, right->start(), finish() + 1, this, transfer_n(to_move - 1, finish() + 1, right->start(), right, alloc);
alloc);
// 3) Move the new delimiting value to the parent from the right node. // 3) Move the new delimiting value to the parent from the right node.
params_type::move(alloc, right->slot(to_move - 1), parent()->transfer(position(), right->start() + to_move - 1, right, alloc);
parent()->slot(position()));
// 4) Shift the values in the right node to their correct position. // 4) Shift the values in the right node to their correct positions.
params_type::move(alloc, right->slot(to_move), right->finish_slot(), right->transfer_n(right->count() - to_move, right->start(),
right->start_slot()); right->start() + to_move, right, alloc);
// 5) Destroy the now-empty to_move entries in the right node.
right->value_destroy_n(right->finish() - to_move, to_move, alloc);
if (!leaf()) { if (!leaf()) {
// Move the child pointers from the right to the left node. // Move the child pointers from the right to the left node.
@ -1629,54 +1643,19 @@ void btree_node<P>::rebalance_left_to_right(const int to_move,
// Lastly, a new delimiting value is moved from the left node into the // Lastly, a new delimiting value is moved from the left node into the
// parent, and the remaining empty left node entries are destroyed. // parent, and the remaining empty left node entries are destroyed.
if (right->count() >= to_move) { // 1) Shift existing values in the right node to their correct positions.
// The original location of the right->count() values are sufficient to hold right->transfer_n_backward(right->count(), right->start() + to_move,
// the new to_move entries from the parent and left node. right->start(), right, alloc);
// 1) Shift existing values in the right node to their correct positions. // 2) Move the delimiting value in the parent to the right node.
right->uninitialized_move_n(to_move, right->finish() - to_move, right->transfer(right->start() + to_move - 1, position(), parent(), alloc);
right->finish(), right, alloc);
for (slot_type *src = right->slot(right->finish() - to_move - 1),
*dest = right->slot(right->finish() - 1),
*end = right->start_slot();
src >= end; --src, --dest) {
params_type::move(alloc, src, dest);
}
// 2) Move the delimiting value in the parent to the right node. // 3) Move the (to_move - 1) values from the left node to the right node.
params_type::move(alloc, parent()->slot(position()), right->transfer_n(to_move - 1, right->start(), finish() - (to_move - 1), this,
right->slot(to_move - 1)); alloc);
// 3) Move the (to_move - 1) values from the left node to the right node.
params_type::move(alloc, slot(finish() - (to_move - 1)), finish_slot(),
right->start_slot());
} else {
// The right node does not have enough initialized space to hold the new
// to_move entries, so part of them will move to uninitialized space.
// 1) Shift existing values in the right node to their correct positions.
right->uninitialized_move_n(right->count(), right->start(),
right->start() + to_move, right, alloc);
// 2) Move the delimiting value in the parent to the right node.
right->value_init(to_move - 1, alloc, parent()->slot(position()));
// 3) Move the (to_move - 1) values from the left node to the right node.
const size_type uninitialized_remaining = to_move - right->count() - 1;
uninitialized_move_n(uninitialized_remaining,
finish() - uninitialized_remaining, right->finish(),
right, alloc);
params_type::move(alloc, slot(finish() - (to_move - 1)),
slot(finish() - uninitialized_remaining),
right->start_slot());
}
// 4) Move the new delimiting value to the parent from the left node. // 4) Move the new delimiting value to the parent from the left node.
params_type::move(alloc, slot(finish() - to_move), parent()->transfer(position(), finish() - to_move, this, alloc);
parent()->slot(position()));
// 5) Destroy the now-empty to_move entries in the left node.
value_destroy_n(finish() - to_move, to_move, alloc);
if (!leaf()) { if (!leaf()) {
// Move the child pointers from the left to the right node. // Move the child pointers from the left to the right node.
@ -1716,10 +1695,7 @@ void btree_node<P>::split(const int insert_position, btree_node *dest,
assert(count() >= 1); assert(count() >= 1);
// Move values from the left sibling to the right sibling. // Move values from the left sibling to the right sibling.
uninitialized_move_n(dest->count(), finish(), dest->start(), dest, alloc); dest->transfer_n(dest->count(), dest->start(), finish(), this, alloc);
// Destroy the now-empty entries in the left node.
value_destroy_n(finish(), dest->count(), alloc);
// The split key is the largest value in the left sibling. // The split key is the largest value in the left sibling.
--mutable_finish(); --mutable_finish();
@ -1746,11 +1722,7 @@ void btree_node<P>::merge(btree_node *src, allocator_type *alloc) {
value_init(finish(), alloc, parent()->slot(position())); value_init(finish(), alloc, parent()->slot(position()));
// Move the values from the right to the left node. // Move the values from the right to the left node.
src->uninitialized_move_n(src->count(), src->start(), finish() + 1, this, transfer_n(src->count(), finish() + 1, src->start(), src, alloc);
alloc);
// Destroy the now-empty entries in the right node.
src->value_destroy_n(src->start(), src->count(), alloc);
if (!leaf()) { if (!leaf()) {
// Move the child pointers from the right to the left node. // Move the child pointers from the right to the left node.
@ -2474,9 +2446,8 @@ inline auto btree<P>::internal_emplace(iterator iter, Args &&... args)
// Transfer the values from the old root to the new root. // Transfer the values from the old root to the new root.
node_type *old_root = root(); node_type *old_root = root();
node_type *new_root = iter.node; node_type *new_root = iter.node;
for (int i = old_root->start(), f = old_root->finish(); i < f; ++i) { new_root->transfer_n(old_root->count(), new_root->start(),
new_root->transfer(i, i, old_root, alloc); old_root->start(), old_root, alloc);
}
new_root->set_finish(old_root->finish()); new_root->set_finish(old_root->finish());
old_root->set_finish(old_root->start()); old_root->set_finish(old_root->start());
delete_leaf_node(old_root); delete_leaf_node(old_root);

View file

@ -286,6 +286,8 @@ class UniquePtrModifiersTest : public ::testing::Test {
} }
}; };
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UniquePtrModifiersTest);
TYPED_TEST_SUITE_P(UniquePtrModifiersTest); TYPED_TEST_SUITE_P(UniquePtrModifiersTest);
// Test that we do not move from rvalue arguments if an insertion does not // Test that we do not move from rvalue arguments if an insertion does not

View file

@ -55,6 +55,7 @@ cc_library(
name = "symbolize", name = "symbolize",
srcs = [ srcs = [
"symbolize.cc", "symbolize.cc",
"symbolize_darwin.inc",
"symbolize_elf.inc", "symbolize_elf.inc",
"symbolize_unimplemented.inc", "symbolize_unimplemented.inc",
"symbolize_win32.inc", "symbolize_win32.inc",
@ -77,6 +78,7 @@ cc_library(
"//absl/base:dynamic_annotations", "//absl/base:dynamic_annotations",
"//absl/base:malloc_internal", "//absl/base:malloc_internal",
"//absl/base:raw_logging_internal", "//absl/base:raw_logging_internal",
"//absl/strings",
], ],
) )
@ -98,6 +100,7 @@ cc_test(
"//absl/base:core_headers", "//absl/base:core_headers",
"//absl/base:raw_logging_internal", "//absl/base:raw_logging_internal",
"//absl/memory", "//absl/memory",
"//absl/strings",
"@com_google_googletest//:gtest", "@com_google_googletest//:gtest",
], ],
) )

View file

@ -46,6 +46,7 @@ absl_cc_library(
"internal/symbolize.h" "internal/symbolize.h"
SRCS SRCS
"symbolize.cc" "symbolize.cc"
"symbolize_darwin.inc"
"symbolize_elf.inc" "symbolize_elf.inc"
"symbolize_unimplemented.inc" "symbolize_unimplemented.inc"
"symbolize_win32.inc" "symbolize_win32.inc"
@ -63,6 +64,7 @@ absl_cc_library(
absl::dynamic_annotations absl::dynamic_annotations
absl::malloc_internal absl::malloc_internal
absl::raw_logging_internal absl::raw_logging_internal
absl::strings
PUBLIC PUBLIC
) )
@ -83,6 +85,7 @@ absl_cc_test(
absl::core_headers absl::core_headers
absl::memory absl::memory
absl::raw_logging_internal absl::raw_logging_internal
absl::strings
gmock gmock
) )

View file

@ -20,6 +20,10 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
#ifdef __APPLE__
#include <sys/ucontext.h>
#endif
#include <csignal> #include <csignal>
#include <cstdio> #include <cstdio>
@ -64,6 +68,32 @@ void* GetProgramCounter(void* vuc) {
return reinterpret_cast<void*>(context->uc_mcontext.gregs[16]); return reinterpret_cast<void*>(context->uc_mcontext.gregs[16]);
#else #else
#error "Undefined Architecture." #error "Undefined Architecture."
#endif
}
#elif defined(__APPLE__)
if (vuc != nullptr) {
ucontext_t* signal_ucontext = reinterpret_cast<ucontext_t*>(vuc);
#if defined(__aarch64__)
return reinterpret_cast<void*>(
__darwin_arm_thread_state64_get_pc(signal_ucontext->uc_mcontext->__ss));
#elif defined(__arm__)
#if __DARWIN_UNIX03
return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->__ss.__pc);
#else
return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->ss.pc);
#endif
#elif defined(__i386__)
#if __DARWIN_UNIX03
return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->__ss.__eip);
#else
return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->ss.eip);
#endif
#elif defined(__x86_64__)
#if __DARWIN_UNIX03
return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->__ss.__rip);
#else
return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->ss.rip);
#endif
#endif #endif
} }
#elif defined(__akaros__) #elif defined(__akaros__)

View file

@ -28,6 +28,27 @@
#define ABSL_STACKTRACE_INL_HEADER \ #define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_win32-inl.inc" "absl/debugging/internal/stacktrace_win32-inl.inc"
#elif defined(__APPLE__)
// Thread local support required for UnwindImpl.
// Notes:
// * Xcode's clang did not support `thread_local` until version 8, and
// even then not for all iOS < 9.0.
// * Xcode 9.3 started disallowing `thread_local` for 32-bit iOS simulator
// targeting iOS 9.x.
// * Xcode 10 moves the deployment target check for iOS < 9.0 to link time
// making __has_feature unreliable there.
//
// Otherwise, `__has_feature` is only supported by Clang so it has be inside
// `defined(__APPLE__)` check.
#if __has_feature(cxx_thread_local) && \
!(TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0)
#define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_generic-inl.inc"
#else
#define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_unimplemented-inl.inc"
#endif
#elif defined(__linux__) && !defined(__ANDROID__) #elif defined(__linux__) && !defined(__ANDROID__)
#if !defined(NO_FRAME_POINTER) #if !defined(NO_FRAME_POINTER)
@ -40,7 +61,7 @@
# elif defined(__aarch64__) # elif defined(__aarch64__)
#define ABSL_STACKTRACE_INL_HEADER \ #define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_aarch64-inl.inc" "absl/debugging/internal/stacktrace_aarch64-inl.inc"
# elif defined(__arm__) #elif defined(__arm__) && defined(__GLIBC__)
// Note: When using glibc this may require -funwind-tables to function properly. // Note: When using glibc this may require -funwind-tables to function properly.
#define ABSL_STACKTRACE_INL_HEADER \ #define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_generic-inl.inc" "absl/debugging/internal/stacktrace_generic-inl.inc"

View file

@ -22,6 +22,7 @@
#include <cstdint> #include <cstdint>
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/strings/string_view.h"
#ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE #ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE
#error ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE cannot be directly set #error ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE cannot be directly set
@ -45,7 +46,7 @@ namespace debugging_internal {
// //
// This is not async-signal-safe. // This is not async-signal-safe.
bool ForEachSection(int fd, bool ForEachSection(int fd,
const std::function<bool(const std::string& name, const std::function<bool(absl::string_view name,
const ElfW(Shdr) &)>& callback); const ElfW(Shdr) &)>& callback);
// Gets the section header for the given name, if it exists. Returns true on // Gets the section header for the given name, if it exists. Returns true on
@ -59,6 +60,12 @@ ABSL_NAMESPACE_END
#endif // ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE #endif // ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE
#ifdef ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE
#error ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE cannot be directly set
#elif defined(__APPLE__)
#define ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE 1
#endif
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
namespace debugging_internal { namespace debugging_internal {

View file

@ -20,6 +20,8 @@
// The Windows Symbolizer only works if PDB files containing the debug info // The Windows Symbolizer only works if PDB files containing the debug info
// are available to the program at runtime. // are available to the program at runtime.
#include "absl/debugging/symbolize_win32.inc" #include "absl/debugging/symbolize_win32.inc"
#elif defined(__APPLE__)
#include "absl/debugging/symbolize_darwin.inc"
#else #else
#include "absl/debugging/symbolize_unimplemented.inc" #include "absl/debugging/symbolize_unimplemented.inc"
#endif #endif

View file

@ -0,0 +1,101 @@
// Copyright 2020 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
//
// https://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 <cxxabi.h>
#include <execinfo.h>
#include <algorithm>
#include <cstring>
#include "absl/base/internal/raw_logging.h"
#include "absl/debugging/internal/demangle.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
void InitializeSymbolizer(const char*) {}
namespace debugging_internal {
namespace {
static std::string GetSymbolString(absl::string_view backtrace_line) {
// Example Backtrace lines:
// 0 libimaging_shared.dylib 0x018c152a
// _ZNSt11_Deque_baseIN3nik7mediadb4PageESaIS2_EE17_M_initialize_mapEm + 3478
//
// or
// 0 libimaging_shared.dylib 0x0000000001895c39
// _ZN3nik4util19register_shared_ptrINS_3gpu7TextureEEEvPKvS5_ + 39
//
// or
// 0 mysterious_app 0x0124000120120009 main + 17
auto address_pos = backtrace_line.find(" 0x");
if (address_pos == absl::string_view::npos) return std::string();
absl::string_view symbol_view = backtrace_line.substr(address_pos + 1);
auto space_pos = symbol_view.find(" ");
if (space_pos == absl::string_view::npos) return std::string();
symbol_view = symbol_view.substr(space_pos + 1); // to mangled symbol
auto plus_pos = symbol_view.find(" + ");
if (plus_pos == absl::string_view::npos) return std::string();
symbol_view = symbol_view.substr(0, plus_pos); // strip remainng
return std::string(symbol_view);
}
} // namespace
} // namespace debugging_internal
bool Symbolize(const void* pc, char* out, int out_size) {
if (out_size <= 0 || pc == nullptr) {
out = nullptr;
return false;
}
// This allocates a char* array.
char** frame_strings = backtrace_symbols(const_cast<void**>(&pc), 1);
if (frame_strings == nullptr) return false;
std::string symbol = debugging_internal::GetSymbolString(frame_strings[0]);
free(frame_strings);
char tmp_buf[1024];
if (debugging_internal::Demangle(symbol.c_str(), tmp_buf, sizeof(tmp_buf))) {
int len = strlen(tmp_buf);
if (len + 1 <= out_size) { // +1 for '\0'
assert(len < sizeof(tmp_buf));
memmove(out, tmp_buf, len + 1);
}
} else {
strncpy(out, symbol.c_str(), out_size);
}
if (out[out_size - 1] != '\0') {
// strncpy() does not '\0' terminate when it truncates.
static constexpr char kEllipsis[] = "...";
int ellipsis_size = std::min<int>(sizeof(kEllipsis) - 1, out_size - 1);
memcpy(out + out_size - ellipsis_size - 1, kEllipsis, ellipsis_size);
out[out_size - 1] = '\0';
}
return true;
}
ABSL_NAMESPACE_END
} // namespace absl

View file

@ -74,6 +74,7 @@
#include "absl/base/port.h" #include "absl/base/port.h"
#include "absl/debugging/internal/demangle.h" #include "absl/debugging/internal/demangle.h"
#include "absl/debugging/internal/vdso_support.h" #include "absl/debugging/internal/vdso_support.h"
#include "absl/strings/string_view.h"
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
@ -498,7 +499,7 @@ static ABSL_ATTRIBUTE_NOINLINE bool GetSectionHeaderByType(
const int kMaxSectionNameLen = 64; const int kMaxSectionNameLen = 64;
bool ForEachSection(int fd, bool ForEachSection(int fd,
const std::function<bool(const std::string &name, const std::function<bool(absl::string_view name,
const ElfW(Shdr) &)> &callback) { const ElfW(Shdr) &)> &callback) {
ElfW(Ehdr) elf_header; ElfW(Ehdr) elf_header;
if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) { if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) {
@ -520,7 +521,7 @@ bool ForEachSection(int fd,
return false; return false;
} }
off_t name_offset = shstrtab.sh_offset + out.sh_name; off_t name_offset = shstrtab.sh_offset + out.sh_name;
char header_name[kMaxSectionNameLen + 1]; char header_name[kMaxSectionNameLen];
ssize_t n_read = ssize_t n_read =
ReadFromOffset(fd, &header_name, kMaxSectionNameLen, name_offset); ReadFromOffset(fd, &header_name, kMaxSectionNameLen, name_offset);
if (n_read == -1) { if (n_read == -1) {
@ -529,9 +530,8 @@ bool ForEachSection(int fd,
// Long read? // Long read?
return false; return false;
} }
header_name[n_read] = '\0';
std::string name(header_name); absl::string_view name(header_name, strnlen(header_name, n_read));
if (!callback(name, out)) { if (!callback(name, out)) {
break; break;
} }

View file

@ -32,6 +32,7 @@
#include "absl/base/optimization.h" #include "absl/base/optimization.h"
#include "absl/debugging/internal/stack_consumption.h" #include "absl/debugging/internal/stack_consumption.h"
#include "absl/memory/memory.h" #include "absl/memory/memory.h"
#include "absl/strings/string_view.h"
using testing::Contains; using testing::Contains;
@ -144,7 +145,8 @@ static const char *TrySymbolize(void *pc) {
return TrySymbolizeWithLimit(pc, sizeof(try_symbolize_buffer)); return TrySymbolizeWithLimit(pc, sizeof(try_symbolize_buffer));
} }
#ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE #if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE) || \
defined(ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE)
TEST(Symbolize, Cached) { TEST(Symbolize, Cached) {
// Compilers should give us pointers to them. // Compilers should give us pointers to them.
@ -258,6 +260,7 @@ TEST(Symbolize, SymbolizeWithDemanglingStackConsumption) {
#endif // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION #endif // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
#ifndef ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE
// Use a 64K page size for PPC. // Use a 64K page size for PPC.
const size_t kPageSize = 64 << 10; const size_t kPageSize = 64 << 10;
// We place a read-only symbols into the .text section and verify that we can // We place a read-only symbols into the .text section and verify that we can
@ -399,8 +402,8 @@ TEST(Symbolize, ForEachSection) {
std::vector<std::string> sections; std::vector<std::string> sections;
ASSERT_TRUE(absl::debugging_internal::ForEachSection( ASSERT_TRUE(absl::debugging_internal::ForEachSection(
fd, [&sections](const std::string &name, const ElfW(Shdr) &) { fd, [&sections](const absl::string_view name, const ElfW(Shdr) &) {
sections.push_back(name); sections.emplace_back(name);
return true; return true;
})); }));
@ -413,6 +416,7 @@ TEST(Symbolize, ForEachSection) {
close(fd); close(fd);
} }
#endif // !ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE
// x86 specific tests. Uses some inline assembler. // x86 specific tests. Uses some inline assembler.
extern "C" { extern "C" {
@ -541,7 +545,8 @@ int main(int argc, char **argv) {
absl::InitializeSymbolizer(argv[0]); absl::InitializeSymbolizer(argv[0]);
testing::InitGoogleTest(&argc, argv); testing::InitGoogleTest(&argc, argv);
#ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE #if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE) || \
defined(ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE)
TestWithPCInsideInlineFunction(); TestWithPCInsideInlineFunction();
TestWithPCInsideNonInlineFunction(); TestWithPCInsideNonInlineFunction();
TestWithReturnAddress(); TestWithReturnAddress();

View file

@ -27,28 +27,18 @@ package(default_visibility = ["//visibility:public"])
licenses(["notice"]) # Apache 2.0 licenses(["notice"]) # Apache 2.0
cc_library( cc_library(
name = "flag_internal", name = "path_util",
srcs = [
"internal/flag.cc",
],
hdrs = [ hdrs = [
"internal/flag.h", "internal/path_util.h",
], ],
copts = ABSL_DEFAULT_COPTS, copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS, linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = ["//absl/base:__subpackages__"], visibility = [
"//absl/flags:__pkg__",
],
deps = [ deps = [
":config",
":handle",
":marshalling",
":registry",
"//absl/base",
"//absl/base:config", "//absl/base:config",
"//absl/base:core_headers",
"//absl/memory",
"//absl/meta:type_traits",
"//absl/strings", "//absl/strings",
"//absl/synchronization",
], ],
) )
@ -74,22 +64,6 @@ cc_library(
], ],
) )
cc_library(
name = "path_util",
hdrs = [
"internal/path_util.h",
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = [
"//absl/flags:__pkg__",
],
deps = [
"//absl/base:config",
"//absl/strings",
],
)
cc_library( cc_library(
name = "config", name = "config",
srcs = [ srcs = [
@ -131,21 +105,32 @@ cc_library(
) )
cc_library( cc_library(
name = "handle", name = "commandlineflag_internal",
srcs = [
"internal/commandlineflag.cc",
],
hdrs = [ hdrs = [
"internal/commandlineflag.h", "internal/commandlineflag.h",
], ],
copts = ABSL_DEFAULT_COPTS, copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS, linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = [ visibility = ["//visibility:private"],
"//absl/flags:__pkg__",
],
deps = [ deps = [
"//absl/base:config", "//absl/base:config",
"//absl/base:core_headers", "//absl/base:fast_type_id",
],
)
cc_library(
name = "commandlineflag",
srcs = [
"commandlineflag.cc",
],
hdrs = [
"commandlineflag.h",
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":commandlineflag_internal",
"//absl/base:config",
"//absl/base:fast_type_id", "//absl/base:fast_type_id",
"//absl/strings", "//absl/strings",
"//absl/types:optional", "//absl/types:optional",
@ -165,36 +150,65 @@ cc_library(
visibility = [ visibility = [
"//absl/flags:__pkg__", "//absl/flags:__pkg__",
], ],
deps = [":handle"], deps = [
":commandlineflag",
":commandlineflag_internal",
"//absl/base:config",
"//absl/strings",
],
) )
cc_library( cc_library(
name = "registry", name = "reflection",
srcs = [ srcs = [
"internal/registry.cc", "reflection.cc",
"internal/type_erased.cc",
], ],
hdrs = [ hdrs = [
"internal/registry.h", "internal/registry.h",
"internal/type_erased.h", "reflection.h",
], ],
copts = ABSL_DEFAULT_COPTS, copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS, linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = [
"//absl/flags:__pkg__",
],
deps = [ deps = [
":commandlineflag",
":commandlineflag_internal",
":config", ":config",
":handle",
":private_handle_accessor", ":private_handle_accessor",
"//absl/base:config", "//absl/base:config",
"//absl/base:core_headers", "//absl/base:core_headers",
"//absl/base:raw_logging_internal",
"//absl/strings", "//absl/strings",
"//absl/synchronization", "//absl/synchronization",
], ],
) )
cc_library(
name = "flag_internal",
srcs = [
"internal/flag.cc",
],
hdrs = [
"internal/flag.h",
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = ["//absl/base:__subpackages__"],
deps = [
":commandlineflag",
":commandlineflag_internal",
":config",
":marshalling",
":reflection",
"//absl/base",
"//absl/base:config",
"//absl/base:core_headers",
"//absl/memory",
"//absl/meta:type_traits",
"//absl/strings",
"//absl/synchronization",
"//absl/utility",
],
)
cc_library( cc_library(
name = "flag", name = "flag",
srcs = [ srcs = [
@ -209,9 +223,7 @@ cc_library(
deps = [ deps = [
":config", ":config",
":flag_internal", ":flag_internal",
":handle", ":reflection",
":marshalling",
":registry",
"//absl/base", "//absl/base",
"//absl/base:config", "//absl/base:config",
"//absl/base:core_headers", "//absl/base:core_headers",
@ -233,14 +245,14 @@ cc_library(
"//absl/flags:__pkg__", "//absl/flags:__pkg__",
], ],
deps = [ deps = [
":commandlineflag",
":config", ":config",
":flag", ":flag",
":flag_internal", ":flag_internal",
":handle",
":path_util", ":path_util",
":private_handle_accessor", ":private_handle_accessor",
":program_name", ":program_name",
":registry", ":reflection",
"//absl/base:config", "//absl/base:config",
"//absl/base:core_headers", "//absl/base:core_headers",
"//absl/strings", "//absl/strings",
@ -276,13 +288,14 @@ cc_library(
copts = ABSL_DEFAULT_COPTS, copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS, linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [ deps = [
":commandlineflag",
":commandlineflag_internal",
":config", ":config",
":flag", ":flag",
":flag_internal", ":flag_internal",
":handle",
":private_handle_accessor", ":private_handle_accessor",
":program_name", ":program_name",
":registry", ":reflection",
":usage", ":usage",
":usage_internal", ":usage_internal",
"//absl/base:config", "//absl/base:config",
@ -299,16 +312,17 @@ cc_test(
name = "commandlineflag_test", name = "commandlineflag_test",
size = "small", size = "small",
srcs = [ srcs = [
"internal/commandlineflag_test.cc", "commandlineflag_test.cc",
], ],
copts = ABSL_TEST_COPTS, copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS, linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [ deps = [
":commandlineflag",
":commandlineflag_internal",
":config", ":config",
":flag", ":flag",
":handle",
":private_handle_accessor", ":private_handle_accessor",
":registry", ":reflection",
"//absl/memory", "//absl/memory",
"//absl/strings", "//absl/strings",
"@com_google_googletest//:gtest_main", "@com_google_googletest//:gtest_main",
@ -342,8 +356,8 @@ cc_test(
":config", ":config",
":flag", ":flag",
":flag_internal", ":flag_internal",
":handle", ":marshalling",
":registry", ":reflection",
"//absl/base:core_headers", "//absl/base:core_headers",
"//absl/base:malloc_internal", "//absl/base:malloc_internal",
"//absl/strings", "//absl/strings",
@ -363,6 +377,8 @@ cc_binary(
visibility = ["//visibility:private"], visibility = ["//visibility:private"],
deps = [ deps = [
":flag", ":flag",
":marshalling",
"//absl/strings",
"//absl/time", "//absl/time",
"//absl/types:optional", "//absl/types:optional",
"@com_github_google_benchmark//:benchmark_main", "@com_github_google_benchmark//:benchmark_main",
@ -383,20 +399,6 @@ cc_test(
], ],
) )
cc_test(
name = "path_util_test",
size = "small",
srcs = [
"internal/path_util_test.cc",
],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":path_util",
"@com_google_googletest//:gtest_main",
],
)
cc_test( cc_test(
name = "parse_test", name = "parse_test",
size = "small", size = "small",
@ -408,7 +410,7 @@ cc_test(
deps = [ deps = [
":flag", ":flag",
":parse", ":parse",
":registry", ":reflection",
"//absl/base:raw_logging_internal", "//absl/base:raw_logging_internal",
"//absl/base:scoped_set_env", "//absl/base:scoped_set_env",
"//absl/strings", "//absl/strings",
@ -417,6 +419,20 @@ cc_test(
], ],
) )
cc_test(
name = "path_util_test",
size = "small",
srcs = [
"internal/path_util_test.cc",
],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":path_util",
"@com_google_googletest//:gtest_main",
],
)
cc_test( cc_test(
name = "program_name_test", name = "program_name_test",
size = "small", size = "small",
@ -433,18 +449,18 @@ cc_test(
) )
cc_test( cc_test(
name = "type_erased_test", name = "reflection_test",
size = "small", size = "small",
srcs = [ srcs = [
"internal/type_erased_test.cc", "reflection_test.cc",
], ],
copts = ABSL_TEST_COPTS, copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS, linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [ deps = [
":commandlineflag_internal",
":flag", ":flag",
":handle",
":marshalling", ":marshalling",
":registry", ":reflection",
"//absl/memory", "//absl/memory",
"@com_google_googletest//:gtest_main", "@com_google_googletest//:gtest_main",
], ],
@ -481,10 +497,9 @@ cc_test(
":parse", ":parse",
":path_util", ":path_util",
":program_name", ":program_name",
":registry", ":reflection",
":usage", ":usage",
":usage_internal", ":usage_internal",
"//absl/memory",
"//absl/strings", "//absl/strings",
"@com_google_googletest//:gtest", "@com_google_googletest//:gtest",
], ],

View file

@ -17,24 +17,16 @@
# Internal-only target, do not depend on directly. # Internal-only target, do not depend on directly.
absl_cc_library( absl_cc_library(
NAME NAME
flags_internal flags_path_util
SRCS
"internal/flag.cc"
HDRS HDRS
"internal/flag.h" "internal/path_util.h"
COPTS COPTS
${ABSL_DEFAULT_COPTS} ${ABSL_DEFAULT_COPTS}
LINKOPTS LINKOPTS
${ABSL_DEFAULT_LINKOPTS} ${ABSL_DEFAULT_LINKOPTS}
DEPS DEPS
absl::base
absl::config absl::config
absl::flags_config absl::strings
absl::flags_handle
absl::flags_marshalling
absl::flags_registry
absl::synchronization
absl::meta
PUBLIC PUBLIC
) )
@ -59,22 +51,6 @@ absl_cc_library(
PUBLIC PUBLIC
) )
# Internal-only target, do not depend on directly.
absl_cc_library(
NAME
flags_path_util
HDRS
"internal/path_util.h"
COPTS
${ABSL_DEFAULT_COPTS}
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
absl::config
absl::strings
PUBLIC
)
absl_cc_library( absl_cc_library(
NAME NAME
flags_config flags_config
@ -118,9 +94,7 @@ absl_cc_library(
# Internal-only target, do not depend on directly. # Internal-only target, do not depend on directly.
absl_cc_library( absl_cc_library(
NAME NAME
flags_handle flags_commandlineflag_internal
SRCS
"internal/commandlineflag.cc"
HDRS HDRS
"internal/commandlineflag.h" "internal/commandlineflag.h"
COPTS COPTS
@ -130,11 +104,25 @@ absl_cc_library(
DEPS DEPS
absl::config absl::config
absl::fast_type_id absl::fast_type_id
absl::core_headers )
absl_cc_library(
NAME
flags_commandlineflag
SRCS
"commandlineflag.cc"
HDRS
"commandlineflag.h"
COPTS
${ABSL_DEFAULT_COPTS}
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
absl::config
absl::fast_type_id
absl::flags_commandlineflag_internal
absl::optional absl::optional
absl::raw_logging_internal
absl::strings absl::strings
absl::synchronization
) )
# Internal-only target, do not depend on directly. # Internal-only target, do not depend on directly.
@ -150,34 +138,57 @@ absl_cc_library(
LINKOPTS LINKOPTS
${ABSL_DEFAULT_LINKOPTS} ${ABSL_DEFAULT_LINKOPTS}
DEPS DEPS
absl::flags_handle absl::config
absl::flags_commandlineflag
absl::flags_commandlineflag_internal
absl::strings
) )
# Internal-only target, do not depend on directly.
absl_cc_library( absl_cc_library(
NAME NAME
flags_registry flags_reflection
SRCS SRCS
"internal/registry.cc" "reflection.cc"
"internal/type_erased.cc"
HDRS HDRS
"reflection.h"
"internal/registry.h" "internal/registry.h"
"internal/type_erased.h"
COPTS COPTS
${ABSL_DEFAULT_COPTS} ${ABSL_DEFAULT_COPTS}
LINKOPTS LINKOPTS
${ABSL_DEFAULT_LINKOPTS} ${ABSL_DEFAULT_LINKOPTS}
DEPS DEPS
absl::config absl::config
absl::flags_config absl::flags_commandlineflag
absl::flags_handle
absl::flags_private_handle_accessor absl::flags_private_handle_accessor
absl::core_headers absl::flags_config
absl::raw_logging_internal
absl::strings absl::strings
absl::synchronization absl::synchronization
) )
# Internal-only target, do not depend on directly.
absl_cc_library(
NAME
flags_internal
SRCS
"internal/flag.cc"
HDRS
"internal/flag.h"
COPTS
${ABSL_DEFAULT_COPTS}
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
absl::base
absl::config
absl::flags_commandlineflag_internal
absl::flags_config
absl::flags_marshalling
absl::synchronization
absl::meta
absl::utility
PUBLIC
)
absl_cc_library( absl_cc_library(
NAME NAME
flags flags
@ -192,11 +203,10 @@ absl_cc_library(
${ABSL_DEFAULT_LINKOPTS} ${ABSL_DEFAULT_LINKOPTS}
DEPS DEPS
absl::config absl::config
absl::flags_commandlineflag
absl::flags_config absl::flags_config
absl::flags_handle
absl::flags_internal absl::flags_internal
absl::flags_marshalling absl::flags_reflection
absl::flags_registry
absl::base absl::base
absl::core_headers absl::core_headers
absl::strings absl::strings
@ -218,12 +228,12 @@ absl_cc_library(
absl::config absl::config
absl::flags_config absl::flags_config
absl::flags absl::flags
absl::flags_handle absl::flags_commandlineflag
absl::flags_private_handle_accessor
absl::flags_internal absl::flags_internal
absl::flags_path_util absl::flags_path_util
absl::flags_private_handle_accessor
absl::flags_program_name absl::flags_program_name
absl::flags_registry absl::flags_reflection
absl::strings absl::strings
absl::synchronization absl::synchronization
) )
@ -264,11 +274,12 @@ absl_cc_library(
absl::core_headers absl::core_headers
absl::flags_config absl::flags_config
absl::flags absl::flags
absl::flags_handle absl::flags_commandlineflag
absl::flags_private_handle_accessor absl::flags_commandlineflag_internal
absl::flags_internal absl::flags_internal
absl::flags_private_handle_accessor
absl::flags_program_name absl::flags_program_name
absl::flags_registry absl::flags_reflection
absl::flags_usage absl::flags_usage
absl::strings absl::strings
absl::synchronization absl::synchronization
@ -281,15 +292,16 @@ absl_cc_test(
NAME NAME
flags_commandlineflag_test flags_commandlineflag_test
SRCS SRCS
"internal/commandlineflag_test.cc" "commandlineflag_test.cc"
COPTS COPTS
${ABSL_TEST_COPTS} ${ABSL_TEST_COPTS}
DEPS DEPS
absl::flags absl::flags
absl::flags_commandlineflag
absl::flags_commandlineflag_internal
absl::flags_config absl::flags_config
absl::flags_handle
absl::flags_private_handle_accessor absl::flags_private_handle_accessor
absl::flags_registry absl::flags_reflection
absl::memory absl::memory
absl::strings absl::strings
gtest_main gtest_main
@ -319,9 +331,9 @@ absl_cc_test(
absl::core_headers absl::core_headers
absl::flags absl::flags
absl::flags_config absl::flags_config
absl::flags_handle
absl::flags_internal absl::flags_internal
absl::flags_registry absl::flags_marshalling
absl::flags_reflection
absl::strings absl::strings
absl::time absl::time
gtest_main gtest_main
@ -349,7 +361,7 @@ absl_cc_test(
DEPS DEPS
absl::flags absl::flags
absl::flags_parse absl::flags_parse
absl::flags_registry absl::flags_reflection
absl::raw_logging_internal absl::raw_logging_internal
absl::scoped_set_env absl::scoped_set_env
absl::span absl::span
@ -384,16 +396,15 @@ absl_cc_test(
absl_cc_test( absl_cc_test(
NAME NAME
flags_type_erased_test flags_reflection_test
SRCS SRCS
"internal/type_erased_test.cc" "reflection_test.cc"
COPTS COPTS
${ABSL_TEST_COPTS} ${ABSL_TEST_COPTS}
DEPS DEPS
absl::flags_commandlineflag_internal
absl::flags absl::flags
absl::flags_handle absl::flags_reflection
absl::flags_marshalling
absl::flags_registry
absl::memory absl::memory
absl::strings absl::strings
gtest_main gtest_main
@ -427,9 +438,8 @@ absl_cc_test(
absl::flags_path_util absl::flags_path_util
absl::flags_program_name absl::flags_program_name
absl::flags_parse absl::flags_parse
absl::flags_registry absl::flags_reflection
absl::flags_usage absl::flags_usage
absl::memory
absl::strings absl::strings
gtest gtest
) )

View file

@ -13,21 +13,25 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "absl/flags/commandlineflag.h"
#include <string>
#include "absl/base/config.h"
#include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/commandlineflag.h"
#include "absl/strings/string_view.h"
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
namespace flags_internal {
FlagStateInterface::~FlagStateInterface() {}
bool CommandLineFlag::IsRetired() const { return false; } bool CommandLineFlag::IsRetired() const { return false; }
bool CommandLineFlag::ParseFrom(absl::string_view value, std::string* error) { bool CommandLineFlag::ParseFrom(absl::string_view value, std::string* error) {
return ParseFrom(value, flags_internal::SET_FLAGS_VALUE, return ParseFrom(value, flags_internal::SET_FLAGS_VALUE,
flags_internal::kProgrammaticChange, error); flags_internal::kProgrammaticChange, *error);
} }
namespace flags_internal {
FlagStateInterface::~FlagStateInterface() {}
} // namespace flags_internal } // namespace flags_internal
ABSL_NAMESPACE_END ABSL_NAMESPACE_END
} // namespace absl } // namespace absl

View file

@ -0,0 +1,196 @@
//
// Copyright 2020 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
//
// https://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.
//
// -----------------------------------------------------------------------------
// File: commandlineflag.h
// -----------------------------------------------------------------------------
//
// This header file defines the `CommandLineFlag`, which acts as a type-erased
// handle for accessing metadata about the Abseil Flag in question.
//
// Because an actual Abseil flag is of an unspecified type, you should not
// manipulate or interact directly with objects of that type. Instead, use the
// CommandLineFlag type as an intermediary.
#ifndef ABSL_FLAGS_COMMANDLINEFLAG_H_
#define ABSL_FLAGS_COMMANDLINEFLAG_H_
#include <memory>
#include <string>
#include "absl/base/config.h"
#include "absl/base/internal/fast_type_id.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace flags_internal {
class PrivateHandleAccessor;
} // namespace flags_internal
// CommandLineFlag
//
// This type acts as a type-erased handle for an instance of an Abseil Flag and
// holds reflection information pertaining to that flag. Use CommandLineFlag to
// access a flag's name, location, help string etc.
//
// To obtain an absl::CommandLineFlag, invoke `absl::FindCommandLineFlag()`
// passing it the flag name string.
//
// Example:
//
// // Obtain reflection handle for a flag named "flagname".
// const absl::CommandLineFlag* my_flag_data =
// absl::FindCommandLineFlag("flagname");
//
// // Now you can get flag info from that reflection handle.
// std::string flag_location = my_flag_data->Filename();
// ...
class CommandLineFlag {
public:
constexpr CommandLineFlag() = default;
// Not copyable/assignable.
CommandLineFlag(const CommandLineFlag&) = delete;
CommandLineFlag& operator=(const CommandLineFlag&) = delete;
// absl::CommandLineFlag::IsOfType()
//
// Return true iff flag has type T.
template <typename T>
inline bool IsOfType() const {
return TypeId() == base_internal::FastTypeId<T>();
}
// absl::CommandLineFlag::TryGet()
//
// Attempts to retrieve the flag value. Returns value on success,
// absl::nullopt otherwise.
template <typename T>
absl::optional<T> TryGet() const {
if (IsRetired() || !IsOfType<T>()) {
return absl::nullopt;
}
// Implementation notes:
//
// We are wrapping a union around the value of `T` to serve three purposes:
//
// 1. `U.value` has correct size and alignment for a value of type `T`
// 2. The `U.value` constructor is not invoked since U's constructor does
// not do it explicitly.
// 3. The `U.value` destructor is invoked since U's destructor does it
// explicitly. This makes `U` a kind of RAII wrapper around non default
// constructible value of T, which is destructed when we leave the
// scope. We do need to destroy U.value, which is constructed by
// CommandLineFlag::Read even though we left it in a moved-from state
// after std::move.
//
// All of this serves to avoid requiring `T` being default constructible.
union U {
T value;
U() {}
~U() { value.~T(); }
};
U u;
Read(&u.value);
return std::move(u.value);
}
// absl::CommandLineFlag::Name()
//
// Returns name of this flag.
virtual absl::string_view Name() const = 0;
// absl::CommandLineFlag::Filename()
//
// Returns name of the file where this flag is defined.
virtual std::string Filename() const = 0;
// absl::CommandLineFlag::Help()
//
// Returns help message associated with this flag.
virtual std::string Help() const = 0;
// absl::CommandLineFlag::IsRetired()
//
// Returns true iff this object corresponds to retired flag.
virtual bool IsRetired() const;
// absl::CommandLineFlag::DefaultValue()
//
// Returns the default value for this flag.
virtual std::string DefaultValue() const = 0;
// absl::CommandLineFlag::CurrentValue()
//
// Returns the current value for this flag.
virtual std::string CurrentValue() const = 0;
// absl::CommandLineFlag::ParseFrom()
//
// Sets the value of the flag based on specified string `value`. If the flag
// was successfully set to new value, it returns true. Otherwise, sets `error`
// to indicate the error, leaves the flag unchanged, and returns false.
bool ParseFrom(absl::string_view value, std::string* error);
protected:
~CommandLineFlag() = default;
private:
friend class flags_internal::PrivateHandleAccessor;
// Sets the value of the flag based on specified string `value`. If the flag
// was successfully set to new value, it returns true. Otherwise, sets `error`
// to indicate the error, leaves the flag unchanged, and returns false. There
// are three ways to set the flag's value:
// * Update the current flag value
// * Update the flag's default value
// * Update the current flag value if it was never set before
// The mode is selected based on `set_mode` parameter.
virtual bool ParseFrom(absl::string_view value,
flags_internal::FlagSettingMode set_mode,
flags_internal::ValueSource source,
std::string& error) = 0;
// Returns id of the flag's value type.
virtual flags_internal::FlagFastTypeId TypeId() const = 0;
// Interface to save flag to some persistent state. Returns current flag state
// or nullptr if flag does not support saving and restoring a state.
virtual std::unique_ptr<flags_internal::FlagStateInterface> SaveState() = 0;
// Copy-construct a new value of the flag's type in a memory referenced by
// the dst based on the current flag's value.
virtual void Read(void* dst) const = 0;
// To be deleted. Used to return true if flag's current value originated from
// command line.
virtual bool IsSpecifiedOnCommandLine() const = 0;
// Validates supplied value usign validator or parseflag routine
virtual bool ValidateInputValue(absl::string_view value) const = 0;
// Checks that flags default value can be converted to string and back to the
// flag's value type.
virtual void CheckDefaultValueParsingRoundtrip() const = 0;
};
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_COMMANDLINEFLAG_H_

View file

@ -13,15 +13,16 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "absl/flags/internal/commandlineflag.h" #include "absl/flags/commandlineflag.h"
#include <memory> #include <memory>
#include <string> #include <string>
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "absl/flags/flag.h" #include "absl/flags/flag.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/private_handle_accessor.h" #include "absl/flags/internal/private_handle_accessor.h"
#include "absl/flags/internal/registry.h" #include "absl/flags/reflection.h"
#include "absl/flags/usage_config.h" #include "absl/flags/usage_config.h"
#include "absl/memory/memory.h" #include "absl/memory/memory.h"
#include "absl/strings/match.h" #include "absl/strings/match.h"
@ -33,6 +34,10 @@ ABSL_FLAG(std::string, string_flag, "dflt",
absl::StrCat("string_flag", " help")); absl::StrCat("string_flag", " help"));
ABSL_RETIRED_FLAG(bool, bool_retired_flag, false, "bool_retired_flag help"); ABSL_RETIRED_FLAG(bool, bool_retired_flag, false, "bool_retired_flag help");
// These are only used to test default values.
ABSL_FLAG(int, int_flag2, 201, "");
ABSL_FLAG(std::string, string_flag2, "dflt", "");
namespace { namespace {
namespace flags = absl::flags_internal; namespace flags = absl::flags_internal;
@ -46,7 +51,7 @@ class CommandLineFlagTest : public testing::Test {
absl::SetFlagsUsageConfig(default_config); absl::SetFlagsUsageConfig(default_config);
} }
void SetUp() override { flag_saver_ = absl::make_unique<flags::FlagSaver>(); } void SetUp() override { flag_saver_ = absl::make_unique<absl::FlagSaver>(); }
void TearDown() override { flag_saver_.reset(); } void TearDown() override { flag_saver_.reset(); }
private: private:
@ -59,56 +64,49 @@ class CommandLineFlagTest : public testing::Test {
return std::string(fname); return std::string(fname);
} }
std::unique_ptr<flags::FlagSaver> flag_saver_; std::unique_ptr<absl::FlagSaver> flag_saver_;
}; };
TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) { TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) {
auto* flag_01 = flags::FindCommandLineFlag("int_flag"); auto* flag_01 = absl::FindCommandLineFlag("int_flag");
ASSERT_TRUE(flag_01); ASSERT_TRUE(flag_01);
EXPECT_EQ(flag_01->Name(), "int_flag"); EXPECT_EQ(flag_01->Name(), "int_flag");
EXPECT_EQ(flag_01->Help(), "int_flag help"); EXPECT_EQ(flag_01->Help(), "int_flag help");
EXPECT_TRUE(!flag_01->IsRetired()); EXPECT_TRUE(!flag_01->IsRetired());
EXPECT_TRUE(flag_01->IsOfType<int>()); EXPECT_TRUE(flag_01->IsOfType<int>());
EXPECT_TRUE( EXPECT_TRUE(!flag_01->IsOfType<bool>());
absl::EndsWith(flag_01->Filename(), EXPECT_TRUE(!flag_01->IsOfType<std::string>());
"absl/flags/internal/commandlineflag_test.cc")) EXPECT_TRUE(absl::EndsWith(flag_01->Filename(),
"absl/flags/commandlineflag_test.cc"))
<< flag_01->Filename(); << flag_01->Filename();
auto* flag_02 = flags::FindCommandLineFlag("string_flag"); auto* flag_02 = absl::FindCommandLineFlag("string_flag");
ASSERT_TRUE(flag_02); ASSERT_TRUE(flag_02);
EXPECT_EQ(flag_02->Name(), "string_flag"); EXPECT_EQ(flag_02->Name(), "string_flag");
EXPECT_EQ(flag_02->Help(), "string_flag help"); EXPECT_EQ(flag_02->Help(), "string_flag help");
EXPECT_TRUE(!flag_02->IsRetired()); EXPECT_TRUE(!flag_02->IsRetired());
EXPECT_TRUE(flag_02->IsOfType<std::string>()); EXPECT_TRUE(flag_02->IsOfType<std::string>());
EXPECT_TRUE( EXPECT_TRUE(!flag_02->IsOfType<bool>());
absl::EndsWith(flag_02->Filename(), EXPECT_TRUE(!flag_02->IsOfType<int>());
"absl/flags/internal/commandlineflag_test.cc")) EXPECT_TRUE(absl::EndsWith(flag_02->Filename(),
"absl/flags/commandlineflag_test.cc"))
<< flag_02->Filename(); << flag_02->Filename();
auto* flag_03 = flags::FindRetiredFlag("bool_retired_flag");
ASSERT_TRUE(flag_03);
EXPECT_EQ(flag_03->Name(), "bool_retired_flag");
EXPECT_EQ(flag_03->Help(), "");
EXPECT_TRUE(flag_03->IsRetired());
EXPECT_TRUE(flag_03->IsOfType<bool>());
EXPECT_EQ(flag_03->Filename(), "RETIRED");
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
TEST_F(CommandLineFlagTest, TestValueAccessMethods) { TEST_F(CommandLineFlagTest, TestValueAccessMethods) {
absl::SetFlag(&FLAGS_int_flag, 301); absl::SetFlag(&FLAGS_int_flag2, 301);
auto* flag_01 = flags::FindCommandLineFlag("int_flag"); auto* flag_01 = absl::FindCommandLineFlag("int_flag2");
ASSERT_TRUE(flag_01); ASSERT_TRUE(flag_01);
EXPECT_EQ(flag_01->CurrentValue(), "301"); EXPECT_EQ(flag_01->CurrentValue(), "301");
EXPECT_EQ(flag_01->DefaultValue(), "201"); EXPECT_EQ(flag_01->DefaultValue(), "201");
absl::SetFlag(&FLAGS_string_flag, "new_str_value"); absl::SetFlag(&FLAGS_string_flag2, "new_str_value");
auto* flag_02 = flags::FindCommandLineFlag("string_flag"); auto* flag_02 = absl::FindCommandLineFlag("string_flag2");
ASSERT_TRUE(flag_02); ASSERT_TRUE(flag_02);
EXPECT_EQ(flag_02->CurrentValue(), "new_str_value"); EXPECT_EQ(flag_02->CurrentValue(), "new_str_value");
@ -120,62 +118,62 @@ TEST_F(CommandLineFlagTest, TestValueAccessMethods) {
TEST_F(CommandLineFlagTest, TestParseFromCurrentValue) { TEST_F(CommandLineFlagTest, TestParseFromCurrentValue) {
std::string err; std::string err;
auto* flag_01 = flags::FindCommandLineFlag("int_flag"); auto* flag_01 = absl::FindCommandLineFlag("int_flag");
EXPECT_FALSE( EXPECT_FALSE(
flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01)); flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01));
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
flag_01, "11", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, &err)); *flag_01, "11", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, err));
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11);
EXPECT_FALSE( EXPECT_FALSE(
flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01)); flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01));
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
flag_01, "-123", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, *flag_01, "-123", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange,
&err)); err));
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123);
EXPECT_FALSE( EXPECT_FALSE(
flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01)); flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01));
EXPECT_TRUE(!flags::PrivateHandleAccessor::ParseFrom( EXPECT_TRUE(!flags::PrivateHandleAccessor::ParseFrom(
flag_01, "xyz", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, *flag_01, "xyz", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange,
&err)); err));
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123);
EXPECT_EQ(err, "Illegal value 'xyz' specified for flag 'int_flag'"); EXPECT_EQ(err, "Illegal value 'xyz' specified for flag 'int_flag'");
EXPECT_FALSE( EXPECT_FALSE(
flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01)); flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01));
EXPECT_TRUE(!flags::PrivateHandleAccessor::ParseFrom( EXPECT_TRUE(!flags::PrivateHandleAccessor::ParseFrom(
flag_01, "A1", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, &err)); *flag_01, "A1", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, err));
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123);
EXPECT_EQ(err, "Illegal value 'A1' specified for flag 'int_flag'"); EXPECT_EQ(err, "Illegal value 'A1' specified for flag 'int_flag'");
EXPECT_FALSE( EXPECT_FALSE(
flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01)); flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01));
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
flag_01, "0x10", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, *flag_01, "0x10", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange,
&err)); err));
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 16); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 16);
EXPECT_FALSE( EXPECT_FALSE(
flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01)); flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01));
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
flag_01, "011", flags::SET_FLAGS_VALUE, flags::kCommandLine, &err)); *flag_01, "011", flags::SET_FLAGS_VALUE, flags::kCommandLine, err));
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11);
EXPECT_TRUE(flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01)); EXPECT_TRUE(flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01));
EXPECT_TRUE(!flags::PrivateHandleAccessor::ParseFrom( EXPECT_TRUE(!flags::PrivateHandleAccessor::ParseFrom(
flag_01, "", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, &err)); *flag_01, "", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, err));
EXPECT_EQ(err, "Illegal value '' specified for flag 'int_flag'"); EXPECT_EQ(err, "Illegal value '' specified for flag 'int_flag'");
auto* flag_02 = flags::FindCommandLineFlag("string_flag"); auto* flag_02 = absl::FindCommandLineFlag("string_flag");
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
flag_02, "xyz", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, *flag_02, "xyz", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange,
&err)); err));
EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "xyz"); EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "xyz");
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
flag_02, "", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, &err)); *flag_02, "", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, err));
EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), ""); EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "");
} }
@ -184,18 +182,18 @@ TEST_F(CommandLineFlagTest, TestParseFromCurrentValue) {
TEST_F(CommandLineFlagTest, TestParseFromDefaultValue) { TEST_F(CommandLineFlagTest, TestParseFromDefaultValue) {
std::string err; std::string err;
auto* flag_01 = flags::FindCommandLineFlag("int_flag"); auto* flag_01 = absl::FindCommandLineFlag("int_flag");
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
flag_01, "111", flags::SET_FLAGS_DEFAULT, flags::kProgrammaticChange, *flag_01, "111", flags::SET_FLAGS_DEFAULT, flags::kProgrammaticChange,
&err)); err));
EXPECT_EQ(flag_01->DefaultValue(), "111"); EXPECT_EQ(flag_01->DefaultValue(), "111");
auto* flag_02 = flags::FindCommandLineFlag("string_flag"); auto* flag_02 = absl::FindCommandLineFlag("string_flag");
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
flag_02, "abc", flags::SET_FLAGS_DEFAULT, flags::kProgrammaticChange, *flag_02, "abc", flags::SET_FLAGS_DEFAULT, flags::kProgrammaticChange,
&err)); err));
EXPECT_EQ(flag_02->DefaultValue(), "abc"); EXPECT_EQ(flag_02->DefaultValue(), "abc");
} }
@ -204,28 +202,28 @@ TEST_F(CommandLineFlagTest, TestParseFromDefaultValue) {
TEST_F(CommandLineFlagTest, TestParseFromIfDefault) { TEST_F(CommandLineFlagTest, TestParseFromIfDefault) {
std::string err; std::string err;
auto* flag_01 = flags::FindCommandLineFlag("int_flag"); auto* flag_01 = absl::FindCommandLineFlag("int_flag");
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
flag_01, "22", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange, *flag_01, "22", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange,
&err)) err))
<< err; << err;
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 22); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 22);
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
flag_01, "33", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange, *flag_01, "33", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange,
&err)); err));
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 22); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 22);
// EXPECT_EQ(err, "ERROR: int_flag is already set to 22"); // EXPECT_EQ(err, "ERROR: int_flag is already set to 22");
// Reset back to default value // Reset back to default value
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
flag_01, "201", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, *flag_01, "201", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange,
&err)); err));
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
flag_01, "33", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange, *flag_01, "33", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange,
&err)); err));
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 201); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 201);
// EXPECT_EQ(err, "ERROR: int_flag is already set to 201"); // EXPECT_EQ(err, "ERROR: int_flag is already set to 201");
} }

View file

@ -26,7 +26,6 @@
#define ABSL_FLAGS_DECLARE_H_ #define ABSL_FLAGS_DECLARE_H_
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/strings/string_view.h"
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN

View file

@ -16,8 +16,6 @@
#include "absl/flags/flag.h" #include "absl/flags/flag.h"
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/flag.h"
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN

View file

@ -33,14 +33,11 @@
#include <type_traits> #include <type_traits>
#include "absl/base/attributes.h" #include "absl/base/attributes.h"
#include "absl/base/casts.h"
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/base/optimization.h"
#include "absl/flags/config.h" #include "absl/flags/config.h"
#include "absl/flags/declare.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/flag.h" #include "absl/flags/internal/flag.h"
#include "absl/flags/internal/registry.h" #include "absl/flags/internal/registry.h"
#include "absl/flags/marshalling.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
namespace absl { namespace absl {
@ -111,12 +108,12 @@ class Flag {
impl_(nullptr) {} impl_(nullptr) {}
#endif #endif
flags_internal::Flag<T>* GetImpl() const { flags_internal::Flag<T>& GetImpl() const {
if (!inited_.load(std::memory_order_acquire)) { if (!inited_.load(std::memory_order_acquire)) {
absl::MutexLock l(flags_internal::GetGlobalConstructionGuard()); absl::MutexLock l(flags_internal::GetGlobalConstructionGuard());
if (inited_.load(std::memory_order_acquire)) { if (inited_.load(std::memory_order_acquire)) {
return impl_; return *impl_;
} }
impl_ = new flags_internal::Flag<T>( impl_ = new flags_internal::Flag<T>(
@ -128,28 +125,30 @@ class Flag {
inited_.store(true, std::memory_order_release); inited_.store(true, std::memory_order_release);
} }
return impl_; return *impl_;
} }
// Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API. // Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API.
// See https://abseil.io/docs/cpp/guides/flags // See https://abseil.io/docs/cpp/guides/flags
bool IsRetired() const { return GetImpl()->IsRetired(); } bool IsRetired() const { return GetImpl().IsRetired(); }
absl::string_view Name() const { return GetImpl()->Name(); } absl::string_view Name() const { return GetImpl().Name(); }
std::string Help() const { return GetImpl()->Help(); } std::string Help() const { return GetImpl().Help(); }
bool IsModified() const { return GetImpl()->IsModified(); } bool IsModified() const { return GetImpl().IsModified(); }
bool IsSpecifiedOnCommandLine() const { bool IsSpecifiedOnCommandLine() const {
return GetImpl()->IsSpecifiedOnCommandLine(); return GetImpl().IsSpecifiedOnCommandLine();
} }
std::string Filename() const { return GetImpl()->Filename(); } std::string Filename() const { return GetImpl().Filename(); }
std::string DefaultValue() const { return GetImpl()->DefaultValue(); } std::string DefaultValue() const { return GetImpl().DefaultValue(); }
std::string CurrentValue() const { return GetImpl()->CurrentValue(); } std::string CurrentValue() const { return GetImpl().CurrentValue(); }
template <typename U> template <typename U>
inline bool IsOfType() const { inline bool IsOfType() const {
return GetImpl()->template IsOfType<U>(); return GetImpl().template IsOfType<U>();
} }
T Get() const { return GetImpl()->Get(); } T Get() const { return GetImpl().Get(); }
void Set(const T& v) { GetImpl()->Set(v); } void Set(const T& v) { GetImpl().Set(v); }
void InvokeCallback() { GetImpl()->InvokeCallback(); } void InvokeCallback() { GetImpl().InvokeCallback(); }
const CommandLineFlag& Reflect() const { return GetImpl().Reflect(); }
// The data members are logically private, but they need to be public for // The data members are logically private, but they need to be public for
// this to be an aggregate type. // this to be an aggregate type.
@ -205,6 +204,21 @@ void SetFlag(absl::Flag<T>* flag, const V& v) {
flag->Set(value); flag->Set(value);
} }
// GetFlagReflectionHandle()
//
// Returns the reflection handle corresponding to specified Abseil Flag
// instance. Use this handle to access flag's reflection information, like name,
// location, default value etc.
//
// Example:
//
// std::string = absl::GetFlagReflectionHandle(FLAGS_count).DefaultValue();
template <typename T>
const CommandLineFlag& GetFlagReflectionHandle(const absl::Flag<T>& f) {
return f.Reflect();
}
ABSL_NAMESPACE_END ABSL_NAMESPACE_END
} // namespace absl } // namespace absl
@ -265,27 +279,29 @@ ABSL_NAMESPACE_END
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// ABSL_FLAG_IMPL macro definition conditional on ABSL_FLAGS_STRIP_NAMES // ABSL_FLAG_IMPL macro definition conditional on ABSL_FLAGS_STRIP_NAMES
#if !defined(_MSC_VER) || defined(__clang__)
#define ABSL_FLAG_IMPL_FLAG_PTR(flag) flag
#define ABSL_FLAG_IMPL_HELP_ARG(name) \
absl::flags_internal::HelpArg<AbslFlagHelpGenFor##name>( \
FLAGS_help_storage_##name)
#define ABSL_FLAG_IMPL_DEFAULT_ARG(Type, name) \
absl::flags_internal::DefaultArg<Type, AbslFlagDefaultGenFor##name>(0)
#else
#define ABSL_FLAG_IMPL_FLAG_PTR(flag) flag.GetImpl()
#define ABSL_FLAG_IMPL_HELP_ARG(name) &AbslFlagHelpGenFor##name::NonConst
#define ABSL_FLAG_IMPL_DEFAULT_ARG(Type, name) &AbslFlagDefaultGenFor##name::Gen
#endif
#if ABSL_FLAGS_STRIP_NAMES #if ABSL_FLAGS_STRIP_NAMES
#define ABSL_FLAG_IMPL_FLAGNAME(txt) "" #define ABSL_FLAG_IMPL_FLAGNAME(txt) ""
#define ABSL_FLAG_IMPL_FILENAME() "" #define ABSL_FLAG_IMPL_FILENAME() ""
#if !defined(_MSC_VER) || defined(__clang__)
#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ #define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
absl::flags_internal::FlagRegistrar<T, false>(&flag) absl::flags_internal::FlagRegistrar<T, false>(ABSL_FLAG_IMPL_FLAG_PTR(flag))
#else
#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
absl::flags_internal::FlagRegistrar<T, false>(flag.GetImpl())
#endif
#else #else
#define ABSL_FLAG_IMPL_FLAGNAME(txt) txt #define ABSL_FLAG_IMPL_FLAGNAME(txt) txt
#define ABSL_FLAG_IMPL_FILENAME() __FILE__ #define ABSL_FLAG_IMPL_FILENAME() __FILE__
#if !defined(_MSC_VER) || defined(__clang__)
#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ #define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
absl::flags_internal::FlagRegistrar<T, true>(&flag) absl::flags_internal::FlagRegistrar<T, true>(ABSL_FLAG_IMPL_FLAG_PTR(flag))
#else
#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
absl::flags_internal::FlagRegistrar<T, true>(flag.GetImpl())
#endif
#endif #endif
// ABSL_FLAG_IMPL macro definition conditional on ABSL_FLAGS_STRIP_HELP // ABSL_FLAG_IMPL macro definition conditional on ABSL_FLAGS_STRIP_HELP
@ -301,15 +317,24 @@ ABSL_NAMESPACE_END
// between the two via the call to HelpArg in absl::Flag instantiation below. // between the two via the call to HelpArg in absl::Flag instantiation below.
// If help message expression is constexpr evaluable compiler will optimize // If help message expression is constexpr evaluable compiler will optimize
// away this whole struct. // away this whole struct.
#define ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, txt) \ // TODO(rogeeff): place these generated structs into local namespace and apply
struct AbslFlagHelpGenFor##name { \ // ABSL_INTERNAL_UNIQUE_SHORT_NAME.
template <typename T = void> \ // TODO(rogeeff): Apply __attribute__((nodebug)) to FLAGS_help_storage_##name
static constexpr const char* Const() { \ #define ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, txt) \
return absl::flags_internal::HelpConstexprWrap( \ struct AbslFlagHelpGenFor##name { \
ABSL_FLAG_IMPL_FLAGHELP(txt)); \ /* The expression is run in the caller as part of the */ \
} \ /* default value argument. That keeps temporaries alive */ \
static std::string NonConst() { return ABSL_FLAG_IMPL_FLAGHELP(txt); } \ /* long enough for NonConst to work correctly. */ \
} static constexpr absl::string_view Value( \
absl::string_view v = ABSL_FLAG_IMPL_FLAGHELP(txt)) { \
return v; \
} \
static std::string NonConst() { return std::string(Value()); } \
}; \
constexpr auto FLAGS_help_storage_##name ABSL_INTERNAL_UNIQUE_SMALL_NAME() \
ABSL_ATTRIBUTE_SECTION_VARIABLE(flags_help_cold) = \
absl::flags_internal::HelpStringAsArray<AbslFlagHelpGenFor##name>( \
0);
#define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ #define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \
struct AbslFlagDefaultGenFor##name { \ struct AbslFlagDefaultGenFor##name { \
@ -317,40 +342,23 @@ ABSL_NAMESPACE_END
static void Gen(void* p) { \ static void Gen(void* p) { \
new (p) Type(AbslFlagDefaultGenFor##name{}.value); \ new (p) Type(AbslFlagDefaultGenFor##name{}.value); \
} \ } \
} };
// ABSL_FLAG_IMPL // ABSL_FLAG_IMPL
// //
// Note: Name of registrar object is not arbitrary. It is used to "grab" // Note: Name of registrar object is not arbitrary. It is used to "grab"
// global name for FLAGS_no<flag_name> symbol, thus preventing the possibility // global name for FLAGS_no<flag_name> symbol, thus preventing the possibility
// of defining two flags with names foo and nofoo. // of defining two flags with names foo and nofoo.
#if !defined(_MSC_VER) || defined(__clang__) #define ABSL_FLAG_IMPL(Type, name, default_value, help) \
namespace absl /* block flags in namespaces */ {} \
#define ABSL_FLAG_IMPL(Type, name, default_value, help) \ ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \
namespace absl /* block flags in namespaces */ {} \ ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help) \
ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value); \ ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name{ \
ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help); \ ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(), \
ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name{ \ ABSL_FLAG_IMPL_HELP_ARG(name), ABSL_FLAG_IMPL_DEFAULT_ARG(Type, name)}; \
ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(), \ extern absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name; \
absl::flags_internal::HelpArg<AbslFlagHelpGenFor##name>(0), \ absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name = \
absl::flags_internal::DefaultArg<Type, AbslFlagDefaultGenFor##name>(0)}; \
extern absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name; \
absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name = \
ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name) ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name)
#else
// MSVC version uses aggregate initialization. We also do not try to
// optimize away help wrapper.
#define ABSL_FLAG_IMPL(Type, name, default_value, help) \
namespace absl /* block flags in namespaces */ {} \
ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value); \
ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help); \
ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name{ \
ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(), \
&AbslFlagHelpGenFor##name::NonConst, &AbslFlagDefaultGenFor##name::Gen}; \
extern absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name; \
absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name = \
ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name)
#endif
// ABSL_RETIRED_FLAG // ABSL_RETIRED_FLAG
// //

View file

@ -13,7 +13,14 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include <stdint.h>
#include <string>
#include <vector>
#include "absl/flags/flag.h" #include "absl/flags/flag.h"
#include "absl/flags/marshalling.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h" #include "absl/time/time.h"
#include "absl/types/optional.h" #include "absl/types/optional.h"
#include "benchmark/benchmark.h" #include "benchmark/benchmark.h"

View file

@ -15,9 +15,11 @@
#include "absl/flags/flag.h" #include "absl/flags/flag.h"
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <cmath> #include <cmath>
#include <new>
#include <string> #include <string>
#include <thread> // NOLINT #include <thread> // NOLINT
#include <vector> #include <vector>
@ -26,9 +28,9 @@
#include "absl/base/attributes.h" #include "absl/base/attributes.h"
#include "absl/flags/config.h" #include "absl/flags/config.h"
#include "absl/flags/declare.h" #include "absl/flags/declare.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/flag.h" #include "absl/flags/internal/flag.h"
#include "absl/flags/internal/registry.h" #include "absl/flags/marshalling.h"
#include "absl/flags/reflection.h"
#include "absl/flags/usage_config.h" #include "absl/flags/usage_config.h"
#include "absl/strings/match.h" #include "absl/strings/match.h"
#include "absl/strings/numbers.h" #include "absl/strings/numbers.h"
@ -45,6 +47,9 @@ namespace {
namespace flags = absl::flags_internal; namespace flags = absl::flags_internal;
std::string TestHelpMsg() { return "dynamic help"; } std::string TestHelpMsg() { return "dynamic help"; }
#if defined(_MSC_VER) && !defined(__clang__)
std::string TestLiteralHelpMsg() { return "literal help"; }
#endif
template <typename T> template <typename T>
void TestMakeDflt(void* dst) { void TestMakeDflt(void* dst) {
new (dst) T{}; new (dst) T{};
@ -76,7 +81,7 @@ class FlagTest : public testing::Test {
#endif #endif
return std::string(fname); return std::string(fname);
} }
flags::FlagSaver flag_saver_; absl::FlagSaver flag_saver_;
}; };
struct S1 { struct S1 {
@ -128,15 +133,29 @@ constexpr flags::FlagHelpArg help_arg{flags::FlagHelpMsg("literal help"),
using String = std::string; using String = std::string;
#define DEFINE_CONSTRUCTED_FLAG(T, dflt, dflt_kind) \ #if !defined(_MSC_VER) || defined(__clang__)
constexpr flags::FlagDefaultArg f1default##T{ \ #define DEFINE_CONSTRUCTED_FLAG(T, dflt, dflt_kind) \
flags::FlagDefaultSrc{dflt}, flags::FlagDefaultKind::dflt_kind}; \ constexpr flags::FlagDefaultArg f1default##T{ \
constexpr flags::Flag<T> f1##T("f1", "file", help_arg, f1default##T); \ flags::FlagDefaultSrc{dflt}, flags::FlagDefaultKind::dflt_kind}; \
ABSL_CONST_INIT flags::Flag<T> f2##T( \ constexpr absl::Flag<T> f1##T{"f1", "file", help_arg, f1default##T}; \
"f2", "file", \ ABSL_CONST_INIT absl::Flag<T> f2##T { \
{flags::FlagHelpMsg(&TestHelpMsg), flags::FlagHelpKind::kGenFunc}, \ "f2", "file", \
flags::FlagDefaultArg{flags::FlagDefaultSrc(&TestMakeDflt<T>), \ {flags::FlagHelpMsg(&TestHelpMsg), flags::FlagHelpKind::kGenFunc}, \
flags::FlagDefaultKind::kGenFunc}) flags::FlagDefaultArg { \
flags::FlagDefaultSrc(&TestMakeDflt<T>), \
flags::FlagDefaultKind::kGenFunc \
} \
}
#else
#define DEFINE_CONSTRUCTED_FLAG(T, dflt, dflt_kind) \
constexpr flags::FlagDefaultArg f1default##T{ \
flags::FlagDefaultSrc{dflt}, flags::FlagDefaultKind::dflt_kind}; \
constexpr absl::Flag<T> f1##T{"f1", "file", &TestLiteralHelpMsg, \
&TestMakeDflt<T>}; \
ABSL_CONST_INIT absl::Flag<T> f2##T { \
"f2", "file", &TestHelpMsg, &TestMakeDflt<T> \
}
#endif
DEFINE_CONSTRUCTED_FLAG(bool, true, kOneWord); DEFINE_CONSTRUCTED_FLAG(bool, true, kOneWord);
DEFINE_CONSTRUCTED_FLAG(int16_t, 1, kOneWord); DEFINE_CONSTRUCTED_FLAG(int16_t, 1, kOneWord);
@ -151,21 +170,22 @@ DEFINE_CONSTRUCTED_FLAG(String, &TestMakeDflt<String>, kGenFunc);
DEFINE_CONSTRUCTED_FLAG(UDT, &TestMakeDflt<UDT>, kGenFunc); DEFINE_CONSTRUCTED_FLAG(UDT, &TestMakeDflt<UDT>, kGenFunc);
template <typename T> template <typename T>
bool TestConstructionFor(const flags::Flag<T>& f1, flags::Flag<T>* f2) { bool TestConstructionFor(const absl::Flag<T>& f1, absl::Flag<T>& f2) {
EXPECT_EQ(f1.Name(), "f1"); EXPECT_EQ(absl::GetFlagReflectionHandle(f1).Name(), "f1");
EXPECT_EQ(f1.Help(), "literal help"); EXPECT_EQ(absl::GetFlagReflectionHandle(f1).Help(), "literal help");
EXPECT_EQ(f1.Filename(), "file"); EXPECT_EQ(absl::GetFlagReflectionHandle(f1).Filename(), "file");
flags::FlagRegistrar<T, false>(f2).OnUpdate(TestCallback); flags::FlagRegistrar<T, false>(ABSL_FLAG_IMPL_FLAG_PTR(f2))
.OnUpdate(TestCallback);
EXPECT_EQ(f2->Name(), "f2"); EXPECT_EQ(absl::GetFlagReflectionHandle(f2).Name(), "f2");
EXPECT_EQ(f2->Help(), "dynamic help"); EXPECT_EQ(absl::GetFlagReflectionHandle(f2).Help(), "dynamic help");
EXPECT_EQ(f2->Filename(), "file"); EXPECT_EQ(absl::GetFlagReflectionHandle(f2).Filename(), "file");
return true; return true;
} }
#define TEST_CONSTRUCTED_FLAG(T) TestConstructionFor(f1##T, &f2##T); #define TEST_CONSTRUCTED_FLAG(T) TestConstructionFor(f1##T, f2##T);
TEST_F(FlagTest, TestConstruction) { TEST_F(FlagTest, TestConstruction) {
TEST_CONSTRUCTED_FLAG(bool); TEST_CONSTRUCTED_FLAG(bool);
@ -204,18 +224,30 @@ namespace {
TEST_F(FlagTest, TestFlagDeclaration) { TEST_F(FlagTest, TestFlagDeclaration) {
// test that we can access flag objects. // test that we can access flag objects.
EXPECT_EQ(FLAGS_test_flag_01.Name(), "test_flag_01"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_01).Name(),
EXPECT_EQ(FLAGS_test_flag_02.Name(), "test_flag_02"); "test_flag_01");
EXPECT_EQ(FLAGS_test_flag_03.Name(), "test_flag_03"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_02).Name(),
EXPECT_EQ(FLAGS_test_flag_04.Name(), "test_flag_04"); "test_flag_02");
EXPECT_EQ(FLAGS_test_flag_05.Name(), "test_flag_05"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_03).Name(),
EXPECT_EQ(FLAGS_test_flag_06.Name(), "test_flag_06"); "test_flag_03");
EXPECT_EQ(FLAGS_test_flag_07.Name(), "test_flag_07"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_04).Name(),
EXPECT_EQ(FLAGS_test_flag_08.Name(), "test_flag_08"); "test_flag_04");
EXPECT_EQ(FLAGS_test_flag_09.Name(), "test_flag_09"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_05).Name(),
EXPECT_EQ(FLAGS_test_flag_10.Name(), "test_flag_10"); "test_flag_05");
EXPECT_EQ(FLAGS_test_flag_11.Name(), "test_flag_11"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_06).Name(),
EXPECT_EQ(FLAGS_test_flag_12.Name(), "test_flag_12"); "test_flag_06");
EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_07).Name(),
"test_flag_07");
EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_08).Name(),
"test_flag_08");
EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_09).Name(),
"test_flag_09");
EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_10).Name(),
"test_flag_10");
EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_11).Name(),
"test_flag_11");
EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_12).Name(),
"test_flag_12");
} }
#endif // !ABSL_FLAGS_STRIP_NAMES #endif // !ABSL_FLAGS_STRIP_NAMES
@ -242,96 +274,168 @@ namespace {
TEST_F(FlagTest, TestFlagDefinition) { TEST_F(FlagTest, TestFlagDefinition) {
absl::string_view expected_file_name = "absl/flags/flag_test.cc"; absl::string_view expected_file_name = "absl/flags/flag_test.cc";
EXPECT_EQ(FLAGS_test_flag_01.Name(), "test_flag_01"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_01).Name(),
EXPECT_EQ(FLAGS_test_flag_01.Help(), "test flag 01"); "test_flag_01");
EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_01.Filename(), expected_file_name)) EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_01).Help(),
<< FLAGS_test_flag_01.Filename(); "test flag 01");
EXPECT_TRUE(absl::EndsWith(
absl::GetFlagReflectionHandle(FLAGS_test_flag_01).Filename(),
expected_file_name))
<< absl::GetFlagReflectionHandle(FLAGS_test_flag_01).Filename();
EXPECT_EQ(FLAGS_test_flag_02.Name(), "test_flag_02"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_02).Name(),
EXPECT_EQ(FLAGS_test_flag_02.Help(), "test flag 02"); "test_flag_02");
EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_02.Filename(), expected_file_name)) EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_02).Help(),
<< FLAGS_test_flag_02.Filename(); "test flag 02");
EXPECT_TRUE(absl::EndsWith(
absl::GetFlagReflectionHandle(FLAGS_test_flag_02).Filename(),
expected_file_name))
<< absl::GetFlagReflectionHandle(FLAGS_test_flag_02).Filename();
EXPECT_EQ(FLAGS_test_flag_03.Name(), "test_flag_03"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_03).Name(),
EXPECT_EQ(FLAGS_test_flag_03.Help(), "test flag 03"); "test_flag_03");
EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_03.Filename(), expected_file_name)) EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_03).Help(),
<< FLAGS_test_flag_03.Filename(); "test flag 03");
EXPECT_TRUE(absl::EndsWith(
absl::GetFlagReflectionHandle(FLAGS_test_flag_03).Filename(),
expected_file_name))
<< absl::GetFlagReflectionHandle(FLAGS_test_flag_03).Filename();
EXPECT_EQ(FLAGS_test_flag_04.Name(), "test_flag_04"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_04).Name(),
EXPECT_EQ(FLAGS_test_flag_04.Help(), "test flag 04"); "test_flag_04");
EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_04.Filename(), expected_file_name)) EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_04).Help(),
<< FLAGS_test_flag_04.Filename(); "test flag 04");
EXPECT_TRUE(absl::EndsWith(
absl::GetFlagReflectionHandle(FLAGS_test_flag_04).Filename(),
expected_file_name))
<< absl::GetFlagReflectionHandle(FLAGS_test_flag_04).Filename();
EXPECT_EQ(FLAGS_test_flag_05.Name(), "test_flag_05"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_05).Name(),
EXPECT_EQ(FLAGS_test_flag_05.Help(), "test flag 05"); "test_flag_05");
EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_05.Filename(), expected_file_name)) EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_05).Help(),
<< FLAGS_test_flag_05.Filename(); "test flag 05");
EXPECT_TRUE(absl::EndsWith(
absl::GetFlagReflectionHandle(FLAGS_test_flag_05).Filename(),
expected_file_name))
<< absl::GetFlagReflectionHandle(FLAGS_test_flag_05).Filename();
EXPECT_EQ(FLAGS_test_flag_06.Name(), "test_flag_06"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_06).Name(),
EXPECT_EQ(FLAGS_test_flag_06.Help(), "test flag 06"); "test_flag_06");
EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_06.Filename(), expected_file_name)) EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_06).Help(),
<< FLAGS_test_flag_06.Filename(); "test flag 06");
EXPECT_TRUE(absl::EndsWith(
absl::GetFlagReflectionHandle(FLAGS_test_flag_06).Filename(),
expected_file_name))
<< absl::GetFlagReflectionHandle(FLAGS_test_flag_06).Filename();
EXPECT_EQ(FLAGS_test_flag_07.Name(), "test_flag_07"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_07).Name(),
EXPECT_EQ(FLAGS_test_flag_07.Help(), "test flag 07"); "test_flag_07");
EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_07.Filename(), expected_file_name)) EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_07).Help(),
<< FLAGS_test_flag_07.Filename(); "test flag 07");
EXPECT_TRUE(absl::EndsWith(
absl::GetFlagReflectionHandle(FLAGS_test_flag_07).Filename(),
expected_file_name))
<< absl::GetFlagReflectionHandle(FLAGS_test_flag_07).Filename();
EXPECT_EQ(FLAGS_test_flag_08.Name(), "test_flag_08"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_08).Name(),
EXPECT_EQ(FLAGS_test_flag_08.Help(), "test flag 08"); "test_flag_08");
EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_08.Filename(), expected_file_name)) EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_08).Help(),
<< FLAGS_test_flag_08.Filename(); "test flag 08");
EXPECT_TRUE(absl::EndsWith(
absl::GetFlagReflectionHandle(FLAGS_test_flag_08).Filename(),
expected_file_name))
<< absl::GetFlagReflectionHandle(FLAGS_test_flag_08).Filename();
EXPECT_EQ(FLAGS_test_flag_09.Name(), "test_flag_09"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_09).Name(),
EXPECT_EQ(FLAGS_test_flag_09.Help(), "test flag 09"); "test_flag_09");
EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_09.Filename(), expected_file_name)) EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_09).Help(),
<< FLAGS_test_flag_09.Filename(); "test flag 09");
EXPECT_TRUE(absl::EndsWith(
absl::GetFlagReflectionHandle(FLAGS_test_flag_09).Filename(),
expected_file_name))
<< absl::GetFlagReflectionHandle(FLAGS_test_flag_09).Filename();
EXPECT_EQ(FLAGS_test_flag_10.Name(), "test_flag_10"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_10).Name(),
EXPECT_EQ(FLAGS_test_flag_10.Help(), "test flag 10"); "test_flag_10");
EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_10.Filename(), expected_file_name)) EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_10).Help(),
<< FLAGS_test_flag_10.Filename(); "test flag 10");
EXPECT_TRUE(absl::EndsWith(
absl::GetFlagReflectionHandle(FLAGS_test_flag_10).Filename(),
expected_file_name))
<< absl::GetFlagReflectionHandle(FLAGS_test_flag_10).Filename();
EXPECT_EQ(FLAGS_test_flag_11.Name(), "test_flag_11"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_11).Name(),
EXPECT_EQ(FLAGS_test_flag_11.Help(), "test flag 11"); "test_flag_11");
EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_11.Filename(), expected_file_name)) EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_11).Help(),
<< FLAGS_test_flag_11.Filename(); "test flag 11");
EXPECT_TRUE(absl::EndsWith(
absl::GetFlagReflectionHandle(FLAGS_test_flag_11).Filename(),
expected_file_name))
<< absl::GetFlagReflectionHandle(FLAGS_test_flag_11).Filename();
EXPECT_EQ(FLAGS_test_flag_12.Name(), "test_flag_12"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_12).Name(),
EXPECT_EQ(FLAGS_test_flag_12.Help(), "test flag 12"); "test_flag_12");
EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_12.Filename(), expected_file_name)) EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_12).Help(),
<< FLAGS_test_flag_12.Filename(); "test flag 12");
EXPECT_TRUE(absl::EndsWith(
absl::GetFlagReflectionHandle(FLAGS_test_flag_12).Filename(),
expected_file_name))
<< absl::GetFlagReflectionHandle(FLAGS_test_flag_12).Filename();
} }
#endif // !ABSL_FLAGS_STRIP_NAMES #endif // !ABSL_FLAGS_STRIP_NAMES
// -------------------------------------------------------------------- // --------------------------------------------------------------------
TEST_F(FlagTest, TestDefault) { TEST_F(FlagTest, TestDefault) {
EXPECT_EQ(FLAGS_test_flag_01.DefaultValue(), "true"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_01).DefaultValue(),
EXPECT_EQ(FLAGS_test_flag_02.DefaultValue(), "1234"); "true");
EXPECT_EQ(FLAGS_test_flag_03.DefaultValue(), "-34"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_02).DefaultValue(),
EXPECT_EQ(FLAGS_test_flag_04.DefaultValue(), "189"); "1234");
EXPECT_EQ(FLAGS_test_flag_05.DefaultValue(), "10765"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_03).DefaultValue(),
EXPECT_EQ(FLAGS_test_flag_06.DefaultValue(), "40000"); "-34");
EXPECT_EQ(FLAGS_test_flag_07.DefaultValue(), "-1234567"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_04).DefaultValue(),
EXPECT_EQ(FLAGS_test_flag_08.DefaultValue(), "9876543"); "189");
EXPECT_EQ(FLAGS_test_flag_09.DefaultValue(), "-9.876e-50"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_05).DefaultValue(),
EXPECT_EQ(FLAGS_test_flag_10.DefaultValue(), "1.234e+12"); "10765");
EXPECT_EQ(FLAGS_test_flag_11.DefaultValue(), ""); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_06).DefaultValue(),
EXPECT_EQ(FLAGS_test_flag_12.DefaultValue(), "10m"); "40000");
EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_07).DefaultValue(),
"-1234567");
EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_08).DefaultValue(),
"9876543");
EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_09).DefaultValue(),
"-9.876e-50");
EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_10).DefaultValue(),
"1.234e+12");
EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_11).DefaultValue(),
"");
EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_12).DefaultValue(),
"10m");
EXPECT_EQ(FLAGS_test_flag_01.CurrentValue(), "true"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_01).CurrentValue(),
EXPECT_EQ(FLAGS_test_flag_02.CurrentValue(), "1234"); "true");
EXPECT_EQ(FLAGS_test_flag_03.CurrentValue(), "-34"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_02).CurrentValue(),
EXPECT_EQ(FLAGS_test_flag_04.CurrentValue(), "189"); "1234");
EXPECT_EQ(FLAGS_test_flag_05.CurrentValue(), "10765"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_03).CurrentValue(),
EXPECT_EQ(FLAGS_test_flag_06.CurrentValue(), "40000"); "-34");
EXPECT_EQ(FLAGS_test_flag_07.CurrentValue(), "-1234567"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_04).CurrentValue(),
EXPECT_EQ(FLAGS_test_flag_08.CurrentValue(), "9876543"); "189");
EXPECT_EQ(FLAGS_test_flag_09.CurrentValue(), "-9.876e-50"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_05).CurrentValue(),
EXPECT_EQ(FLAGS_test_flag_10.CurrentValue(), "1.234e+12"); "10765");
EXPECT_EQ(FLAGS_test_flag_11.CurrentValue(), ""); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_06).CurrentValue(),
EXPECT_EQ(FLAGS_test_flag_12.CurrentValue(), "10m"); "40000");
EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_07).CurrentValue(),
"-1234567");
EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_08).CurrentValue(),
"9876543");
EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_09).CurrentValue(),
"-9.876e-50");
EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_10).CurrentValue(),
"1.234e+12");
EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_11).CurrentValue(),
"");
EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_12).CurrentValue(),
"10m");
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), true); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), true);
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_02), 1234); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_02), 1234);
@ -386,12 +490,18 @@ ABSL_FLAG(NonTriviallyCopyableAggregate, test_flag_eb_06, {}, "");
namespace { namespace {
TEST_F(FlagTest, TestEmptyBracesDefault) { TEST_F(FlagTest, TestEmptyBracesDefault) {
EXPECT_EQ(FLAGS_test_flag_eb_01.DefaultValue(), "false"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_01).DefaultValue(),
EXPECT_EQ(FLAGS_test_flag_eb_02.DefaultValue(), "0"); "false");
EXPECT_EQ(FLAGS_test_flag_eb_03.DefaultValue(), "0"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_02).DefaultValue(),
EXPECT_EQ(FLAGS_test_flag_eb_04.DefaultValue(), "0"); "0");
EXPECT_EQ(FLAGS_test_flag_eb_05.DefaultValue(), ""); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_03).DefaultValue(),
EXPECT_EQ(FLAGS_test_flag_eb_06.DefaultValue(), "0"); "0");
EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_04).DefaultValue(),
"0");
EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_05).DefaultValue(),
"");
EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_06).DefaultValue(),
"0");
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_01), false); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_01), false);
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_02), 0); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_02), 0);
@ -445,29 +555,29 @@ TEST_F(FlagTest, TestGetSet) {
// -------------------------------------------------------------------- // --------------------------------------------------------------------
TEST_F(FlagTest, TestGetViaReflection) { TEST_F(FlagTest, TestGetViaReflection) {
auto* handle = flags::FindCommandLineFlag("test_flag_01"); auto* handle = absl::FindCommandLineFlag("test_flag_01");
EXPECT_EQ(*handle->TryGet<bool>(), true); EXPECT_EQ(*handle->TryGet<bool>(), true);
handle = flags::FindCommandLineFlag("test_flag_02"); handle = absl::FindCommandLineFlag("test_flag_02");
EXPECT_EQ(*handle->TryGet<int>(), 1234); EXPECT_EQ(*handle->TryGet<int>(), 1234);
handle = flags::FindCommandLineFlag("test_flag_03"); handle = absl::FindCommandLineFlag("test_flag_03");
EXPECT_EQ(*handle->TryGet<int16_t>(), -34); EXPECT_EQ(*handle->TryGet<int16_t>(), -34);
handle = flags::FindCommandLineFlag("test_flag_04"); handle = absl::FindCommandLineFlag("test_flag_04");
EXPECT_EQ(*handle->TryGet<uint16_t>(), 189); EXPECT_EQ(*handle->TryGet<uint16_t>(), 189);
handle = flags::FindCommandLineFlag("test_flag_05"); handle = absl::FindCommandLineFlag("test_flag_05");
EXPECT_EQ(*handle->TryGet<int32_t>(), 10765); EXPECT_EQ(*handle->TryGet<int32_t>(), 10765);
handle = flags::FindCommandLineFlag("test_flag_06"); handle = absl::FindCommandLineFlag("test_flag_06");
EXPECT_EQ(*handle->TryGet<uint32_t>(), 40000); EXPECT_EQ(*handle->TryGet<uint32_t>(), 40000);
handle = flags::FindCommandLineFlag("test_flag_07"); handle = absl::FindCommandLineFlag("test_flag_07");
EXPECT_EQ(*handle->TryGet<int64_t>(), -1234567); EXPECT_EQ(*handle->TryGet<int64_t>(), -1234567);
handle = flags::FindCommandLineFlag("test_flag_08"); handle = absl::FindCommandLineFlag("test_flag_08");
EXPECT_EQ(*handle->TryGet<uint64_t>(), 9876543); EXPECT_EQ(*handle->TryGet<uint64_t>(), 9876543);
handle = flags::FindCommandLineFlag("test_flag_09"); handle = absl::FindCommandLineFlag("test_flag_09");
EXPECT_NEAR(*handle->TryGet<double>(), -9.876e-50, 1e-55); EXPECT_NEAR(*handle->TryGet<double>(), -9.876e-50, 1e-55);
handle = flags::FindCommandLineFlag("test_flag_10"); handle = absl::FindCommandLineFlag("test_flag_10");
EXPECT_NEAR(*handle->TryGet<float>(), 1.234e12f, 1e5f); EXPECT_NEAR(*handle->TryGet<float>(), 1.234e12f, 1e5f);
handle = flags::FindCommandLineFlag("test_flag_11"); handle = absl::FindCommandLineFlag("test_flag_11");
EXPECT_EQ(*handle->TryGet<std::string>(), ""); EXPECT_EQ(*handle->TryGet<std::string>(), "");
handle = flags::FindCommandLineFlag("test_flag_12"); handle = absl::FindCommandLineFlag("test_flag_12");
EXPECT_EQ(*handle->TryGet<absl::Duration>(), absl::Minutes(10)); EXPECT_EQ(*handle->TryGet<absl::Duration>(), absl::Minutes(10));
} }
@ -501,8 +611,9 @@ namespace {
#if !ABSL_FLAGS_STRIP_HELP #if !ABSL_FLAGS_STRIP_HELP
TEST_F(FlagTest, TestNonConstexprHelp) { TEST_F(FlagTest, TestNonConstexprHelp) {
EXPECT_EQ(FLAGS_test_flag_with_non_const_help.Help(), EXPECT_EQ(
"test flag non const help"); absl::GetFlagReflectionHandle(FLAGS_test_flag_with_non_const_help).Help(),
"test flag non const help");
} }
#endif //! ABSL_FLAGS_STRIP_HELP #endif //! ABSL_FLAGS_STRIP_HELP
@ -704,14 +815,15 @@ ABSL_RETIRED_FLAG(std::string, old_str_flag, "", absl::StrCat("old ", "descr"));
namespace { namespace {
TEST_F(FlagTest, TestRetiredFlagRegistration) { TEST_F(FlagTest, TestRetiredFlagRegistration) {
bool is_bool = false; auto* handle = absl::FindCommandLineFlag("old_bool_flag");
EXPECT_TRUE(flags::IsRetiredFlag("old_bool_flag", &is_bool)); EXPECT_TRUE(handle->IsOfType<bool>());
EXPECT_TRUE(is_bool); EXPECT_TRUE(handle->IsRetired());
EXPECT_TRUE(flags::IsRetiredFlag("old_int_flag", &is_bool)); handle = absl::FindCommandLineFlag("old_int_flag");
EXPECT_FALSE(is_bool); EXPECT_TRUE(handle->IsOfType<int>());
EXPECT_TRUE(flags::IsRetiredFlag("old_str_flag", &is_bool)); EXPECT_TRUE(handle->IsRetired());
EXPECT_FALSE(is_bool); handle = absl::FindCommandLineFlag("old_str_flag");
EXPECT_FALSE(flags::IsRetiredFlag("some_other_flag", &is_bool)); EXPECT_TRUE(handle->IsOfType<std::string>());
EXPECT_TRUE(handle->IsRetired());
} }
} // namespace } // namespace

View file

@ -16,14 +16,8 @@
#ifndef ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ #ifndef ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
#define ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ #define ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
#include <memory>
#include <string>
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/base/internal/fast_type_id.h" #include "absl/base/internal/fast_type_id.h"
#include "absl/base/macros.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
@ -34,7 +28,7 @@ namespace flags_internal {
// cases this id is enough to uniquely identify the flag's value type. In a few // cases this id is enough to uniquely identify the flag's value type. In a few
// cases we'll have to resort to using actual RTTI implementation if it is // cases we'll have to resort to using actual RTTI implementation if it is
// available. // available.
using FlagFastTypeId = base_internal::FastTypeIdType; using FlagFastTypeId = absl::base_internal::FastTypeIdType;
// Options that control SetCommandLineOptionWithMode. // Options that control SetCommandLineOptionWithMode.
enum FlagSettingMode { enum FlagSettingMode {
@ -67,117 +61,6 @@ class FlagStateInterface {
virtual void Restore() const = 0; virtual void Restore() const = 0;
}; };
// Holds all information for a flag.
class CommandLineFlag {
public:
constexpr CommandLineFlag() = default;
// Not copyable/assignable.
CommandLineFlag(const CommandLineFlag&) = delete;
CommandLineFlag& operator=(const CommandLineFlag&) = delete;
// Non-polymorphic access methods.
// Return true iff flag has type T.
template <typename T>
inline bool IsOfType() const {
return TypeId() == base_internal::FastTypeId<T>();
}
// Attempts to retrieve the flag value. Returns value on success,
// absl::nullopt otherwise.
template <typename T>
absl::optional<T> TryGet() const {
if (IsRetired() || !IsOfType<T>()) {
return absl::nullopt;
}
// Implementation notes:
//
// We are wrapping a union around the value of `T` to serve three purposes:
//
// 1. `U.value` has correct size and alignment for a value of type `T`
// 2. The `U.value` constructor is not invoked since U's constructor does
// not do it explicitly.
// 3. The `U.value` destructor is invoked since U's destructor does it
// explicitly. This makes `U` a kind of RAII wrapper around non default
// constructible value of T, which is destructed when we leave the
// scope. We do need to destroy U.value, which is constructed by
// CommandLineFlag::Read even though we left it in a moved-from state
// after std::move.
//
// All of this serves to avoid requiring `T` being default constructible.
union U {
T value;
U() {}
~U() { value.~T(); }
};
U u;
Read(&u.value);
return std::move(u.value);
}
// Polymorphic access methods
// Returns name of this flag.
virtual absl::string_view Name() const = 0;
// Returns name of the file where this flag is defined.
virtual std::string Filename() const = 0;
// Returns help message associated with this flag.
virtual std::string Help() const = 0;
// Returns true iff this object corresponds to retired flag.
virtual bool IsRetired() const;
virtual std::string DefaultValue() const = 0;
virtual std::string CurrentValue() const = 0;
// Sets the value of the flag based on specified string `value`. If the flag
// was successfully set to new value, it returns true. Otherwise, sets `error`
// to indicate the error, leaves the flag unchanged, and returns false.
bool ParseFrom(absl::string_view value, std::string* error);
protected:
~CommandLineFlag() = default;
private:
friend class PrivateHandleAccessor;
// Sets the value of the flag based on specified string `value`. If the flag
// was successfully set to new value, it returns true. Otherwise, sets `error`
// to indicate the error, leaves the flag unchanged, and returns false. There
// are three ways to set the flag's value:
// * Update the current flag value
// * Update the flag's default value
// * Update the current flag value if it was never set before
// The mode is selected based on `set_mode` parameter.
virtual bool ParseFrom(absl::string_view value,
flags_internal::FlagSettingMode set_mode,
flags_internal::ValueSource source,
std::string* error) = 0;
// Returns id of the flag's value type.
virtual FlagFastTypeId TypeId() const = 0;
// Interface to save flag to some persistent state. Returns current flag state
// or nullptr if flag does not support saving and restoring a state.
virtual std::unique_ptr<FlagStateInterface> SaveState() = 0;
// Copy-construct a new value of the flag's type in a memory referenced by
// the dst based on the current flag's value.
virtual void Read(void* dst) const = 0;
// To be deleted. Used to return true if flag's current value originated from
// command line.
virtual bool IsSpecifiedOnCommandLine() const = 0;
// Validates supplied value usign validator or parseflag routine
virtual bool ValidateInputValue(absl::string_view value) const = 0;
// Checks that flags default value can be converted to string and back to the
// flag's value type.
virtual void CheckDefaultValueParsingRoundtrip() const = 0;
};
} // namespace flags_internal } // namespace flags_internal
ABSL_NAMESPACE_END ABSL_NAMESPACE_END
} // namespace absl } // namespace absl

View file

@ -15,22 +15,26 @@
#include "absl/flags/internal/flag.h" #include "absl/flags/internal/flag.h"
#include <assert.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <array>
#include <atomic> #include <atomic>
#include <memory> #include <memory>
#include <new>
#include <string> #include <string>
#include <vector> #include <typeinfo>
#include "absl/base/attributes.h" #include "absl/base/call_once.h"
#include "absl/base/casts.h" #include "absl/base/casts.h"
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/base/const_init.h"
#include "absl/base/optimization.h" #include "absl/base/optimization.h"
#include "absl/flags/config.h"
#include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/usage_config.h" #include "absl/flags/usage_config.h"
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h" #include "absl/synchronization/mutex.h"
@ -63,14 +67,14 @@ bool ShouldValidateFlagValue(FlagFastTypeId flag_type_id) {
// need to acquire these locks themselves. // need to acquire these locks themselves.
class MutexRelock { class MutexRelock {
public: public:
explicit MutexRelock(absl::Mutex* mu) : mu_(mu) { mu_->Unlock(); } explicit MutexRelock(absl::Mutex& mu) : mu_(mu) { mu_.Unlock(); }
~MutexRelock() { mu_->Lock(); } ~MutexRelock() { mu_.Lock(); }
MutexRelock(const MutexRelock&) = delete; MutexRelock(const MutexRelock&) = delete;
MutexRelock& operator=(const MutexRelock&) = delete; MutexRelock& operator=(const MutexRelock&) = delete;
private: private:
absl::Mutex* mu_; absl::Mutex& mu_;
}; };
} // namespace } // namespace
@ -83,7 +87,7 @@ class FlagImpl;
class FlagState : public flags_internal::FlagStateInterface { class FlagState : public flags_internal::FlagStateInterface {
public: public:
template <typename V> template <typename V>
FlagState(FlagImpl* flag_impl, const V& v, bool modified, FlagState(FlagImpl& flag_impl, const V& v, bool modified,
bool on_command_line, int64_t counter) bool on_command_line, int64_t counter)
: flag_impl_(flag_impl), : flag_impl_(flag_impl),
value_(v), value_(v),
@ -92,9 +96,9 @@ class FlagState : public flags_internal::FlagStateInterface {
counter_(counter) {} counter_(counter) {}
~FlagState() override { ~FlagState() override {
if (flag_impl_->ValueStorageKind() != FlagValueStorageKind::kAlignedBuffer) if (flag_impl_.ValueStorageKind() != FlagValueStorageKind::kAlignedBuffer)
return; return;
flags_internal::Delete(flag_impl_->op_, value_.heap_allocated); flags_internal::Delete(flag_impl_.op_, value_.heap_allocated);
} }
private: private:
@ -102,15 +106,15 @@ class FlagState : public flags_internal::FlagStateInterface {
// Restores the flag to the saved state. // Restores the flag to the saved state.
void Restore() const override { void Restore() const override {
if (!flag_impl_->RestoreState(*this)) return; if (!flag_impl_.RestoreState(*this)) return;
ABSL_INTERNAL_LOG( ABSL_INTERNAL_LOG(INFO,
INFO, absl::StrCat("Restore saved value of ", flag_impl_->Name(), absl::StrCat("Restore saved value of ", flag_impl_.Name(),
" to: ", flag_impl_->CurrentValue())); " to: ", flag_impl_.CurrentValue()));
} }
// Flag and saved flag data. // Flag and saved flag data.
FlagImpl* flag_impl_; FlagImpl& flag_impl_;
union SavedValue { union SavedValue {
explicit SavedValue(void* v) : heap_allocated(v) {} explicit SavedValue(void* v) : heap_allocated(v) {}
explicit SavedValue(int64_t v) : one_word(v) {} explicit SavedValue(int64_t v) : one_word(v) {}
@ -327,7 +331,7 @@ void FlagImpl::InvokeCallback() const {
// and it also can be different by the time the callback invocation is // and it also can be different by the time the callback invocation is
// completed. Requires that *primary_lock be held in exclusive mode; it may be // completed. Requires that *primary_lock be held in exclusive mode; it may be
// released and reacquired by the implementation. // released and reacquired by the implementation.
MutexRelock relock(DataGuard()); MutexRelock relock(*DataGuard());
absl::MutexLock lock(&callback_->guard); absl::MutexLock lock(&callback_->guard);
cb(); cb();
} }
@ -340,17 +344,17 @@ std::unique_ptr<FlagStateInterface> FlagImpl::SaveState() {
switch (ValueStorageKind()) { switch (ValueStorageKind()) {
case FlagValueStorageKind::kAlignedBuffer: { case FlagValueStorageKind::kAlignedBuffer: {
return absl::make_unique<FlagState>( return absl::make_unique<FlagState>(
this, flags_internal::Clone(op_, AlignedBufferValue()), modified, *this, flags_internal::Clone(op_, AlignedBufferValue()), modified,
on_command_line, counter_); on_command_line, counter_);
} }
case FlagValueStorageKind::kOneWordAtomic: { case FlagValueStorageKind::kOneWordAtomic: {
return absl::make_unique<FlagState>( return absl::make_unique<FlagState>(
this, OneWordValue().load(std::memory_order_acquire), modified, *this, OneWordValue().load(std::memory_order_acquire), modified,
on_command_line, counter_); on_command_line, counter_);
} }
case FlagValueStorageKind::kTwoWordsAtomic: { case FlagValueStorageKind::kTwoWordsAtomic: {
return absl::make_unique<FlagState>( return absl::make_unique<FlagState>(
this, TwoWordsValue().load(std::memory_order_acquire), modified, *this, TwoWordsValue().load(std::memory_order_acquire), modified,
on_command_line, counter_); on_command_line, counter_);
} }
} }
@ -411,14 +415,14 @@ std::atomic<AlignedTwoWords>& FlagImpl::TwoWordsValue() const {
// parsed value. In case if any error is encountered in either step, the error // parsed value. In case if any error is encountered in either step, the error
// message is stored in 'err' // message is stored in 'err'
std::unique_ptr<void, DynValueDeleter> FlagImpl::TryParse( std::unique_ptr<void, DynValueDeleter> FlagImpl::TryParse(
absl::string_view value, std::string* err) const { absl::string_view value, std::string& err) const {
std::unique_ptr<void, DynValueDeleter> tentative_value = MakeInitValue(); std::unique_ptr<void, DynValueDeleter> tentative_value = MakeInitValue();
std::string parse_err; std::string parse_err;
if (!flags_internal::Parse(op_, value, tentative_value.get(), &parse_err)) { if (!flags_internal::Parse(op_, value, tentative_value.get(), &parse_err)) {
absl::string_view err_sep = parse_err.empty() ? "" : "; "; absl::string_view err_sep = parse_err.empty() ? "" : "; ";
*err = absl::StrCat("Illegal value '", value, "' specified for flag '", err = absl::StrCat("Illegal value '", value, "' specified for flag '",
Name(), "'", err_sep, parse_err); Name(), "'", err_sep, parse_err);
return nullptr; return nullptr;
} }
@ -474,7 +478,7 @@ void FlagImpl::Write(const void* src) {
// * Update the current flag value if it was never set before // * Update the current flag value if it was never set before
// The mode is selected based on 'set_mode' parameter. // The mode is selected based on 'set_mode' parameter.
bool FlagImpl::ParseFrom(absl::string_view value, FlagSettingMode set_mode, bool FlagImpl::ParseFrom(absl::string_view value, FlagSettingMode set_mode,
ValueSource source, std::string* err) { ValueSource source, std::string& err) {
absl::MutexLock l(DataGuard()); absl::MutexLock l(DataGuard());
switch (set_mode) { switch (set_mode) {

View file

@ -16,31 +16,36 @@
#ifndef ABSL_FLAGS_INTERNAL_FLAG_H_ #ifndef ABSL_FLAGS_INTERNAL_FLAG_H_
#define ABSL_FLAGS_INTERNAL_FLAG_H_ #define ABSL_FLAGS_INTERNAL_FLAG_H_
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <atomic> #include <atomic>
#include <cstring> #include <cstring>
#include <memory> #include <memory>
#include <new>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#include <typeinfo> #include <typeinfo>
#include "absl/base/attributes.h"
#include "absl/base/call_once.h" #include "absl/base/call_once.h"
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/base/optimization.h"
#include "absl/base/thread_annotations.h" #include "absl/base/thread_annotations.h"
#include "absl/flags/commandlineflag.h"
#include "absl/flags/config.h" #include "absl/flags/config.h"
#include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/registry.h" #include "absl/flags/internal/registry.h"
#include "absl/flags/marshalling.h" #include "absl/flags/marshalling.h"
#include "absl/memory/memory.h"
#include "absl/meta/type_traits.h" #include "absl/meta/type_traits.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h" #include "absl/synchronization/mutex.h"
#include "absl/utility/utility.h"
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
///////////////////////////////////////////////////////////////////////////////
// Forward declaration of absl::Flag<T> public API. // Forward declaration of absl::Flag<T> public API.
namespace flags_internal { namespace flags_internal {
template <typename T> template <typename T>
@ -64,12 +69,15 @@ void SetFlag(absl::Flag<T>* flag, const T& v);
template <typename T, typename V> template <typename T, typename V>
void SetFlag(absl::Flag<T>* flag, const V& v); void SetFlag(absl::Flag<T>* flag, const V& v);
namespace flags_internal { template <typename U>
const CommandLineFlag& GetFlagReflectionHandle(const absl::Flag<U>& f);
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Flag value type operations, eg., parsing, copying, etc. are provided // Flag value type operations, eg., parsing, copying, etc. are provided
// by function specific to that type with a signature matching FlagOpFn. // by function specific to that type with a signature matching FlagOpFn.
namespace flags_internal {
enum class FlagOp { enum class FlagOp {
kAlloc, kAlloc,
kDelete, kDelete,
@ -168,6 +176,28 @@ inline const std::type_info* GenRuntimeTypeId() {
// cases. // cases.
using HelpGenFunc = std::string (*)(); using HelpGenFunc = std::string (*)();
template <size_t N>
struct FixedCharArray {
char value[N];
template <size_t... I>
static constexpr FixedCharArray<N> FromLiteralString(
absl::string_view str, absl::index_sequence<I...>) {
return (void)str, FixedCharArray<N>({{str[I]..., '\0'}});
}
};
template <typename Gen, size_t N = Gen::Value().size()>
constexpr FixedCharArray<N + 1> HelpStringAsArray(int) {
return FixedCharArray<N + 1>::FromLiteralString(
Gen::Value(), absl::make_index_sequence<N>{});
}
template <typename Gen>
constexpr std::false_type HelpStringAsArray(char) {
return std::false_type{};
}
union FlagHelpMsg { union FlagHelpMsg {
constexpr explicit FlagHelpMsg(const char* help_msg) : literal(help_msg) {} constexpr explicit FlagHelpMsg(const char* help_msg) : literal(help_msg) {}
constexpr explicit FlagHelpMsg(HelpGenFunc help_gen) : gen_func(help_gen) {} constexpr explicit FlagHelpMsg(HelpGenFunc help_gen) : gen_func(help_gen) {}
@ -185,40 +215,28 @@ struct FlagHelpArg {
extern const char kStrippedFlagHelp[]; extern const char kStrippedFlagHelp[];
// HelpConstexprWrap is used by struct AbslFlagHelpGenFor##name generated by
// ABSL_FLAG macro. It is only used to silence the compiler in the case where
// help message expression is not constexpr and does not have type const char*.
// If help message expression is indeed constexpr const char* HelpConstexprWrap
// is just a trivial identity function.
template <typename T>
const char* HelpConstexprWrap(const T&) {
return nullptr;
}
constexpr const char* HelpConstexprWrap(const char* p) { return p; }
constexpr const char* HelpConstexprWrap(char* p) { return p; }
// These two HelpArg overloads allows us to select at compile time one of two // These two HelpArg overloads allows us to select at compile time one of two
// way to pass Help argument to absl::Flag. We'll be passing // way to pass Help argument to absl::Flag. We'll be passing
// AbslFlagHelpGenFor##name as T and integer 0 as a single argument to prefer // AbslFlagHelpGenFor##name as Gen and integer 0 as a single argument to prefer
// first overload if possible. If T::Const is evaluatable on constexpr // first overload if possible. If help message is evaluatable on constexpr
// context (see non template int parameter below) we'll choose first overload. // context We'll be able to make FixedCharArray out of it and we'll choose first
// In this case the help message expression is immediately evaluated and is used // overload. In this case the help message expression is immediately evaluated
// to construct the absl::Flag. No additionl code is generated by ABSL_FLAG. // and is used to construct the absl::Flag. No additionl code is generated by
// Otherwise SFINAE kicks in and first overload is dropped from the // ABSL_FLAG Otherwise SFINAE kicks in and first overload is dropped from the
// consideration, in which case the second overload will be used. The second // consideration, in which case the second overload will be used. The second
// overload does not attempt to evaluate the help message expression // overload does not attempt to evaluate the help message expression
// immediately and instead delays the evaluation by returing the function // immediately and instead delays the evaluation by returing the function
// pointer (&T::NonConst) genering the help message when necessary. This is // pointer (&T::NonConst) genering the help message when necessary. This is
// evaluatable in constexpr context, but the cost is an extra function being // evaluatable in constexpr context, but the cost is an extra function being
// generated in the ABSL_FLAG code. // generated in the ABSL_FLAG code.
template <typename T, int = (T::Const(), 1)> template <typename Gen, size_t N>
constexpr FlagHelpArg HelpArg(int) { constexpr FlagHelpArg HelpArg(const FixedCharArray<N>& value) {
return {FlagHelpMsg(T::Const()), FlagHelpKind::kLiteral}; return {FlagHelpMsg(value.value), FlagHelpKind::kLiteral};
} }
template <typename T> template <typename Gen>
constexpr FlagHelpArg HelpArg(char) { constexpr FlagHelpArg HelpArg(std::false_type) {
return {FlagHelpMsg(&T::NonConst), FlagHelpKind::kGenFunc}; return {FlagHelpMsg(&Gen::NonConst), FlagHelpKind::kGenFunc};
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -364,31 +382,31 @@ struct FlagValue;
template <typename T> template <typename T>
struct FlagValue<T, FlagValueStorageKind::kAlignedBuffer> { struct FlagValue<T, FlagValueStorageKind::kAlignedBuffer> {
bool Get(T*) const { return false; } bool Get(T&) const { return false; }
alignas(T) char value[sizeof(T)]; alignas(T) char value[sizeof(T)];
}; };
template <typename T> template <typename T>
struct FlagValue<T, FlagValueStorageKind::kOneWordAtomic> : FlagOneWordValue { struct FlagValue<T, FlagValueStorageKind::kOneWordAtomic> : FlagOneWordValue {
bool Get(T* dst) const { bool Get(T& dst) const {
int64_t one_word_val = value.load(std::memory_order_acquire); int64_t one_word_val = value.load(std::memory_order_acquire);
if (ABSL_PREDICT_FALSE(one_word_val == UninitializedFlagValue())) { if (ABSL_PREDICT_FALSE(one_word_val == UninitializedFlagValue())) {
return false; return false;
} }
std::memcpy(dst, static_cast<const void*>(&one_word_val), sizeof(T)); std::memcpy(&dst, static_cast<const void*>(&one_word_val), sizeof(T));
return true; return true;
} }
}; };
template <typename T> template <typename T>
struct FlagValue<T, FlagValueStorageKind::kTwoWordsAtomic> : FlagTwoWordsValue { struct FlagValue<T, FlagValueStorageKind::kTwoWordsAtomic> : FlagTwoWordsValue {
bool Get(T* dst) const { bool Get(T& dst) const {
AlignedTwoWords two_words_val = value.load(std::memory_order_acquire); AlignedTwoWords two_words_val = value.load(std::memory_order_acquire);
if (ABSL_PREDICT_FALSE(!two_words_val.IsInitialized())) { if (ABSL_PREDICT_FALSE(!two_words_val.IsInitialized())) {
return false; return false;
} }
std::memcpy(dst, static_cast<const void*>(&two_words_val), sizeof(T)); std::memcpy(&dst, static_cast<const void*>(&two_words_val), sizeof(T));
return true; return true;
} }
}; };
@ -419,7 +437,7 @@ struct DynValueDeleter {
class FlagState; class FlagState;
class FlagImpl final : public flags_internal::CommandLineFlag { class FlagImpl final : public CommandLineFlag {
public: public:
constexpr FlagImpl(const char* name, const char* filename, FlagOpFn op, constexpr FlagImpl(const char* name, const char* filename, FlagOpFn op,
FlagHelpArg help, FlagValueStorageKind value_kind, FlagHelpArg help, FlagValueStorageKind value_kind,
@ -492,7 +510,7 @@ class FlagImpl final : public flags_internal::CommandLineFlag {
// Attempts to parse supplied `value` string. If parsing is successful, // Attempts to parse supplied `value` string. If parsing is successful,
// returns new value. Otherwise returns nullptr. // returns new value. Otherwise returns nullptr.
std::unique_ptr<void, DynValueDeleter> TryParse(absl::string_view value, std::unique_ptr<void, DynValueDeleter> TryParse(absl::string_view value,
std::string* err) const std::string& err) const
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
// Stores the flag value based on the pointer to the source. // Stores the flag value based on the pointer to the source.
void StoreValue(const void* src) ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); void StoreValue(const void* src) ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
@ -534,7 +552,7 @@ class FlagImpl final : public flags_internal::CommandLineFlag {
ABSL_LOCKS_EXCLUDED(*DataGuard()); ABSL_LOCKS_EXCLUDED(*DataGuard());
bool ParseFrom(absl::string_view value, FlagSettingMode set_mode, bool ParseFrom(absl::string_view value, FlagSettingMode set_mode,
ValueSource source, std::string* error) override ValueSource source, std::string& error) override
ABSL_LOCKS_EXCLUDED(*DataGuard()); ABSL_LOCKS_EXCLUDED(*DataGuard());
// Immutable flag's state. // Immutable flag's state.
@ -641,7 +659,7 @@ class Flag {
impl_.AssertValidType(base_internal::FastTypeId<T>(), &GenRuntimeTypeId<T>); impl_.AssertValidType(base_internal::FastTypeId<T>(), &GenRuntimeTypeId<T>);
#endif #endif
if (!value_.Get(&u.value)) impl_.Read(&u.value); if (!value_.Get(u.value)) impl_.Read(&u.value);
return std::move(u.value); return std::move(u.value);
} }
void Set(const T& v) { void Set(const T& v) {
@ -649,6 +667,13 @@ class Flag {
impl_.Write(&v); impl_.Write(&v);
} }
template <typename U>
friend const CommandLineFlag& absl::GetFlagReflectionHandle(
const absl::Flag<U>& f);
// Access to the reflection.
const CommandLineFlag& Reflect() const { return impl_; }
// Flag's data // Flag's data
// The implementation depends on value_ field to be placed exactly after the // The implementation depends on value_ field to be placed exactly after the
// impl_ field, so that impl_ can figure out the offset to the value and // impl_ field, so that impl_ can figure out the offset to the value and
@ -720,12 +745,12 @@ struct FlagRegistrarEmpty {};
template <typename T, bool do_register> template <typename T, bool do_register>
class FlagRegistrar { class FlagRegistrar {
public: public:
explicit FlagRegistrar(Flag<T>* flag) : flag_(flag) { explicit FlagRegistrar(Flag<T>& flag) : flag_(flag) {
if (do_register) flags_internal::RegisterCommandLineFlag(&flag_->impl_); if (do_register) flags_internal::RegisterCommandLineFlag(flag_.impl_);
} }
FlagRegistrar OnUpdate(FlagCallbackFunc cb) && { FlagRegistrar OnUpdate(FlagCallbackFunc cb) && {
flag_->impl_.SetCallback(cb); flag_.impl_.SetCallback(cb);
return *this; return *this;
} }
@ -735,7 +760,7 @@ class FlagRegistrar {
operator FlagRegistrarEmpty() const { return {}; } // NOLINT operator FlagRegistrarEmpty() const { return {}; } // NOLINT
private: private:
Flag<T>* flag_; // Flag being registered (not owned). Flag<T>& flag_; // Flag being registered (not owned).
}; };
} // namespace flags_internal } // namespace flags_internal

View file

@ -21,6 +21,7 @@
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/flags/declare.h" #include "absl/flags/declare.h"
#include "absl/strings/string_view.h"
ABSL_DECLARE_FLAG(std::vector<std::string>, flagfile); ABSL_DECLARE_FLAG(std::vector<std::string>, flagfile);
ABSL_DECLARE_FLAG(std::vector<std::string>, fromenv); ABSL_DECLARE_FLAG(std::vector<std::string>, fromenv);

View file

@ -17,7 +17,6 @@
#define ABSL_FLAGS_INTERNAL_PATH_UTIL_H_ #define ABSL_FLAGS_INTERNAL_PATH_UTIL_H_
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/strings/match.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
namespace absl { namespace absl {

View file

@ -15,6 +15,14 @@
#include "absl/flags/internal/private_handle_accessor.h" #include "absl/flags/internal/private_handle_accessor.h"
#include <memory>
#include <string>
#include "absl/base/config.h"
#include "absl/flags/commandlineflag.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/strings/string_view.h"
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
namespace flags_internal { namespace flags_internal {
@ -24,8 +32,8 @@ FlagFastTypeId PrivateHandleAccessor::TypeId(const CommandLineFlag& flag) {
} }
std::unique_ptr<FlagStateInterface> PrivateHandleAccessor::SaveState( std::unique_ptr<FlagStateInterface> PrivateHandleAccessor::SaveState(
CommandLineFlag* flag) { CommandLineFlag& flag) {
return flag->SaveState(); return flag.SaveState();
} }
bool PrivateHandleAccessor::IsSpecifiedOnCommandLine( bool PrivateHandleAccessor::IsSpecifiedOnCommandLine(
@ -43,12 +51,12 @@ void PrivateHandleAccessor::CheckDefaultValueParsingRoundtrip(
flag.CheckDefaultValueParsingRoundtrip(); flag.CheckDefaultValueParsingRoundtrip();
} }
bool PrivateHandleAccessor::ParseFrom(CommandLineFlag* flag, bool PrivateHandleAccessor::ParseFrom(CommandLineFlag& flag,
absl::string_view value, absl::string_view value,
flags_internal::FlagSettingMode set_mode, flags_internal::FlagSettingMode set_mode,
flags_internal::ValueSource source, flags_internal::ValueSource source,
std::string* error) { std::string& error) {
return flag->ParseFrom(value, set_mode, source, error); return flag.ParseFrom(value, set_mode, source, error);
} }
} // namespace flags_internal } // namespace flags_internal

View file

@ -16,7 +16,13 @@
#ifndef ABSL_FLAGS_INTERNAL_PRIVATE_HANDLE_ACCESSOR_H_ #ifndef ABSL_FLAGS_INTERNAL_PRIVATE_HANDLE_ACCESSOR_H_
#define ABSL_FLAGS_INTERNAL_PRIVATE_HANDLE_ACCESSOR_H_ #define ABSL_FLAGS_INTERNAL_PRIVATE_HANDLE_ACCESSOR_H_
#include <memory>
#include <string>
#include "absl/base/config.h"
#include "absl/flags/commandlineflag.h"
#include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/commandlineflag.h"
#include "absl/strings/string_view.h"
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
@ -31,7 +37,7 @@ class PrivateHandleAccessor {
static FlagFastTypeId TypeId(const CommandLineFlag& flag); static FlagFastTypeId TypeId(const CommandLineFlag& flag);
// Access to CommandLineFlag::SaveState. // Access to CommandLineFlag::SaveState.
static std::unique_ptr<FlagStateInterface> SaveState(CommandLineFlag* flag); static std::unique_ptr<FlagStateInterface> SaveState(CommandLineFlag& flag);
// Access to CommandLineFlag::IsSpecifiedOnCommandLine. // Access to CommandLineFlag::IsSpecifiedOnCommandLine.
static bool IsSpecifiedOnCommandLine(const CommandLineFlag& flag); static bool IsSpecifiedOnCommandLine(const CommandLineFlag& flag);
@ -43,9 +49,9 @@ class PrivateHandleAccessor {
// Access to CommandLineFlag::CheckDefaultValueParsingRoundtrip. // Access to CommandLineFlag::CheckDefaultValueParsingRoundtrip.
static void CheckDefaultValueParsingRoundtrip(const CommandLineFlag& flag); static void CheckDefaultValueParsingRoundtrip(const CommandLineFlag& flag);
static bool ParseFrom(CommandLineFlag* flag, absl::string_view value, static bool ParseFrom(CommandLineFlag& flag, absl::string_view value,
flags_internal::FlagSettingMode set_mode, flags_internal::FlagSettingMode set_mode,
flags_internal::ValueSource source, std::string* error); flags_internal::ValueSource source, std::string& error);
}; };
} // namespace flags_internal } // namespace flags_internal

View file

@ -25,7 +25,7 @@ namespace {
namespace flags = absl::flags_internal; namespace flags = absl::flags_internal;
TEST(FlagsPathUtilTest, TestInitialProgamName) { TEST(FlagsPathUtilTest, TestProgamNameInterfaces) {
flags::SetProgramInvocationName("absl/flags/program_name_test"); flags::SetProgramInvocationName("absl/flags/program_name_test");
std::string program_name = flags::ProgramInvocationName(); std::string program_name = flags::ProgramInvocationName();
for (char& c : program_name) for (char& c : program_name)
@ -43,9 +43,7 @@ TEST(FlagsPathUtilTest, TestInitialProgamName) {
EXPECT_TRUE(absl::EndsWith(program_name, expect_name)) << program_name; EXPECT_TRUE(absl::EndsWith(program_name, expect_name)) << program_name;
EXPECT_EQ(flags::ShortProgramInvocationName(), expect_basename); EXPECT_EQ(flags::ShortProgramInvocationName(), expect_basename);
}
TEST(FlagsPathUtilTest, TestProgamNameInterfaces) {
flags::SetProgramInvocationName("a/my_test"); flags::SetProgramInvocationName("a/my_test");
EXPECT_EQ(flags::ProgramInvocationName(), "a/my_test"); EXPECT_EQ(flags::ProgramInvocationName(), "a/my_test");

View file

@ -17,11 +17,9 @@
#define ABSL_FLAGS_INTERNAL_REGISTRY_H_ #define ABSL_FLAGS_INTERNAL_REGISTRY_H_
#include <functional> #include <functional>
#include <map>
#include <string>
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/base/macros.h" #include "absl/flags/commandlineflag.h"
#include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/commandlineflag.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
@ -32,19 +30,16 @@ namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
namespace flags_internal { namespace flags_internal {
CommandLineFlag* FindCommandLineFlag(absl::string_view name);
CommandLineFlag* FindRetiredFlag(absl::string_view name);
// Executes specified visitor for each non-retired flag in the registry. // Executes specified visitor for each non-retired flag in the registry.
// Requires the caller hold the registry lock. // Requires the caller hold the registry lock.
void ForEachFlagUnlocked(std::function<void(CommandLineFlag*)> visitor); void ForEachFlagUnlocked(std::function<void(CommandLineFlag&)> visitor);
// Executes specified visitor for each non-retired flag in the registry. While // Executes specified visitor for each non-retired flag in the registry. While
// callback are executed, the registry is locked and can't be changed. // callback are executed, the registry is locked and can't be changed.
void ForEachFlag(std::function<void(CommandLineFlag*)> visitor); void ForEachFlag(std::function<void(CommandLineFlag&)> visitor);
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
bool RegisterCommandLineFlag(CommandLineFlag*); bool RegisterCommandLineFlag(CommandLineFlag&);
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Retired registrations: // Retired registrations:
@ -87,36 +82,6 @@ inline bool RetiredFlag(const char* flag_name) {
return flags_internal::Retire(flag_name, base_internal::FastTypeId<T>()); return flags_internal::Retire(flag_name, base_internal::FastTypeId<T>());
} }
// If the flag is retired, returns true and indicates in |*type_is_bool|
// whether the type of the retired flag is a bool.
// Only to be called by code that needs to explicitly ignore retired flags.
bool IsRetiredFlag(absl::string_view name, bool* type_is_bool);
//-----------------------------------------------------------------------------
// Saves the states (value, default value, whether the user has set
// the flag, registered validators, etc) of all flags, and restores
// them when the FlagSaver is destroyed.
//
// This class is thread-safe. However, its destructor writes to
// exactly the set of flags that have changed value during its
// lifetime, so concurrent _direct_ access to those flags
// (i.e. FLAGS_foo instead of {Get,Set}CommandLineOption()) is unsafe.
class FlagSaver {
public:
FlagSaver();
~FlagSaver();
FlagSaver(const FlagSaver&) = delete;
void operator=(const FlagSaver&) = delete;
// Prevents saver from restoring the saved state of flags.
void Ignore();
private:
class FlagSaverImpl* impl_; // we use pimpl here to keep API steady
};
} // namespace flags_internal } // namespace flags_internal
ABSL_NAMESPACE_END ABSL_NAMESPACE_END
} // namespace absl } // namespace absl

View file

@ -1,86 +0,0 @@
//
// Copyright 2019 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
//
// https://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/flags/internal/type_erased.h"
#include <assert.h>
#include <string>
#include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/private_handle_accessor.h"
#include "absl/flags/internal/registry.h"
#include "absl/flags/usage_config.h"
#include "absl/strings/string_view.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace flags_internal {
bool GetCommandLineOption(absl::string_view name, std::string* value) {
if (name.empty()) return false;
assert(value);
CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name);
if (flag == nullptr || flag->IsRetired()) {
return false;
}
*value = flag->CurrentValue();
return true;
}
bool SetCommandLineOption(absl::string_view name, absl::string_view value) {
return SetCommandLineOptionWithMode(name, value,
flags_internal::SET_FLAGS_VALUE);
}
bool SetCommandLineOptionWithMode(absl::string_view name,
absl::string_view value,
FlagSettingMode set_mode) {
CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name);
if (!flag || flag->IsRetired()) return false;
std::string error;
if (!flags_internal::PrivateHandleAccessor::ParseFrom(
flag, value, set_mode, kProgrammaticChange, &error)) {
// Errors here are all of the form: the provided name was a recognized
// flag, but the value was invalid (bad type, or validation failed).
flags_internal::ReportUsageError(error, false);
return false;
}
return true;
}
// --------------------------------------------------------------------
bool IsValidFlagValue(absl::string_view name, absl::string_view value) {
CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name);
return flag != nullptr &&
(flag->IsRetired() ||
flags_internal::PrivateHandleAccessor::ValidateInputValue(*flag,
value));
}
// --------------------------------------------------------------------
} // namespace flags_internal
ABSL_NAMESPACE_END
} // namespace absl

View file

@ -1,79 +0,0 @@
//
// Copyright 2019 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
//
// https://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.
#ifndef ABSL_FLAGS_INTERNAL_TYPE_ERASED_H_
#define ABSL_FLAGS_INTERNAL_TYPE_ERASED_H_
#include <string>
#include "absl/base/config.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/registry.h"
#include "absl/strings/string_view.h"
// --------------------------------------------------------------------
// Registry interfaces operating on type erased handles.
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace flags_internal {
// If a flag named "name" exists, store its current value in *OUTPUT
// and return true. Else return false without changing *OUTPUT.
// Thread-safe.
bool GetCommandLineOption(absl::string_view name, std::string* value);
// Set the value of the flag named "name" to value. If successful,
// returns true. If not successful (e.g., the flag was not found or
// the value is not a valid value), returns false.
// Thread-safe.
bool SetCommandLineOption(absl::string_view name, absl::string_view value);
bool SetCommandLineOptionWithMode(absl::string_view name,
absl::string_view value,
FlagSettingMode set_mode);
//-----------------------------------------------------------------------------
// Returns true iff all of the following conditions are true:
// (a) "name" names a registered flag
// (b) "value" can be parsed succesfully according to the type of the flag
// (c) parsed value passes any validator associated with the flag
bool IsValidFlagValue(absl::string_view name, absl::string_view value);
//-----------------------------------------------------------------------------
// If a flag with specified "name" exists and has type T, store
// its current value in *dst and return true. Else return false
// without touching *dst. T must obey all of the requirements for
// types passed to DEFINE_FLAG.
template <typename T>
inline bool GetByName(absl::string_view name, T* dst) {
CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name);
if (!flag) return false;
if (auto val = flag->TryGet<T>()) {
*dst = *val;
return true;
}
return false;
}
} // namespace flags_internal
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_INTERNAL_TYPE_ERASED_H_

View file

@ -1,157 +0,0 @@
//
// Copyright 2019 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
//
// https://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/flags/internal/type_erased.h"
#include <memory>
#include <string>
#include "gtest/gtest.h"
#include "absl/flags/flag.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/registry.h"
#include "absl/flags/marshalling.h"
#include "absl/memory/memory.h"
ABSL_FLAG(int, int_flag, 1, "int_flag help");
ABSL_FLAG(std::string, string_flag, "dflt", "string_flag help");
ABSL_RETIRED_FLAG(bool, bool_retired_flag, false, "bool_retired_flag help");
namespace {
namespace flags = absl::flags_internal;
class TypeErasedTest : public testing::Test {
protected:
void SetUp() override { flag_saver_ = absl::make_unique<flags::FlagSaver>(); }
void TearDown() override { flag_saver_.reset(); }
private:
std::unique_ptr<flags::FlagSaver> flag_saver_;
};
// --------------------------------------------------------------------
TEST_F(TypeErasedTest, TestGetCommandLineOption) {
std::string value;
EXPECT_TRUE(flags::GetCommandLineOption("int_flag", &value));
EXPECT_EQ(value, "1");
EXPECT_TRUE(flags::GetCommandLineOption("string_flag", &value));
EXPECT_EQ(value, "dflt");
EXPECT_FALSE(flags::GetCommandLineOption("bool_retired_flag", &value));
EXPECT_FALSE(flags::GetCommandLineOption("unknown_flag", &value));
}
// --------------------------------------------------------------------
TEST_F(TypeErasedTest, TestSetCommandLineOption) {
EXPECT_TRUE(flags::SetCommandLineOption("int_flag", "101"));
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 101);
EXPECT_TRUE(flags::SetCommandLineOption("string_flag", "asdfgh"));
EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh");
EXPECT_FALSE(flags::SetCommandLineOption("bool_retired_flag", "true"));
EXPECT_FALSE(flags::SetCommandLineOption("unknown_flag", "true"));
}
// --------------------------------------------------------------------
TEST_F(TypeErasedTest, TestSetCommandLineOptionWithMode_SET_FLAGS_VALUE) {
EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "101",
flags::SET_FLAGS_VALUE));
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 101);
EXPECT_TRUE(flags::SetCommandLineOptionWithMode("string_flag", "asdfgh",
flags::SET_FLAGS_VALUE));
EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh");
EXPECT_FALSE(flags::SetCommandLineOptionWithMode("bool_retired_flag", "true",
flags::SET_FLAGS_VALUE));
EXPECT_FALSE(flags::SetCommandLineOptionWithMode("unknown_flag", "true",
flags::SET_FLAGS_VALUE));
}
// --------------------------------------------------------------------
TEST_F(TypeErasedTest, TestSetCommandLineOptionWithMode_SET_FLAG_IF_DEFAULT) {
EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "101",
flags::SET_FLAG_IF_DEFAULT));
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 101);
// This semantic is broken. We return true instead of false. Value is not
// updated.
EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "202",
flags::SET_FLAG_IF_DEFAULT));
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 101);
EXPECT_TRUE(flags::SetCommandLineOptionWithMode("string_flag", "asdfgh",
flags::SET_FLAG_IF_DEFAULT));
EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh");
EXPECT_FALSE(flags::SetCommandLineOptionWithMode("bool_retired_flag", "true",
flags::SET_FLAG_IF_DEFAULT));
EXPECT_FALSE(flags::SetCommandLineOptionWithMode("unknown_flag", "true",
flags::SET_FLAG_IF_DEFAULT));
}
// --------------------------------------------------------------------
TEST_F(TypeErasedTest, TestSetCommandLineOptionWithMode_SET_FLAGS_DEFAULT) {
EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "101",
flags::SET_FLAGS_DEFAULT));
// Set it again to ensure that resetting logic is covered.
EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "102",
flags::SET_FLAGS_DEFAULT));
EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "103",
flags::SET_FLAGS_DEFAULT));
EXPECT_TRUE(flags::SetCommandLineOptionWithMode("string_flag", "asdfgh",
flags::SET_FLAGS_DEFAULT));
EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh");
EXPECT_FALSE(flags::SetCommandLineOptionWithMode("bool_retired_flag", "true",
flags::SET_FLAGS_DEFAULT));
EXPECT_FALSE(flags::SetCommandLineOptionWithMode("unknown_flag", "true",
flags::SET_FLAGS_DEFAULT));
// This should be successfull, since flag is still is not set
EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "202",
flags::SET_FLAG_IF_DEFAULT));
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 202);
}
// --------------------------------------------------------------------
TEST_F(TypeErasedTest, TestIsValidFlagValue) {
EXPECT_TRUE(flags::IsValidFlagValue("int_flag", "57"));
EXPECT_TRUE(flags::IsValidFlagValue("int_flag", "-101"));
EXPECT_FALSE(flags::IsValidFlagValue("int_flag", "1.1"));
EXPECT_TRUE(flags::IsValidFlagValue("string_flag", "#%^#%^$%DGHDG$W%adsf"));
EXPECT_TRUE(flags::IsValidFlagValue("bool_retired_flag", "true"));
}
} // namespace

View file

@ -15,6 +15,8 @@
#include "absl/flags/internal/usage.h" #include "absl/flags/internal/usage.h"
#include <stdint.h>
#include <functional> #include <functional>
#include <map> #include <map>
#include <ostream> #include <ostream>
@ -23,8 +25,8 @@
#include <vector> #include <vector>
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/flags/commandlineflag.h"
#include "absl/flags/flag.h" #include "absl/flags/flag.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/flag.h" #include "absl/flags/internal/flag.h"
#include "absl/flags/internal/path_util.h" #include "absl/flags/internal/path_util.h"
#include "absl/flags/internal/private_handle_accessor.h" #include "absl/flags/internal/private_handle_accessor.h"
@ -107,8 +109,8 @@ class FlagHelpPrettyPrinter {
public: public:
// Pretty printer holds on to the std::ostream& reference to direct an output // Pretty printer holds on to the std::ostream& reference to direct an output
// to that stream. // to that stream.
FlagHelpPrettyPrinter(int max_line_len, std::ostream* out) FlagHelpPrettyPrinter(int max_line_len, std::ostream& out)
: out_(*out), : out_(out),
max_line_len_(max_line_len), max_line_len_(max_line_len),
line_len_(0), line_len_(0),
first_line_(true) {} first_line_(true) {}
@ -182,8 +184,7 @@ class FlagHelpPrettyPrinter {
bool first_line_; bool first_line_;
}; };
void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag, void FlagHelpHumanReadable(const CommandLineFlag& flag, std::ostream& out) {
std::ostream* out) {
FlagHelpPrettyPrinter printer(80, out); // Max line length is 80. FlagHelpPrettyPrinter printer(80, out); // Max line length is 80.
// Flag name. // Flag name.
@ -245,30 +246,28 @@ void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
// This map is used to output matching flags grouped by package and file // This map is used to output matching flags grouped by package and file
// name. // name.
std::map<std::string, std::map<std::string,
std::map<std::string, std::map<std::string, std::vector<const absl::CommandLineFlag*>>>
std::vector<const flags_internal::CommandLineFlag*>>>
matching_flags; matching_flags;
flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) { flags_internal::ForEachFlag([&](absl::CommandLineFlag& flag) {
std::string flag_filename = flag->Filename(); std::string flag_filename = flag.Filename();
// Ignore retired flags. // Ignore retired flags.
if (flag->IsRetired()) return; if (flag.IsRetired()) return;
// If the flag has been stripped, pretend that it doesn't exist. // If the flag has been stripped, pretend that it doesn't exist.
if (flag->Help() == flags_internal::kStrippedFlagHelp) return; if (flag.Help() == flags_internal::kStrippedFlagHelp) return;
// Make sure flag satisfies the filter // Make sure flag satisfies the filter
if (!filter_cb || !filter_cb(flag_filename)) return; if (!filter_cb || !filter_cb(flag_filename)) return;
matching_flags[std::string(flags_internal::Package(flag_filename))] matching_flags[std::string(flags_internal::Package(flag_filename))]
[flag_filename] [flag_filename]
.push_back(flag); .push_back(&flag);
}); });
absl::string_view absl::string_view package_separator; // controls blank lines between packages
package_separator; // controls blank lines between packages. absl::string_view file_separator; // controls blank lines between files
absl::string_view file_separator; // controls blank lines between files.
for (const auto& package : matching_flags) { for (const auto& package : matching_flags) {
if (format == HelpFormat::kHumanReadable) { if (format == HelpFormat::kHumanReadable) {
out << package_separator; out << package_separator;
@ -303,10 +302,10 @@ void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// Produces the help message describing specific flag. // Produces the help message describing specific flag.
void FlagHelp(std::ostream& out, const flags_internal::CommandLineFlag& flag, void FlagHelp(std::ostream& out, const CommandLineFlag& flag,
HelpFormat format) { HelpFormat format) {
if (format == HelpFormat::kHumanReadable) if (format == HelpFormat::kHumanReadable)
flags_internal::FlagHelpHumanReadable(flag, &out); flags_internal::FlagHelpHumanReadable(flag, out);
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------

View file

@ -20,8 +20,8 @@
#include <string> #include <string>
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/flags/commandlineflag.h"
#include "absl/flags/declare.h" #include "absl/flags/declare.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
// -------------------------------------------------------------------- // --------------------------------------------------------------------
@ -37,7 +37,7 @@ enum class HelpFormat {
}; };
// Outputs the help message describing specific flag. // Outputs the help message describing specific flag.
void FlagHelp(std::ostream& out, const flags_internal::CommandLineFlag& flag, void FlagHelp(std::ostream& out, const CommandLineFlag& flag,
HelpFormat format = HelpFormat::kHumanReadable); HelpFormat format = HelpFormat::kHumanReadable);
// Produces the help messages for all flags matching the filter. A flag matches // Produces the help messages for all flags matching the filter. A flag matches

View file

@ -21,15 +21,13 @@
#include <string> #include <string>
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "absl/flags/declare.h"
#include "absl/flags/flag.h" #include "absl/flags/flag.h"
#include "absl/flags/internal/parse.h" #include "absl/flags/internal/parse.h"
#include "absl/flags/internal/path_util.h" #include "absl/flags/internal/path_util.h"
#include "absl/flags/internal/program_name.h" #include "absl/flags/internal/program_name.h"
#include "absl/flags/internal/registry.h" #include "absl/flags/reflection.h"
#include "absl/flags/usage.h" #include "absl/flags/usage.h"
#include "absl/flags/usage_config.h" #include "absl/flags/usage_config.h"
#include "absl/memory/memory.h"
#include "absl/strings/match.h" #include "absl/strings/match.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
@ -91,7 +89,7 @@ class UsageReportingTest : public testing::Test {
} }
private: private:
flags::FlagSaver flag_saver_; absl::FlagSaver flag_saver_;
}; };
// -------------------------------------------------------------------- // --------------------------------------------------------------------
@ -112,7 +110,7 @@ TEST_F(UsageReportingDeathTest, TestSetProgramUsageMessage) {
// -------------------------------------------------------------------- // --------------------------------------------------------------------
TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_01) { TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_01) {
const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_01"); const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_01");
std::stringstream test_buf; std::stringstream test_buf;
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
@ -124,7 +122,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_01) {
} }
TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_02) { TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_02) {
const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_02"); const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_02");
std::stringstream test_buf; std::stringstream test_buf;
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
@ -136,7 +134,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_02) {
} }
TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_03) { TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_03) {
const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_03"); const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_03");
std::stringstream test_buf; std::stringstream test_buf;
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
@ -148,7 +146,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_03) {
} }
TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_04) { TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_04) {
const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_04"); const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_04");
std::stringstream test_buf; std::stringstream test_buf;
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
@ -160,7 +158,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_04) {
} }
TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_05) { TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_05) {
const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_05"); const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_05");
std::stringstream test_buf; std::stringstream test_buf;
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);

View file

@ -74,15 +74,16 @@ static int NumericBase(absl::string_view text) {
} }
template <typename IntType> template <typename IntType>
inline bool ParseFlagImpl(absl::string_view text, IntType* dst) { inline bool ParseFlagImpl(absl::string_view text, IntType& dst) {
text = absl::StripAsciiWhitespace(text); text = absl::StripAsciiWhitespace(text);
return absl::numbers_internal::safe_strtoi_base(text, dst, NumericBase(text)); return absl::numbers_internal::safe_strtoi_base(text, &dst,
NumericBase(text));
} }
bool AbslParseFlag(absl::string_view text, short* dst, std::string*) { bool AbslParseFlag(absl::string_view text, short* dst, std::string*) {
int val; int val;
if (!ParseFlagImpl(text, &val)) return false; if (!ParseFlagImpl(text, val)) return false;
if (static_cast<short>(val) != val) // worked, but number out of range if (static_cast<short>(val) != val) // worked, but number out of range
return false; return false;
*dst = static_cast<short>(val); *dst = static_cast<short>(val);
@ -91,7 +92,7 @@ bool AbslParseFlag(absl::string_view text, short* dst, std::string*) {
bool AbslParseFlag(absl::string_view text, unsigned short* dst, std::string*) { bool AbslParseFlag(absl::string_view text, unsigned short* dst, std::string*) {
unsigned int val; unsigned int val;
if (!ParseFlagImpl(text, &val)) return false; if (!ParseFlagImpl(text, val)) return false;
if (static_cast<unsigned short>(val) != if (static_cast<unsigned short>(val) !=
val) // worked, but number out of range val) // worked, but number out of range
return false; return false;
@ -100,28 +101,28 @@ bool AbslParseFlag(absl::string_view text, unsigned short* dst, std::string*) {
} }
bool AbslParseFlag(absl::string_view text, int* dst, std::string*) { bool AbslParseFlag(absl::string_view text, int* dst, std::string*) {
return ParseFlagImpl(text, dst); return ParseFlagImpl(text, *dst);
} }
bool AbslParseFlag(absl::string_view text, unsigned int* dst, std::string*) { bool AbslParseFlag(absl::string_view text, unsigned int* dst, std::string*) {
return ParseFlagImpl(text, dst); return ParseFlagImpl(text, *dst);
} }
bool AbslParseFlag(absl::string_view text, long* dst, std::string*) { bool AbslParseFlag(absl::string_view text, long* dst, std::string*) {
return ParseFlagImpl(text, dst); return ParseFlagImpl(text, *dst);
} }
bool AbslParseFlag(absl::string_view text, unsigned long* dst, std::string*) { bool AbslParseFlag(absl::string_view text, unsigned long* dst, std::string*) {
return ParseFlagImpl(text, dst); return ParseFlagImpl(text, *dst);
} }
bool AbslParseFlag(absl::string_view text, long long* dst, std::string*) { bool AbslParseFlag(absl::string_view text, long long* dst, std::string*) {
return ParseFlagImpl(text, dst); return ParseFlagImpl(text, *dst);
} }
bool AbslParseFlag(absl::string_view text, unsigned long long* dst, bool AbslParseFlag(absl::string_view text, unsigned long long* dst,
std::string*) { std::string*) {
return ParseFlagImpl(text, dst); return ParseFlagImpl(text, *dst);
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------

View file

@ -34,6 +34,7 @@
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/base/const_init.h" #include "absl/base/const_init.h"
#include "absl/base/thread_annotations.h" #include "absl/base/thread_annotations.h"
#include "absl/flags/commandlineflag.h"
#include "absl/flags/config.h" #include "absl/flags/config.h"
#include "absl/flags/flag.h" #include "absl/flags/flag.h"
#include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/commandlineflag.h"
@ -41,8 +42,8 @@
#include "absl/flags/internal/parse.h" #include "absl/flags/internal/parse.h"
#include "absl/flags/internal/private_handle_accessor.h" #include "absl/flags/internal/private_handle_accessor.h"
#include "absl/flags/internal/program_name.h" #include "absl/flags/internal/program_name.h"
#include "absl/flags/internal/registry.h"
#include "absl/flags/internal/usage.h" #include "absl/flags/internal/usage.h"
#include "absl/flags/reflection.h"
#include "absl/flags/usage.h" #include "absl/flags/usage.h"
#include "absl/flags/usage_config.h" #include "absl/flags/usage_config.h"
#include "absl/strings/ascii.h" #include "absl/strings/ascii.h"
@ -222,7 +223,7 @@ bool ArgsList::ReadFromFlagfile(const std::string& flag_file_name) {
// Reads the environment variable with name `name` and stores results in // Reads the environment variable with name `name` and stores results in
// `value`. If variable is not present in environment returns false, otherwise // `value`. If variable is not present in environment returns false, otherwise
// returns true. // returns true.
bool GetEnvVar(const char* var_name, std::string* var_value) { bool GetEnvVar(const char* var_name, std::string& var_value) {
#ifdef _WIN32 #ifdef _WIN32
char buf[1024]; char buf[1024];
auto get_res = GetEnvironmentVariableA(var_name, buf, sizeof(buf)); auto get_res = GetEnvironmentVariableA(var_name, buf, sizeof(buf));
@ -234,14 +235,14 @@ bool GetEnvVar(const char* var_name, std::string* var_value) {
return false; return false;
} }
*var_value = std::string(buf, get_res); var_value = std::string(buf, get_res);
#else #else
const char* val = ::getenv(var_name); const char* val = ::getenv(var_name);
if (val == nullptr) { if (val == nullptr) {
return false; return false;
} }
*var_value = val; var_value = val;
#endif #endif
return true; return true;
@ -289,11 +290,11 @@ std::tuple<absl::string_view, absl::string_view, bool> SplitNameAndValue(
// found flag or nullptr // found flag or nullptr
// is negative in case of --nofoo // is negative in case of --nofoo
std::tuple<CommandLineFlag*, bool> LocateFlag(absl::string_view flag_name) { std::tuple<CommandLineFlag*, bool> LocateFlag(absl::string_view flag_name) {
CommandLineFlag* flag = flags_internal::FindCommandLineFlag(flag_name); CommandLineFlag* flag = absl::FindCommandLineFlag(flag_name);
bool is_negative = false; bool is_negative = false;
if (!flag && absl::ConsumePrefix(&flag_name, "no")) { if (!flag && absl::ConsumePrefix(&flag_name, "no")) {
flag = flags_internal::FindCommandLineFlag(flag_name); flag = absl::FindCommandLineFlag(flag_name);
is_negative = true; is_negative = true;
} }
@ -306,17 +307,17 @@ std::tuple<CommandLineFlag*, bool> LocateFlag(absl::string_view flag_name) {
// back. // back.
void CheckDefaultValuesParsingRoundtrip() { void CheckDefaultValuesParsingRoundtrip() {
#ifndef NDEBUG #ifndef NDEBUG
flags_internal::ForEachFlag([&](CommandLineFlag* flag) { flags_internal::ForEachFlag([&](CommandLineFlag& flag) {
if (flag->IsRetired()) return; if (flag.IsRetired()) return;
#define ABSL_FLAGS_INTERNAL_IGNORE_TYPE(T, _) \ #define ABSL_FLAGS_INTERNAL_IGNORE_TYPE(T, _) \
if (flag->IsOfType<T>()) return; if (flag.IsOfType<T>()) return;
ABSL_FLAGS_INTERNAL_SUPPORTED_TYPES(ABSL_FLAGS_INTERNAL_IGNORE_TYPE) ABSL_FLAGS_INTERNAL_SUPPORTED_TYPES(ABSL_FLAGS_INTERNAL_IGNORE_TYPE)
#undef ABSL_FLAGS_INTERNAL_IGNORE_TYPE #undef ABSL_FLAGS_INTERNAL_IGNORE_TYPE
flags_internal::PrivateHandleAccessor::CheckDefaultValueParsingRoundtrip( flags_internal::PrivateHandleAccessor::CheckDefaultValueParsingRoundtrip(
*flag); flag);
}); });
#endif #endif
} }
@ -329,13 +330,13 @@ void CheckDefaultValuesParsingRoundtrip() {
// the first flagfile in the input list are processed before the second flagfile // the first flagfile in the input list are processed before the second flagfile
// etc. // etc.
bool ReadFlagfiles(const std::vector<std::string>& flagfiles, bool ReadFlagfiles(const std::vector<std::string>& flagfiles,
std::vector<ArgsList>* input_args) { std::vector<ArgsList>& input_args) {
bool success = true; bool success = true;
for (auto it = flagfiles.rbegin(); it != flagfiles.rend(); ++it) { for (auto it = flagfiles.rbegin(); it != flagfiles.rend(); ++it) {
ArgsList al; ArgsList al;
if (al.ReadFromFlagfile(*it)) { if (al.ReadFromFlagfile(*it)) {
input_args->push_back(al); input_args.push_back(al);
} else { } else {
success = false; success = false;
} }
@ -350,7 +351,7 @@ bool ReadFlagfiles(const std::vector<std::string>& flagfiles,
// `flag_name` is a string from the input flag_names list. If successful we // `flag_name` is a string from the input flag_names list. If successful we
// append a single ArgList at the end of the input_args. // append a single ArgList at the end of the input_args.
bool ReadFlagsFromEnv(const std::vector<std::string>& flag_names, bool ReadFlagsFromEnv(const std::vector<std::string>& flag_names,
std::vector<ArgsList>* input_args, std::vector<ArgsList>& input_args,
bool fail_on_absent_in_env) { bool fail_on_absent_in_env) {
bool success = true; bool success = true;
std::vector<std::string> args; std::vector<std::string> args;
@ -371,7 +372,7 @@ bool ReadFlagsFromEnv(const std::vector<std::string>& flag_names,
const std::string envname = absl::StrCat("FLAGS_", flag_name); const std::string envname = absl::StrCat("FLAGS_", flag_name);
std::string envval; std::string envval;
if (!GetEnvVar(envname.c_str(), &envval)) { if (!GetEnvVar(envname.c_str(), envval)) {
if (fail_on_absent_in_env) { if (fail_on_absent_in_env) {
flags_internal::ReportUsageError( flags_internal::ReportUsageError(
absl::StrCat(envname, " not found in environment"), true); absl::StrCat(envname, " not found in environment"), true);
@ -386,7 +387,7 @@ bool ReadFlagsFromEnv(const std::vector<std::string>& flag_names,
} }
if (success) { if (success) {
input_args->emplace_back(args); input_args.emplace_back(args);
} }
return success; return success;
@ -396,8 +397,8 @@ bool ReadFlagsFromEnv(const std::vector<std::string>& flag_names,
// Returns success status, which is true if were able to handle all generator // Returns success status, which is true if were able to handle all generator
// flags (flagfile, fromenv, tryfromemv) successfully. // flags (flagfile, fromenv, tryfromemv) successfully.
bool HandleGeneratorFlags(std::vector<ArgsList>* input_args, bool HandleGeneratorFlags(std::vector<ArgsList>& input_args,
std::vector<std::string>* flagfile_value) { std::vector<std::string>& flagfile_value) {
bool success = true; bool success = true;
absl::MutexLock l(&flags_internal::processing_checks_guard); absl::MutexLock l(&flags_internal::processing_checks_guard);
@ -422,9 +423,9 @@ bool HandleGeneratorFlags(std::vector<ArgsList>* input_args,
if (flags_internal::flagfile_needs_processing) { if (flags_internal::flagfile_needs_processing) {
auto flagfiles = absl::GetFlag(FLAGS_flagfile); auto flagfiles = absl::GetFlag(FLAGS_flagfile);
if (input_args->size() == 1) { if (input_args.size() == 1) {
flagfile_value->insert(flagfile_value->end(), flagfiles.begin(), flagfile_value.insert(flagfile_value.end(), flagfiles.begin(),
flagfiles.end()); flagfiles.end());
} }
success &= ReadFlagfiles(flagfiles, input_args); success &= ReadFlagfiles(flagfiles, input_args);
@ -647,7 +648,7 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
bool success = true; bool success = true;
while (!input_args.empty()) { while (!input_args.empty()) {
// 10. First we process the built-in generator flags. // 10. First we process the built-in generator flags.
success &= HandleGeneratorFlags(&input_args, &flagfile_value); success &= HandleGeneratorFlags(input_args, flagfile_value);
// 30. Select top-most (most recent) arguments list. If it is empty drop it // 30. Select top-most (most recent) arguments list. If it is empty drop it
// and re-try. // and re-try.
@ -733,7 +734,7 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
std::string error; std::string error;
if (!flags_internal::PrivateHandleAccessor::ParseFrom( if (!flags_internal::PrivateHandleAccessor::ParseFrom(
flag, value, SET_FLAGS_VALUE, kCommandLine, &error)) { *flag, value, SET_FLAGS_VALUE, kCommandLine, error)) {
flags_internal::ReportUsageError(error, true); flags_internal::ReportUsageError(error, true);
success = false; success = false;
} else { } else {

View file

@ -23,7 +23,6 @@
#ifndef ABSL_FLAGS_PARSE_H_ #ifndef ABSL_FLAGS_PARSE_H_
#define ABSL_FLAGS_PARSE_H_ #define ABSL_FLAGS_PARSE_H_
#include <string>
#include <vector> #include <vector>
#include "absl/base/config.h" #include "absl/base/config.h"

View file

@ -28,7 +28,7 @@
#include "absl/flags/declare.h" #include "absl/flags/declare.h"
#include "absl/flags/flag.h" #include "absl/flags/flag.h"
#include "absl/flags/internal/parse.h" #include "absl/flags/internal/parse.h"
#include "absl/flags/internal/registry.h" #include "absl/flags/reflection.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "absl/strings/substitute.h" #include "absl/strings/substitute.h"
@ -171,8 +171,8 @@ constexpr const char* const ff2_data[] = {
// temporary directory location. This way we can test inclusion of one flagfile // temporary directory location. This way we can test inclusion of one flagfile
// from another flagfile. // from another flagfile.
const char* GetFlagfileFlag(const std::vector<FlagfileData>& ffd, const char* GetFlagfileFlag(const std::vector<FlagfileData>& ffd,
std::string* flagfile_flag) { std::string& flagfile_flag) {
*flagfile_flag = "--flagfile="; flagfile_flag = "--flagfile=";
absl::string_view separator; absl::string_view separator;
for (const auto& flagfile_data : ffd) { for (const auto& flagfile_data : ffd) {
std::string flagfile_name = std::string flagfile_name =
@ -183,11 +183,11 @@ const char* GetFlagfileFlag(const std::vector<FlagfileData>& ffd,
flagfile_out << absl::Substitute(line, GetTestTempDir()) << "\n"; flagfile_out << absl::Substitute(line, GetTestTempDir()) << "\n";
} }
absl::StrAppend(flagfile_flag, separator, flagfile_name); absl::StrAppend(&flagfile_flag, separator, flagfile_name);
separator = ","; separator = ",";
} }
return flagfile_flag->c_str(); return flagfile_flag.c_str();
} }
} // namespace } // namespace
@ -208,7 +208,7 @@ using testing::ElementsAreArray;
class ParseTest : public testing::Test { class ParseTest : public testing::Test {
private: private:
flags::FlagSaver flag_saver_; absl::FlagSaver flag_saver_;
}; };
// -------------------------------------------------------------------- // --------------------------------------------------------------------
@ -588,14 +588,14 @@ TEST_F(ParseTest, TestSimpleValidFlagfile) {
const char* in_args1[] = { const char* in_args1[] = {
"testbin", "testbin",
GetFlagfileFlag({{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}}, GetFlagfileFlag({{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}},
&flagfile_flag), flagfile_flag),
}; };
TestParse(in_args1, -1, 0.1, "q2w2 ", true); TestParse(in_args1, -1, 0.1, "q2w2 ", true);
const char* in_args2[] = { const char* in_args2[] = {
"testbin", "testbin",
GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)}}, GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)}},
&flagfile_flag), flagfile_flag),
}; };
TestParse(in_args2, 100, 0.1, "q2w2 ", false); TestParse(in_args2, 100, 0.1, "q2w2 ", false);
} }
@ -609,7 +609,7 @@ TEST_F(ParseTest, TestValidMultiFlagfile) {
"testbin", "testbin",
GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)}, GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)},
{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}}, {"parse_test.ff1", absl::MakeConstSpan(ff1_data)}},
&flagfile_flag), flagfile_flag),
}; };
TestParse(in_args1, -1, 0.1, "q2w2 ", true); TestParse(in_args1, -1, 0.1, "q2w2 ", true);
} }
@ -622,7 +622,7 @@ TEST_F(ParseTest, TestFlagfileMixedWithRegularFlags) {
const char* in_args1[] = { const char* in_args1[] = {
"testbin", "--int_flag=3", "testbin", "--int_flag=3",
GetFlagfileFlag({{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}}, GetFlagfileFlag({{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}},
&flagfile_flag), flagfile_flag),
"-double_flag=0.2"}; "-double_flag=0.2"};
TestParse(in_args1, -1, 0.2, "q2w2 ", true); TestParse(in_args1, -1, 0.2, "q2w2 ", true);
} }
@ -637,10 +637,14 @@ TEST_F(ParseTest, TestFlagfileInFlagfile) {
"--flagfile=$0/parse_test.ff2", "--flagfile=$0/parse_test.ff2",
}; };
GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)},
{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}},
flagfile_flag);
const char* in_args1[] = { const char* in_args1[] = {
"testbin", "testbin",
GetFlagfileFlag({{"parse_test.ff3", absl::MakeConstSpan(ff3_data)}}, GetFlagfileFlag({{"parse_test.ff3", absl::MakeConstSpan(ff3_data)}},
&flagfile_flag), flagfile_flag),
}; };
TestParse(in_args1, 100, 0.1, "q2w2 ", false); TestParse(in_args1, 100, 0.1, "q2w2 ", false);
} }
@ -657,7 +661,7 @@ TEST_F(ParseDeathTest, TestInvalidFlagfiles) {
const char* in_args1[] = { const char* in_args1[] = {
"testbin", "testbin",
GetFlagfileFlag({{"parse_test.ff4", GetFlagfileFlag({{"parse_test.ff4",
absl::MakeConstSpan(ff4_data)}}, &flagfile_flag), absl::MakeConstSpan(ff4_data)}}, flagfile_flag),
}; };
EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1), EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1),
"Unknown command line flag 'unknown_flag'"); "Unknown command line flag 'unknown_flag'");
@ -669,7 +673,7 @@ TEST_F(ParseDeathTest, TestInvalidFlagfiles) {
const char* in_args2[] = { const char* in_args2[] = {
"testbin", "testbin",
GetFlagfileFlag({{"parse_test.ff5", GetFlagfileFlag({{"parse_test.ff5",
absl::MakeConstSpan(ff5_data)}}, &flagfile_flag), absl::MakeConstSpan(ff5_data)}}, flagfile_flag),
}; };
EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2), EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2),
"Unknown command line flag 'int_flag 10'"); "Unknown command line flag 'int_flag 10'");
@ -681,7 +685,7 @@ TEST_F(ParseDeathTest, TestInvalidFlagfiles) {
const char* in_args3[] = { const char* in_args3[] = {
"testbin", "testbin",
GetFlagfileFlag({{"parse_test.ff6", absl::MakeConstSpan(ff6_data)}}, GetFlagfileFlag({{"parse_test.ff6", absl::MakeConstSpan(ff6_data)}},
&flagfile_flag), flagfile_flag),
}; };
EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args3), EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args3),
"Flagfile can't contain position arguments or --"); "Flagfile can't contain position arguments or --");
@ -702,7 +706,7 @@ TEST_F(ParseDeathTest, TestInvalidFlagfiles) {
const char* in_args5[] = { const char* in_args5[] = {
"testbin", "testbin",
GetFlagfileFlag({{"parse_test.ff7", absl::MakeConstSpan(ff7_data)}}, GetFlagfileFlag({{"parse_test.ff7", absl::MakeConstSpan(ff7_data)}},
&flagfile_flag), flagfile_flag),
}; };
EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args5), EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args5),
"Unexpected line in the flagfile .*: \\*bin\\*"); "Unexpected line in the flagfile .*: \\*bin\\*");

View file

@ -1,5 +1,5 @@
// //
// Copyright 2019 The Abseil Authors. // Copyright 2020 The Abseil Authors.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -13,46 +13,34 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "absl/flags/internal/registry.h" #include "absl/flags/reflection.h"
#include <assert.h> #include <assert.h>
#include <stdlib.h>
#include <functional>
#include <map> #include <map>
#include <memory>
#include <string> #include <string>
#include <utility>
#include <vector>
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/thread_annotations.h" #include "absl/base/thread_annotations.h"
#include "absl/flags/internal/commandlineflag.h" #include "absl/flags/commandlineflag.h"
#include "absl/flags/internal/private_handle_accessor.h" #include "absl/flags/internal/private_handle_accessor.h"
#include "absl/flags/internal/registry.h"
#include "absl/flags/usage_config.h" #include "absl/flags/usage_config.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h" #include "absl/synchronization/mutex.h"
// --------------------------------------------------------------------
// FlagRegistry implementation
// A FlagRegistry holds all flag objects indexed
// by their names so that if you know a flag's name you can access or
// set it.
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
namespace flags_internal { namespace flags_internal {
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// FlagRegistry // FlagRegistry
// A FlagRegistry singleton object holds all flag objects indexed // A FlagRegistry singleton object holds all flag objects indexed by their
// by their names so that if you know a flag's name (as a C // names so that if you know a flag's name, you can access or set it. If the
// string), you can access or set it. If the function is named // function is named FooLocked(), you must own the registry lock before
// FooLocked(), you must own the registry lock before calling // calling the function; otherwise, you should *not* hold the lock, and the
// the function; otherwise, you should *not* hold the lock, and // function will acquire it itself if needed.
// the function will acquire it itself if needed.
// -------------------------------------------------------------------- // --------------------------------------------------------------------
class FlagRegistry { class FlagRegistry {
@ -60,8 +48,8 @@ class FlagRegistry {
FlagRegistry() = default; FlagRegistry() = default;
~FlagRegistry() = default; ~FlagRegistry() = default;
// Store a flag in this registry. Takes ownership of *flag. // Store a flag in this registry. Takes ownership of *flag.
void RegisterFlag(CommandLineFlag* flag); void RegisterFlag(CommandLineFlag& flag);
void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION(lock_) { lock_.Lock(); } void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION(lock_) { lock_.Lock(); }
void Unlock() ABSL_UNLOCK_FUNCTION(lock_) { lock_.Unlock(); } void Unlock() ABSL_UNLOCK_FUNCTION(lock_) { lock_.Unlock(); }
@ -74,12 +62,13 @@ class FlagRegistry {
// found or not retired. Does not emit a warning. // found or not retired. Does not emit a warning.
CommandLineFlag* FindRetiredFlagLocked(absl::string_view name); CommandLineFlag* FindRetiredFlagLocked(absl::string_view name);
static FlagRegistry* GlobalRegistry(); // returns a singleton registry static FlagRegistry& GlobalRegistry(); // returns a singleton registry
private: private:
friend class FlagSaverImpl; // reads all the flags in order to copy them friend class flags_internal::FlagSaverImpl; // reads all the flags in order
// to copy them
friend void ForEachFlagUnlocked( friend void ForEachFlagUnlocked(
std::function<void(CommandLineFlag*)> visitor); std::function<void(CommandLineFlag&)> visitor);
// The map from name to flag, for FindFlagLocked(). // The map from name to flag, for FindFlagLocked().
using FlagMap = std::map<absl::string_view, CommandLineFlag*>; using FlagMap = std::map<absl::string_view, CommandLineFlag*>;
@ -94,74 +83,6 @@ class FlagRegistry {
FlagRegistry& operator=(const FlagRegistry&); FlagRegistry& operator=(const FlagRegistry&);
}; };
FlagRegistry* FlagRegistry::GlobalRegistry() {
static FlagRegistry* global_registry = new FlagRegistry;
return global_registry;
}
namespace {
class FlagRegistryLock {
public:
explicit FlagRegistryLock(FlagRegistry* fr) : fr_(fr) { fr_->Lock(); }
~FlagRegistryLock() { fr_->Unlock(); }
private:
FlagRegistry* const fr_;
};
void DestroyRetiredFlag(CommandLineFlag* flag);
} // namespace
void FlagRegistry::RegisterFlag(CommandLineFlag* flag) {
FlagRegistryLock registry_lock(this);
std::pair<FlagIterator, bool> ins =
flags_.insert(FlagMap::value_type(flag->Name(), flag));
if (ins.second == false) { // means the name was already in the map
CommandLineFlag* old_flag = ins.first->second;
if (flag->IsRetired() != old_flag->IsRetired()) {
// All registrations must agree on the 'retired' flag.
flags_internal::ReportUsageError(
absl::StrCat(
"Retired flag '", flag->Name(),
"' was defined normally in file '",
(flag->IsRetired() ? old_flag->Filename() : flag->Filename()),
"'."),
true);
} else if (flags_internal::PrivateHandleAccessor::TypeId(*flag) !=
flags_internal::PrivateHandleAccessor::TypeId(*old_flag)) {
flags_internal::ReportUsageError(
absl::StrCat("Flag '", flag->Name(),
"' was defined more than once but with "
"differing types. Defined in files '",
old_flag->Filename(), "' and '", flag->Filename(), "'."),
true);
} else if (old_flag->IsRetired()) {
// Retired flag can just be deleted.
DestroyRetiredFlag(flag);
return;
} else if (old_flag->Filename() != flag->Filename()) {
flags_internal::ReportUsageError(
absl::StrCat("Flag '", flag->Name(),
"' was defined more than once (in files '",
old_flag->Filename(), "' and '", flag->Filename(),
"')."),
true);
} else {
flags_internal::ReportUsageError(
absl::StrCat(
"Something wrong with flag '", flag->Name(), "' in file '",
flag->Filename(), "'. One possibility: file '", flag->Filename(),
"' is being linked both statically and dynamically into this "
"executable. e.g. some files listed as srcs to a test and also "
"listed as srcs of some shared lib deps of the same test."),
true);
}
// All cases above are fatal, except for the retired flags.
std::exit(1);
}
}
CommandLineFlag* FlagRegistry::FindFlagLocked(absl::string_view name) { CommandLineFlag* FlagRegistry::FindFlagLocked(absl::string_view name) {
FlagConstIterator i = flags_.find(name); FlagConstIterator i = flags_.find(name);
if (i == flags_.end()) { if (i == flags_.end()) {
@ -185,97 +106,92 @@ CommandLineFlag* FlagRegistry::FindRetiredFlagLocked(absl::string_view name) {
return i->second; return i->second;
} }
// -------------------------------------------------------------------- namespace {
// FlagSaver
// FlagSaverImpl
// This class stores the states of all flags at construct time,
// and restores all flags to that state at destruct time.
// Its major implementation challenge is that it never modifies
// pointers in the 'main' registry, so global FLAG_* vars always
// point to the right place.
// --------------------------------------------------------------------
class FlagSaverImpl { class FlagRegistryLock {
public: public:
FlagSaverImpl() = default; explicit FlagRegistryLock(FlagRegistry& fr) : fr_(fr) { fr_.Lock(); }
FlagSaverImpl(const FlagSaverImpl&) = delete; ~FlagRegistryLock() { fr_.Unlock(); }
void operator=(const FlagSaverImpl&) = delete;
// Saves the flag states from the flag registry into this object.
// It's an error to call this more than once.
void SaveFromRegistry() {
assert(backup_registry_.empty()); // call only once!
flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) {
if (auto flag_state =
flags_internal::PrivateHandleAccessor::SaveState(flag)) {
backup_registry_.emplace_back(std::move(flag_state));
}
});
}
// Restores the saved flag states into the flag registry.
void RestoreToRegistry() {
for (const auto& flag_state : backup_registry_) {
flag_state->Restore();
}
}
private: private:
std::vector<std::unique_ptr<flags_internal::FlagStateInterface>> FlagRegistry& fr_;
backup_registry_;
}; };
FlagSaver::FlagSaver() : impl_(new FlagSaverImpl) { impl_->SaveFromRegistry(); } void DestroyRetiredFlag(CommandLineFlag& flag);
void FlagSaver::Ignore() { } // namespace
delete impl_;
impl_ = nullptr;
}
FlagSaver::~FlagSaver() { void FlagRegistry::RegisterFlag(CommandLineFlag& flag) {
if (!impl_) return; FlagRegistryLock registry_lock(*this);
std::pair<FlagIterator, bool> ins =
impl_->RestoreToRegistry(); flags_.insert(FlagMap::value_type(flag.Name(), &flag));
delete impl_; if (ins.second == false) { // means the name was already in the map
} CommandLineFlag& old_flag = *ins.first->second;
if (flag.IsRetired() != old_flag.IsRetired()) {
// -------------------------------------------------------------------- // All registrations must agree on the 'retired' flag.
flags_internal::ReportUsageError(
CommandLineFlag* FindCommandLineFlag(absl::string_view name) { absl::StrCat(
if (name.empty()) return nullptr; "Retired flag '", flag.Name(), "' was defined normally in file '",
FlagRegistry* const registry = FlagRegistry::GlobalRegistry(); (flag.IsRetired() ? old_flag.Filename() : flag.Filename()), "'."),
FlagRegistryLock frl(registry); true);
} else if (flags_internal::PrivateHandleAccessor::TypeId(flag) !=
return registry->FindFlagLocked(name); flags_internal::PrivateHandleAccessor::TypeId(old_flag)) {
} flags_internal::ReportUsageError(
absl::StrCat("Flag '", flag.Name(),
CommandLineFlag* FindRetiredFlag(absl::string_view name) { "' was defined more than once but with "
FlagRegistry* const registry = FlagRegistry::GlobalRegistry(); "differing types. Defined in files '",
FlagRegistryLock frl(registry); old_flag.Filename(), "' and '", flag.Filename(), "'."),
true);
return registry->FindRetiredFlagLocked(name); } else if (old_flag.IsRetired()) {
} // Retired flag can just be deleted.
DestroyRetiredFlag(flag);
// -------------------------------------------------------------------- return;
} else if (old_flag.Filename() != flag.Filename()) {
void ForEachFlagUnlocked(std::function<void(CommandLineFlag*)> visitor) { flags_internal::ReportUsageError(
FlagRegistry* const registry = FlagRegistry::GlobalRegistry(); absl::StrCat("Flag '", flag.Name(),
for (FlagRegistry::FlagConstIterator i = registry->flags_.begin(); "' was defined more than once (in files '",
i != registry->flags_.end(); ++i) { old_flag.Filename(), "' and '", flag.Filename(), "')."),
visitor(i->second); true);
} else {
flags_internal::ReportUsageError(
absl::StrCat(
"Something wrong with flag '", flag.Name(), "' in file '",
flag.Filename(), "'. One possibility: file '", flag.Filename(),
"' is being linked both statically and dynamically into this "
"executable. e.g. some files listed as srcs to a test and also "
"listed as srcs of some shared lib deps of the same test."),
true);
}
// All cases above are fatal, except for the retired flags.
std::exit(1);
} }
} }
void ForEachFlag(std::function<void(CommandLineFlag*)> visitor) { FlagRegistry& FlagRegistry::GlobalRegistry() {
FlagRegistry* const registry = FlagRegistry::GlobalRegistry(); static FlagRegistry* global_registry = new FlagRegistry;
return *global_registry;
}
// --------------------------------------------------------------------
void ForEachFlagUnlocked(std::function<void(CommandLineFlag&)> visitor) {
FlagRegistry& registry = FlagRegistry::GlobalRegistry();
for (FlagRegistry::FlagConstIterator i = registry.flags_.begin();
i != registry.flags_.end(); ++i) {
visitor(*i->second);
}
}
void ForEachFlag(std::function<void(CommandLineFlag&)> visitor) {
FlagRegistry& registry = FlagRegistry::GlobalRegistry();
FlagRegistryLock frl(registry); FlagRegistryLock frl(registry);
ForEachFlagUnlocked(visitor); ForEachFlagUnlocked(visitor);
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
bool RegisterCommandLineFlag(CommandLineFlag* flag) { bool RegisterCommandLineFlag(CommandLineFlag& flag) {
FlagRegistry::GlobalRegistry()->RegisterFlag(flag); FlagRegistry::GlobalRegistry().RegisterFlag(flag);
return true; return true;
} }
@ -283,7 +199,7 @@ bool RegisterCommandLineFlag(CommandLineFlag* flag) {
namespace { namespace {
class RetiredFlagObj final : public flags_internal::CommandLineFlag { class RetiredFlagObj final : public CommandLineFlag {
public: public:
constexpr RetiredFlagObj(const char* name, FlagFastTypeId type_id) constexpr RetiredFlagObj(const char* name, FlagFastTypeId type_id)
: name_(name), type_id_(type_id) {} : name_(name), type_id_(type_id) {}
@ -306,7 +222,7 @@ class RetiredFlagObj final : public flags_internal::CommandLineFlag {
} }
bool ParseFrom(absl::string_view, flags_internal::FlagSettingMode, bool ParseFrom(absl::string_view, flags_internal::FlagSettingMode,
flags_internal::ValueSource, std::string*) override { flags_internal::ValueSource, std::string&) override {
return false; return false;
} }
@ -319,32 +235,74 @@ class RetiredFlagObj final : public flags_internal::CommandLineFlag {
const FlagFastTypeId type_id_; const FlagFastTypeId type_id_;
}; };
void DestroyRetiredFlag(flags_internal::CommandLineFlag* flag) { void DestroyRetiredFlag(CommandLineFlag& flag) {
assert(flag->IsRetired()); assert(flag.IsRetired());
delete static_cast<RetiredFlagObj*>(flag); delete static_cast<RetiredFlagObj*>(&flag);
} }
} // namespace } // namespace
bool Retire(const char* name, FlagFastTypeId type_id) { bool Retire(const char* name, FlagFastTypeId type_id) {
auto* flag = new flags_internal::RetiredFlagObj(name, type_id); auto* flag = new flags_internal::RetiredFlagObj(name, type_id);
FlagRegistry::GlobalRegistry()->RegisterFlag(flag); FlagRegistry::GlobalRegistry().RegisterFlag(*flag);
return true; return true;
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
bool IsRetiredFlag(absl::string_view name, bool* type_is_bool) { class FlagSaverImpl {
assert(!name.empty()); public:
CommandLineFlag* flag = flags_internal::FindRetiredFlag(name); FlagSaverImpl() = default;
if (flag == nullptr) { FlagSaverImpl(const FlagSaverImpl&) = delete;
return false; void operator=(const FlagSaverImpl&) = delete;
// Saves the flag states from the flag registry into this object.
// It's an error to call this more than once.
void SaveFromRegistry() {
assert(backup_registry_.empty()); // call only once!
flags_internal::ForEachFlag([&](CommandLineFlag& flag) {
if (auto flag_state =
flags_internal::PrivateHandleAccessor::SaveState(flag)) {
backup_registry_.emplace_back(std::move(flag_state));
}
});
} }
assert(type_is_bool);
*type_is_bool = flag->IsOfType<bool>(); // Restores the saved flag states into the flag registry.
return true; void RestoreToRegistry() {
} for (const auto& flag_state : backup_registry_) {
flag_state->Restore();
}
}
private:
std::vector<std::unique_ptr<flags_internal::FlagStateInterface>>
backup_registry_;
};
} // namespace flags_internal } // namespace flags_internal
FlagSaver::FlagSaver() : impl_(new flags_internal::FlagSaverImpl) {
impl_->SaveFromRegistry();
}
FlagSaver::~FlagSaver() {
if (!impl_) return;
impl_->RestoreToRegistry();
delete impl_;
}
// --------------------------------------------------------------------
CommandLineFlag* FindCommandLineFlag(absl::string_view name) {
if (name.empty()) return nullptr;
flags_internal::FlagRegistry& registry =
flags_internal::FlagRegistry::GlobalRegistry();
flags_internal::FlagRegistryLock frl(registry);
return registry.FindFlagLocked(name);
}
ABSL_NAMESPACE_END ABSL_NAMESPACE_END
} // namespace absl } // namespace absl

View file

@ -0,0 +1,85 @@
//
// Copyright 2020 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
//
// https://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.
//
// -----------------------------------------------------------------------------
// File: reflection.h
// -----------------------------------------------------------------------------
//
// This file defines the routines to access and operate on an Abseil Flag's
// reflection handle.
#ifndef ABSL_FLAGS_REFLECTION_H_
#define ABSL_FLAGS_REFLECTION_H_
#include <string>
#include "absl/base/config.h"
#include "absl/flags/commandlineflag.h"
#include "absl/flags/internal/commandlineflag.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace flags_internal {
class FlagSaverImpl;
} // namespace flags_internal
// FindCommandLineFlag()
//
// Returns the reflection handle of an Abseil flag of the specified name, or
// `nullptr` if not found. This function will emit a warning if the name of a
// 'retired' flag is specified.
CommandLineFlag* FindCommandLineFlag(absl::string_view name);
//------------------------------------------------------------------------------
// FlagSaver
//------------------------------------------------------------------------------
//
// A FlagSaver object stores the state of flags in the scope where the FlagSaver
// is defined, allowing modification of those flags within that scope and
// automatic restoration of the flags to their previous state upon leaving the
// scope.
//
// A FlagSaver can be used within tests to temporarily change the test
// environment and restore the test case to its previous state.
//
// Example:
//
// void MyFunc() {
// absl::FlagSaver fs;
// ...
// absl::SetFlag(FLAGS_myFlag, otherValue);
// ...
// } // scope of FlagSaver left, flags return to previous state
//
// This class is thread-safe.
class FlagSaver {
public:
FlagSaver();
~FlagSaver();
FlagSaver(const FlagSaver&) = delete;
void operator=(const FlagSaver&) = delete;
private:
flags_internal::FlagSaverImpl* impl_;
};
//-----------------------------------------------------------------------------
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_REFLECTION_H_

View file

@ -0,0 +1,60 @@
//
// Copyright 2019 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
//
// https://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/flags/reflection.h"
#include <memory>
#include <string>
#include "gtest/gtest.h"
#include "absl/flags/flag.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/marshalling.h"
#include "absl/memory/memory.h"
ABSL_FLAG(int, int_flag, 1, "int_flag help");
ABSL_FLAG(std::string, string_flag, "dflt", "string_flag help");
ABSL_RETIRED_FLAG(bool, bool_retired_flag, false, "bool_retired_flag help");
namespace {
namespace flags = absl::flags_internal;
class ReflectionTest : public testing::Test {
protected:
void SetUp() override { flag_saver_ = absl::make_unique<absl::FlagSaver>(); }
void TearDown() override { flag_saver_.reset(); }
private:
std::unique_ptr<absl::FlagSaver> flag_saver_;
};
// --------------------------------------------------------------------
TEST_F(ReflectionTest, TestFindCommandLineFlag) {
auto* handle = absl::FindCommandLineFlag("some_flag");
EXPECT_EQ(handle, nullptr);
handle = absl::FindCommandLineFlag("int_flag");
EXPECT_NE(handle, nullptr);
handle = absl::FindCommandLineFlag("string_flag");
EXPECT_NE(handle, nullptr);
handle = absl::FindCommandLineFlag("bool_retired_flag");
EXPECT_NE(handle, nullptr);
}
} // namespace

View file

@ -15,6 +15,7 @@
#include "absl/flags/usage_config.h" #include "absl/flags/usage_config.h"
#include <functional>
#include <iostream> #include <iostream>
#include <string> #include <string>

View file

@ -35,6 +35,7 @@ cc_library(
copts = ABSL_DEFAULT_COPTS, copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS, linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [ deps = [
"//absl/base:bits",
"//absl/base:config", "//absl/base:config",
"//absl/base:core_headers", "//absl/base:core_headers",
], ],

View file

@ -26,6 +26,7 @@ absl_cc_library(
COPTS COPTS
${ABSL_DEFAULT_COPTS} ${ABSL_DEFAULT_COPTS}
DEPS DEPS
absl::bits
absl::config absl::config
absl::core_headers absl::core_headers
PUBLIC PUBLIC

View file

@ -15,6 +15,7 @@
#include "absl/numeric/int128.h" #include "absl/numeric/int128.h"
#include <stddef.h> #include <stddef.h>
#include <cassert> #include <cassert>
#include <iomanip> #include <iomanip>
#include <ostream> // NOLINT(readability/streams) #include <ostream> // NOLINT(readability/streams)
@ -22,6 +23,9 @@
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#include "absl/base/internal/bits.h"
#include "absl/base/optimization.h"
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
@ -31,44 +35,26 @@ ABSL_DLL const uint128 kuint128max = MakeUint128(
namespace { namespace {
// Returns the 0-based position of the last set bit (i.e., most significant bit) // Returns the 0-based position of the last set bit (i.e., most significant bit)
// in the given uint64_t. The argument may not be 0. // in the given uint128. The argument is not 0.
// //
// For example: // For example:
// Given: 5 (decimal) == 101 (binary) // Given: 5 (decimal) == 101 (binary)
// Returns: 2 // Returns: 2
#define STEP(T, n, pos, sh) \ inline ABSL_ATTRIBUTE_ALWAYS_INLINE int Fls128(uint128 n) {
do { \
if ((n) >= (static_cast<T>(1) << (sh))) { \
(n) = (n) >> (sh); \
(pos) |= (sh); \
} \
} while (0)
static inline int Fls64(uint64_t n) {
assert(n != 0);
int pos = 0;
STEP(uint64_t, n, pos, 0x20);
uint32_t n32 = static_cast<uint32_t>(n);
STEP(uint32_t, n32, pos, 0x10);
STEP(uint32_t, n32, pos, 0x08);
STEP(uint32_t, n32, pos, 0x04);
return pos + ((uint64_t{0x3333333322221100} >> (n32 << 2)) & 0x3);
}
#undef STEP
// Like Fls64() above, but returns the 0-based position of the last set bit
// (i.e., most significant bit) in the given uint128. The argument may not be 0.
static inline int Fls128(uint128 n) {
if (uint64_t hi = Uint128High64(n)) { if (uint64_t hi = Uint128High64(n)) {
return Fls64(hi) + 64; ABSL_INTERNAL_ASSUME(hi != 0);
return 127 - base_internal::CountLeadingZeros64(hi);
} }
return Fls64(Uint128Low64(n)); const uint64_t low = Uint128Low64(n);
ABSL_INTERNAL_ASSUME(low != 0);
return 63 - base_internal::CountLeadingZeros64(low);
} }
// Long division/modulo for uint128 implemented using the shift-subtract // Long division/modulo for uint128 implemented using the shift-subtract
// division algorithm adapted from: // division algorithm adapted from:
// https://stackoverflow.com/questions/5386377/division-without-using // https://stackoverflow.com/questions/5386377/division-without-using
void DivModImpl(uint128 dividend, uint128 divisor, uint128* quotient_ret, inline void DivModImpl(uint128 dividend, uint128 divisor, uint128* quotient_ret,
uint128* remainder_ret) { uint128* remainder_ret) {
assert(divisor != 0); assert(divisor != 0);
if (divisor > dividend) { if (divisor > dividend) {

View file

@ -12,15 +12,15 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "absl/numeric/int128.h"
#include <algorithm> #include <algorithm>
#include <cstdint> #include <cstdint>
#include <limits>
#include <random> #include <random>
#include <vector> #include <vector>
#include "benchmark/benchmark.h" #include "benchmark/benchmark.h"
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/numeric/int128.h"
namespace { namespace {
@ -32,57 +32,85 @@ std::mt19937 MakeRandomEngine() {
return std::mt19937(seed); return std::mt19937(seed);
} }
std::vector<std::pair<absl::uint128, absl::uint128>> template <typename T,
GetRandomClass128SampleUniformDivisor() { typename H = typename std::conditional<
std::vector<std::pair<absl::uint128, absl::uint128>> values; std::numeric_limits<T>::is_signed, int64_t, uint64_t>::type>
std::vector<std::pair<T, T>> GetRandomClass128SampleUniformDivisor() {
std::vector<std::pair<T, T>> values;
std::mt19937 random = MakeRandomEngine(); std::mt19937 random = MakeRandomEngine();
std::uniform_int_distribution<uint64_t> uniform_uint64; std::uniform_int_distribution<H> uniform_h;
values.reserve(kSampleSize); values.reserve(kSampleSize);
for (size_t i = 0; i < kSampleSize; ++i) { for (size_t i = 0; i < kSampleSize; ++i) {
absl::uint128 a = T a{absl::MakeUint128(uniform_h(random), uniform_h(random))};
absl::MakeUint128(uniform_uint64(random), uniform_uint64(random)); T b{absl::MakeUint128(uniform_h(random), uniform_h(random))};
absl::uint128 b = values.emplace_back(std::max(a, b), std::max(T(2), std::min(a, b)));
absl::MakeUint128(uniform_uint64(random), uniform_uint64(random));
values.emplace_back(std::max(a, b),
std::max(absl::uint128(2), std::min(a, b)));
} }
return values; return values;
} }
template <typename T>
void BM_DivideClass128UniformDivisor(benchmark::State& state) { void BM_DivideClass128UniformDivisor(benchmark::State& state) {
auto values = GetRandomClass128SampleUniformDivisor(); auto values = GetRandomClass128SampleUniformDivisor<T>();
while (state.KeepRunningBatch(values.size())) { while (state.KeepRunningBatch(values.size())) {
for (const auto& pair : values) { for (const auto& pair : values) {
benchmark::DoNotOptimize(pair.first / pair.second); benchmark::DoNotOptimize(pair.first / pair.second);
} }
} }
} }
BENCHMARK(BM_DivideClass128UniformDivisor); BENCHMARK_TEMPLATE(BM_DivideClass128UniformDivisor, absl::uint128);
BENCHMARK_TEMPLATE(BM_DivideClass128UniformDivisor, absl::int128);
std::vector<std::pair<absl::uint128, uint64_t>> template <typename T>
GetRandomClass128SampleSmallDivisor() { void BM_RemainderClass128UniformDivisor(benchmark::State& state) {
std::vector<std::pair<absl::uint128, uint64_t>> values; auto values = GetRandomClass128SampleUniformDivisor<T>();
while (state.KeepRunningBatch(values.size())) {
for (const auto& pair : values) {
benchmark::DoNotOptimize(pair.first % pair.second);
}
}
}
BENCHMARK_TEMPLATE(BM_RemainderClass128UniformDivisor, absl::uint128);
BENCHMARK_TEMPLATE(BM_RemainderClass128UniformDivisor, absl::int128);
template <typename T,
typename H = typename std::conditional<
std::numeric_limits<T>::is_signed, int64_t, uint64_t>::type>
std::vector<std::pair<T, H>> GetRandomClass128SampleSmallDivisor() {
std::vector<std::pair<T, H>> values;
std::mt19937 random = MakeRandomEngine(); std::mt19937 random = MakeRandomEngine();
std::uniform_int_distribution<uint64_t> uniform_uint64; std::uniform_int_distribution<H> uniform_h;
values.reserve(kSampleSize); values.reserve(kSampleSize);
for (size_t i = 0; i < kSampleSize; ++i) { for (size_t i = 0; i < kSampleSize; ++i) {
absl::uint128 a = T a{absl::MakeUint128(uniform_h(random), uniform_h(random))};
absl::MakeUint128(uniform_uint64(random), uniform_uint64(random)); H b{std::max(H{2}, uniform_h(random))};
uint64_t b = std::max(uint64_t{2}, uniform_uint64(random)); values.emplace_back(std::max(a, T(b)), b);
values.emplace_back(std::max(a, absl::uint128(b)), b);
} }
return values; return values;
} }
template <typename T>
void BM_DivideClass128SmallDivisor(benchmark::State& state) { void BM_DivideClass128SmallDivisor(benchmark::State& state) {
auto values = GetRandomClass128SampleSmallDivisor(); auto values = GetRandomClass128SampleSmallDivisor<T>();
while (state.KeepRunningBatch(values.size())) { while (state.KeepRunningBatch(values.size())) {
for (const auto& pair : values) { for (const auto& pair : values) {
benchmark::DoNotOptimize(pair.first / pair.second); benchmark::DoNotOptimize(pair.first / pair.second);
} }
} }
} }
BENCHMARK(BM_DivideClass128SmallDivisor); BENCHMARK_TEMPLATE(BM_DivideClass128SmallDivisor, absl::uint128);
BENCHMARK_TEMPLATE(BM_DivideClass128SmallDivisor, absl::int128);
template <typename T>
void BM_RemainderClass128SmallDivisor(benchmark::State& state) {
auto values = GetRandomClass128SampleSmallDivisor<T>();
while (state.KeepRunningBatch(values.size())) {
for (const auto& pair : values) {
benchmark::DoNotOptimize(pair.first % pair.second);
}
}
}
BENCHMARK_TEMPLATE(BM_RemainderClass128SmallDivisor, absl::uint128);
BENCHMARK_TEMPLATE(BM_RemainderClass128SmallDivisor, absl::int128);
std::vector<std::pair<absl::uint128, absl::uint128>> GetRandomClass128Sample() { std::vector<std::pair<absl::uint128, absl::uint128>> GetRandomClass128Sample() {
std::vector<std::pair<absl::uint128, absl::uint128>> values; std::vector<std::pair<absl::uint128, absl::uint128>> values;
@ -121,74 +149,107 @@ BENCHMARK(BM_AddClass128);
// Some implementations of <random> do not support __int128 when it is // Some implementations of <random> do not support __int128 when it is
// available, so we make our own uniform_int_distribution-like type. // available, so we make our own uniform_int_distribution-like type.
template <typename T,
typename H = typename std::conditional<
std::is_same<T, __int128>::value, int64_t, uint64_t>::type>
class UniformIntDistribution128 { class UniformIntDistribution128 {
public: public:
// NOLINTNEXTLINE: mimicking std::uniform_int_distribution API // NOLINTNEXTLINE: mimicking std::uniform_int_distribution API
unsigned __int128 operator()(std::mt19937& generator) { T operator()(std::mt19937& generator) {
return (static_cast<unsigned __int128>(dist64_(generator)) << 64) | return (static_cast<T>(dist64_(generator)) << 64) | dist64_(generator);
dist64_(generator);
} }
private: private:
std::uniform_int_distribution<uint64_t> dist64_; std::uniform_int_distribution<H> dist64_;
}; };
std::vector<std::pair<unsigned __int128, unsigned __int128>> template <typename T,
GetRandomIntrinsic128SampleUniformDivisor() { typename H = typename std::conditional<
std::vector<std::pair<unsigned __int128, unsigned __int128>> values; std::is_same<T, __int128>::value, int64_t, uint64_t>::type>
std::vector<std::pair<T, T>> GetRandomIntrinsic128SampleUniformDivisor() {
std::vector<std::pair<T, T>> values;
std::mt19937 random = MakeRandomEngine(); std::mt19937 random = MakeRandomEngine();
UniformIntDistribution128 uniform_uint128; UniformIntDistribution128<T> uniform_128;
values.reserve(kSampleSize); values.reserve(kSampleSize);
for (size_t i = 0; i < kSampleSize; ++i) { for (size_t i = 0; i < kSampleSize; ++i) {
unsigned __int128 a = uniform_uint128(random); T a = uniform_128(random);
unsigned __int128 b = uniform_uint128(random); T b = uniform_128(random);
values.emplace_back( values.emplace_back(std::max(a, b),
std::max(a, b), std::max(static_cast<T>(2), std::min(a, b)));
std::max(static_cast<unsigned __int128>(2), std::min(a, b)));
} }
return values; return values;
} }
template <typename T>
void BM_DivideIntrinsic128UniformDivisor(benchmark::State& state) { void BM_DivideIntrinsic128UniformDivisor(benchmark::State& state) {
auto values = GetRandomIntrinsic128SampleUniformDivisor(); auto values = GetRandomIntrinsic128SampleUniformDivisor<T>();
while (state.KeepRunningBatch(values.size())) { while (state.KeepRunningBatch(values.size())) {
for (const auto& pair : values) { for (const auto& pair : values) {
benchmark::DoNotOptimize(pair.first / pair.second); benchmark::DoNotOptimize(pair.first / pair.second);
} }
} }
} }
BENCHMARK(BM_DivideIntrinsic128UniformDivisor); BENCHMARK_TEMPLATE(BM_DivideIntrinsic128UniformDivisor, unsigned __int128);
BENCHMARK_TEMPLATE(BM_DivideIntrinsic128UniformDivisor, __int128);
std::vector<std::pair<unsigned __int128, uint64_t>> template <typename T>
GetRandomIntrinsic128SampleSmallDivisor() { void BM_RemainderIntrinsic128UniformDivisor(benchmark::State& state) {
std::vector<std::pair<unsigned __int128, uint64_t>> values; auto values = GetRandomIntrinsic128SampleUniformDivisor<T>();
while (state.KeepRunningBatch(values.size())) {
for (const auto& pair : values) {
benchmark::DoNotOptimize(pair.first % pair.second);
}
}
}
BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128UniformDivisor, unsigned __int128);
BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128UniformDivisor, __int128);
template <typename T,
typename H = typename std::conditional<
std::is_same<T, __int128>::value, int64_t, uint64_t>::type>
std::vector<std::pair<T, H>> GetRandomIntrinsic128SampleSmallDivisor() {
std::vector<std::pair<T, H>> values;
std::mt19937 random = MakeRandomEngine(); std::mt19937 random = MakeRandomEngine();
UniformIntDistribution128 uniform_uint128; UniformIntDistribution128<T> uniform_int128;
std::uniform_int_distribution<uint64_t> uniform_uint64; std::uniform_int_distribution<H> uniform_int64;
values.reserve(kSampleSize); values.reserve(kSampleSize);
for (size_t i = 0; i < kSampleSize; ++i) { for (size_t i = 0; i < kSampleSize; ++i) {
unsigned __int128 a = uniform_uint128(random); T a = uniform_int128(random);
uint64_t b = std::max(uint64_t{2}, uniform_uint64(random)); H b = std::max(H{2}, uniform_int64(random));
values.emplace_back(std::max(a, static_cast<unsigned __int128>(b)), b); values.emplace_back(std::max(a, static_cast<T>(b)), b);
} }
return values; return values;
} }
template <typename T>
void BM_DivideIntrinsic128SmallDivisor(benchmark::State& state) { void BM_DivideIntrinsic128SmallDivisor(benchmark::State& state) {
auto values = GetRandomIntrinsic128SampleSmallDivisor(); auto values = GetRandomIntrinsic128SampleSmallDivisor<T>();
while (state.KeepRunningBatch(values.size())) { while (state.KeepRunningBatch(values.size())) {
for (const auto& pair : values) { for (const auto& pair : values) {
benchmark::DoNotOptimize(pair.first / pair.second); benchmark::DoNotOptimize(pair.first / pair.second);
} }
} }
} }
BENCHMARK(BM_DivideIntrinsic128SmallDivisor); BENCHMARK_TEMPLATE(BM_DivideIntrinsic128SmallDivisor, unsigned __int128);
BENCHMARK_TEMPLATE(BM_DivideIntrinsic128SmallDivisor, __int128);
template <typename T>
void BM_RemainderIntrinsic128SmallDivisor(benchmark::State& state) {
auto values = GetRandomIntrinsic128SampleSmallDivisor<T>();
while (state.KeepRunningBatch(values.size())) {
for (const auto& pair : values) {
benchmark::DoNotOptimize(pair.first % pair.second);
}
}
}
BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128SmallDivisor, unsigned __int128);
BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128SmallDivisor, __int128);
std::vector<std::pair<unsigned __int128, unsigned __int128>> std::vector<std::pair<unsigned __int128, unsigned __int128>>
GetRandomIntrinsic128Sample() { GetRandomIntrinsic128Sample() {
std::vector<std::pair<unsigned __int128, unsigned __int128>> values; std::vector<std::pair<unsigned __int128, unsigned __int128>> values;
std::mt19937 random = MakeRandomEngine(); std::mt19937 random = MakeRandomEngine();
UniformIntDistribution128 uniform_uint128; UniformIntDistribution128<unsigned __int128> uniform_uint128;
values.reserve(kSampleSize); values.reserve(kSampleSize);
for (size_t i = 0; i < kSampleSize; ++i) { for (size_t i = 0; i < kSampleSize; ++i) {
values.emplace_back(uniform_uint128(random), uniform_uint128(random)); values.emplace_back(uniform_uint128(random), uniform_uint128(random));

View file

@ -69,7 +69,7 @@ cc_library(
"//absl/base:config", "//absl/base:config",
"//absl/base:core_headers", "//absl/base:core_headers",
"//absl/meta:type_traits", "//absl/meta:type_traits",
"//absl/random/internal:distributions", "//absl/random/internal:distribution_caller",
"//absl/random/internal:fast_uniform_bits", "//absl/random/internal:fast_uniform_bits",
"//absl/random/internal:fastmath", "//absl/random/internal:fastmath",
"//absl/random/internal:generate_real", "//absl/random/internal:generate_real",
@ -78,7 +78,6 @@ cc_library(
"//absl/random/internal:uniform_helper", "//absl/random/internal:uniform_helper",
"//absl/random/internal:wide_multiply", "//absl/random/internal:wide_multiply",
"//absl/strings", "//absl/strings",
"//absl/types:span",
], ],
) )
@ -116,11 +115,12 @@ cc_library(
copts = ABSL_DEFAULT_COPTS, copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS, linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [ deps = [
":random",
"//absl/base:core_headers", "//absl/base:core_headers",
"//absl/base:fast_type_id",
"//absl/meta:type_traits", "//absl/meta:type_traits",
"//absl/random/internal:distribution_caller", "//absl/random/internal:distribution_caller",
"//absl/random/internal:fast_uniform_bits", "//absl/random/internal:fast_uniform_bits",
"//absl/random/internal:mocking_bit_gen_base",
], ],
) )
@ -146,10 +146,11 @@ cc_library(
linkopts = ABSL_DEFAULT_LINKOPTS, linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [ deps = [
":distributions", ":distributions",
":random",
"//absl/base:fast_type_id",
"//absl/container:flat_hash_map", "//absl/container:flat_hash_map",
"//absl/meta:type_traits", "//absl/meta:type_traits",
"//absl/random/internal:distribution_caller", "//absl/random/internal:distribution_caller",
"//absl/random/internal:mocking_bit_gen_base",
"//absl/strings", "//absl/strings",
"//absl/types:span", "//absl/types:span",
"//absl/types:variant", "//absl/types:variant",
@ -411,6 +412,7 @@ cc_test(
deps = [ deps = [
":bit_gen_ref", ":bit_gen_ref",
":random", ":random",
"//absl/base:fast_type_id",
"//absl/random/internal:sequence_urbg", "//absl/random/internal:sequence_urbg",
"@com_google_googletest//:gtest_main", "@com_google_googletest//:gtest_main",
], ],

View file

@ -45,7 +45,6 @@ absl_cc_library(
absl::core_headers absl::core_headers
absl::random_internal_distribution_caller absl::random_internal_distribution_caller
absl::random_internal_fast_uniform_bits absl::random_internal_fast_uniform_bits
absl::random_internal_mocking_bit_gen_base
absl::type_traits absl::type_traits
) )
@ -62,6 +61,7 @@ absl_cc_test(
absl::random_bit_gen_ref absl::random_bit_gen_ref
absl::random_random absl::random_random
absl::random_internal_sequence_urbg absl::random_internal_sequence_urbg
absl::fast_type_id
gmock gmock
gtest_main gtest_main
) )
@ -69,16 +69,16 @@ absl_cc_test(
# Internal-only target, do not depend on directly. # Internal-only target, do not depend on directly.
absl_cc_library( absl_cc_library(
NAME NAME
random_internal_mocking_bit_gen_base random_internal_mock_helpers
HDRS HDRS
"internal/mocking_bit_gen_base.h" "internal/mock_helpers.h"
COPTS COPTS
${ABSL_DEFAULT_COPTS} ${ABSL_DEFAULT_COPTS}
LINKOPTS LINKOPTS
${ABSL_DEFAULT_LINKOPTS} ${ABSL_DEFAULT_LINKOPTS}
DEPS DEPS
absl::random_random absl::fast_type_id
absl::strings absl::optional
) )
# Internal-only target, do not depend on directly. # Internal-only target, do not depend on directly.
@ -93,6 +93,7 @@ absl_cc_library(
${ABSL_DEFAULT_LINKOPTS} ${ABSL_DEFAULT_LINKOPTS}
DEPS DEPS
absl::random_mocking_bit_gen absl::random_mocking_bit_gen
absl::random_internal_mock_helpers
TESTONLY TESTONLY
) )
@ -111,8 +112,8 @@ absl_cc_library(
absl::raw_logging_internal absl::raw_logging_internal
absl::random_distributions absl::random_distributions
absl::random_internal_distribution_caller absl::random_internal_distribution_caller
absl::random_internal_mocking_bit_gen_base
absl::random_internal_mock_overload_set absl::random_internal_mock_overload_set
absl::random_random
absl::strings absl::strings
absl::span absl::span
absl::type_traits absl::type_traits
@ -183,7 +184,7 @@ absl_cc_library(
absl::config absl::config
absl::core_headers absl::core_headers
absl::random_internal_generate_real absl::random_internal_generate_real
absl::random_internal_distributions absl::random_internal_distribution_caller
absl::random_internal_fast_uniform_bits absl::random_internal_fast_uniform_bits
absl::random_internal_fastmath absl::random_internal_fastmath
absl::random_internal_iostream_state_saver absl::random_internal_iostream_state_saver
@ -191,7 +192,6 @@ absl_cc_library(
absl::random_internal_uniform_helper absl::random_internal_uniform_helper
absl::random_internal_wide_multiply absl::random_internal_wide_multiply
absl::strings absl::strings
absl::span
absl::type_traits absl::type_traits
) )
@ -534,27 +534,8 @@ absl_cc_library(
${ABSL_DEFAULT_LINKOPTS} ${ABSL_DEFAULT_LINKOPTS}
DEPS DEPS
absl::config absl::config
) absl::utility
absl::fast_type_id
# Internal-only target, do not depend on directly.
absl_cc_library(
NAME
random_internal_distributions
HDRS
"internal/distributions.h"
COPTS
${ABSL_DEFAULT_COPTS}
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
absl::random_internal_distribution_caller
absl::random_internal_fast_uniform_bits
absl::random_internal_fastmath
absl::random_internal_traits
absl::random_internal_uniform_helper
absl::span
absl::strings
absl::type_traits
) )
# Internal-only target, do not depend on directly. # Internal-only target, do not depend on directly.
@ -745,7 +726,6 @@ absl_cc_library(
absl::random_internal_salted_seed_seq absl::random_internal_salted_seed_seq
absl::random_internal_seed_material absl::random_internal_seed_material
absl::span absl::span
absl::strings
absl::type_traits absl::type_traits
) )
@ -1174,9 +1154,7 @@ absl_cc_library(
LINKOPTS LINKOPTS
${ABSL_DEFAULT_LINKOPTS} ${ABSL_DEFAULT_LINKOPTS}
DEPS DEPS
absl::core_headers absl::config
absl::random_internal_fast_uniform_bits
absl::random_internal_iostream_state_saver
absl::random_internal_traits absl::random_internal_traits
absl::type_traits absl::type_traits
) )

View file

@ -24,11 +24,11 @@
#ifndef ABSL_RANDOM_BIT_GEN_REF_H_ #ifndef ABSL_RANDOM_BIT_GEN_REF_H_
#define ABSL_RANDOM_BIT_GEN_REF_H_ #define ABSL_RANDOM_BIT_GEN_REF_H_
#include "absl/base/internal/fast_type_id.h"
#include "absl/base/macros.h" #include "absl/base/macros.h"
#include "absl/meta/type_traits.h" #include "absl/meta/type_traits.h"
#include "absl/random/internal/distribution_caller.h" #include "absl/random/internal/distribution_caller.h"
#include "absl/random/internal/fast_uniform_bits.h" #include "absl/random/internal/fast_uniform_bits.h"
#include "absl/random/internal/mocking_bit_gen_base.h"
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
@ -51,6 +51,10 @@ struct is_urbg<
typename std::decay<decltype(std::declval<URBG>()())>::type>::value>> typename std::decay<decltype(std::declval<URBG>()())>::type>::value>>
: std::true_type {}; : std::true_type {};
template <typename>
struct DistributionCaller;
class MockHelpers;
} // namespace random_internal } // namespace random_internal
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -77,23 +81,50 @@ struct is_urbg<
// } // }
// //
class BitGenRef { class BitGenRef {
public: // SFINAE to detect whether the URBG type includes a member matching
using result_type = uint64_t; // bool InvokeMock(base_internal::FastTypeIdType, void*, void*).
//
// These live inside BitGenRef so that they have friend access
// to MockingBitGen. (see similar methods in DistributionCaller).
template <template <class...> class Trait, class AlwaysVoid, class... Args>
struct detector : std::false_type {};
template <template <class...> class Trait, class... Args>
struct detector<Trait, absl::void_t<Trait<Args...>>, Args...>
: std::true_type {};
BitGenRef(const absl::BitGenRef&) = default; template <class T>
BitGenRef(absl::BitGenRef&&) = default; using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock(
BitGenRef& operator=(const absl::BitGenRef&) = default; std::declval<base_internal::FastTypeIdType>(), std::declval<void*>(),
BitGenRef& operator=(absl::BitGenRef&&) = default; std::declval<void*>()));
template <typename T>
using HasInvokeMock = typename detector<invoke_mock_t, void, T>::type;
public:
BitGenRef(const BitGenRef&) = default;
BitGenRef(BitGenRef&&) = default;
BitGenRef& operator=(const BitGenRef&) = default;
BitGenRef& operator=(BitGenRef&&) = default;
template <typename URBG, typename absl::enable_if_t<
(!std::is_same<URBG, BitGenRef>::value &&
random_internal::is_urbg<URBG>::value &&
!HasInvokeMock<URBG>::value)>* = nullptr>
BitGenRef(URBG& gen) // NOLINT
: t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)),
mock_call_(NotAMock),
generate_impl_fn_(ImplFn<URBG>) {}
template <typename URBG, template <typename URBG,
typename absl::enable_if_t< typename absl::enable_if_t<(!std::is_same<URBG, BitGenRef>::value &&
(!std::is_same<URBG, BitGenRef>::value && random_internal::is_urbg<URBG>::value &&
random_internal::is_urbg<URBG>::value)>* = nullptr> HasInvokeMock<URBG>::value)>* = nullptr>
BitGenRef(URBG& gen) // NOLINT BitGenRef(URBG& gen) // NOLINT
: mocked_gen_ptr_(MakeMockPointer(&gen)), : t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)),
t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)), mock_call_(&MockCall<URBG>),
generate_impl_fn_(ImplFn<URBG>) { generate_impl_fn_(ImplFn<URBG>) {}
}
using result_type = uint64_t;
static constexpr result_type(min)() { static constexpr result_type(min)() {
return (std::numeric_limits<result_type>::min)(); return (std::numeric_limits<result_type>::min)();
@ -106,14 +137,9 @@ class BitGenRef {
result_type operator()() { return generate_impl_fn_(t_erased_gen_ptr_); } result_type operator()() { return generate_impl_fn_(t_erased_gen_ptr_); }
private: private:
friend struct absl::random_internal::DistributionCaller<absl::BitGenRef>;
using impl_fn = result_type (*)(uintptr_t); using impl_fn = result_type (*)(uintptr_t);
using mocker_base_t = absl::random_internal::MockingBitGenBase; using mock_call_fn = bool (*)(uintptr_t, base_internal::FastTypeIdType, void*,
void*);
// Convert an arbitrary URBG pointer into either a valid mocker_base_t
// pointer or a nullptr.
static inline mocker_base_t* MakeMockPointer(mocker_base_t* t) { return t; }
static inline mocker_base_t* MakeMockPointer(void*) { return nullptr; }
template <typename URBG> template <typename URBG>
static result_type ImplFn(uintptr_t ptr) { static result_type ImplFn(uintptr_t ptr) {
@ -123,29 +149,32 @@ class BitGenRef {
return fast_uniform_bits(*reinterpret_cast<URBG*>(ptr)); return fast_uniform_bits(*reinterpret_cast<URBG*>(ptr));
} }
mocker_base_t* mocked_gen_ptr_; // Get a type-erased InvokeMock pointer.
uintptr_t t_erased_gen_ptr_; template <typename URBG>
impl_fn generate_impl_fn_; static bool MockCall(uintptr_t gen_ptr, base_internal::FastTypeIdType type,
}; void* result, void* arg_tuple) {
return reinterpret_cast<URBG*>(gen_ptr)->InvokeMock(type, result,
namespace random_internal { arg_tuple);
template <>
struct DistributionCaller<absl::BitGenRef> {
template <typename DistrT, typename... Args>
static typename DistrT::result_type Call(absl::BitGenRef* gen_ref,
Args&&... args) {
auto* mock_ptr = gen_ref->mocked_gen_ptr_;
if (mock_ptr == nullptr) {
DistrT dist(std::forward<Args>(args)...);
return dist(*gen_ref);
} else {
return mock_ptr->template Call<DistrT>(std::forward<Args>(args)...);
}
} }
static bool NotAMock(uintptr_t, base_internal::FastTypeIdType, void*, void*) {
return false;
}
inline bool InvokeMock(base_internal::FastTypeIdType type, void* args_tuple,
void* result) {
if (mock_call_ == NotAMock) return false; // avoids an indirect call.
return mock_call_(t_erased_gen_ptr_, type, args_tuple, result);
}
uintptr_t t_erased_gen_ptr_;
mock_call_fn mock_call_;
impl_fn generate_impl_fn_;
template <typename>
friend struct ::absl::random_internal::DistributionCaller; // for InvokeMock
friend class ::absl::random_internal::MockHelpers; // for InvokeMock
}; };
} // namespace random_internal
ABSL_NAMESPACE_END ABSL_NAMESPACE_END
} // namespace absl } // namespace absl

View file

@ -17,30 +17,31 @@
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "absl/base/internal/fast_type_id.h"
#include "absl/random/internal/sequence_urbg.h" #include "absl/random/internal/sequence_urbg.h"
#include "absl/random/random.h" #include "absl/random/random.h"
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
class ConstBitGen : public absl::random_internal::MockingBitGenBase { class ConstBitGen {
bool CallImpl(const std::type_info&, void*, void* result) override { public:
// URBG interface
using result_type = absl::BitGen::result_type;
static constexpr result_type(min)() { return (absl::BitGen::min)(); }
static constexpr result_type(max)() { return (absl::BitGen::max)(); }
result_type operator()() { return 1; }
// InvokeMock method
bool InvokeMock(base_internal::FastTypeIdType index, void*, void* result) {
*static_cast<int*>(result) = 42; *static_cast<int*>(result) = 42;
return true; return true;
} }
}; };
namespace random_internal {
template <>
struct DistributionCaller<ConstBitGen> {
template <typename DistrT, typename FormatT, typename... Args>
static typename DistrT::result_type Call(ConstBitGen* gen, Args&&... args) {
return gen->template Call<DistrT, FormatT>(std::forward<Args>(args)...);
}
};
} // namespace random_internal
namespace { namespace {
int FnTest(absl::BitGenRef gen_ref) { return absl::Uniform(gen_ref, 1, 7); } int FnTest(absl::BitGenRef gen_ref) { return absl::Uniform(gen_ref, 1, 7); }
template <typename T> template <typename T>

View file

@ -57,7 +57,7 @@
#include "absl/random/beta_distribution.h" #include "absl/random/beta_distribution.h"
#include "absl/random/exponential_distribution.h" #include "absl/random/exponential_distribution.h"
#include "absl/random/gaussian_distribution.h" #include "absl/random/gaussian_distribution.h"
#include "absl/random/internal/distributions.h" // IWYU pragma: export #include "absl/random/internal/distribution_caller.h" // IWYU pragma: export
#include "absl/random/internal/uniform_helper.h" // IWYU pragma: export #include "absl/random/internal/uniform_helper.h" // IWYU pragma: export
#include "absl/random/log_uniform_int_distribution.h" #include "absl/random/log_uniform_int_distribution.h"
#include "absl/random/poisson_distribution.h" #include "absl/random/poisson_distribution.h"

View file

@ -45,21 +45,10 @@ cc_library(
hdrs = ["distribution_caller.h"], hdrs = ["distribution_caller.h"],
copts = ABSL_DEFAULT_COPTS, copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS, linkopts = ABSL_DEFAULT_LINKOPTS,
deps = ["//absl/base:config"],
)
cc_library(
name = "distributions",
hdrs = ["distributions.h"],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [ deps = [
":distribution_caller", "//absl/base:config",
":traits", "//absl/base:fast_type_id",
":uniform_helper", "//absl/utility",
"//absl/base",
"//absl/meta:type_traits",
"//absl/strings",
], ],
) )
@ -221,7 +210,6 @@ cc_library(
":seed_material", ":seed_material",
"//absl/base:core_headers", "//absl/base:core_headers",
"//absl/meta:type_traits", "//absl/meta:type_traits",
"//absl/strings",
"//absl/types:optional", "//absl/types:optional",
"//absl/types:span", "//absl/types:span",
], ],
@ -497,12 +485,11 @@ cc_test(
) )
cc_library( cc_library(
name = "mocking_bit_gen_base", name = "mock_helpers",
hdrs = ["mocking_bit_gen_base.h"], hdrs = ["mock_helpers.h"],
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [ deps = [
"//absl/random", "//absl/base:fast_type_id",
"//absl/strings", "//absl/types:optional",
], ],
) )
@ -511,6 +498,7 @@ cc_library(
testonly = 1, testonly = 1,
hdrs = ["mock_overload_set.h"], hdrs = ["mock_overload_set.h"],
deps = [ deps = [
":mock_helpers",
"//absl/random:mocking_bit_gen", "//absl/random:mocking_bit_gen",
"@com_google_googletest//:gtest", "@com_google_googletest//:gtest",
], ],
@ -672,6 +660,8 @@ cc_library(
copts = ABSL_DEFAULT_COPTS, copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS, linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [ deps = [
":traits",
"//absl/base:config",
"//absl/meta:type_traits", "//absl/meta:type_traits",
], ],
) )

View file

@ -20,6 +20,8 @@
#include <utility> #include <utility>
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/base/internal/fast_type_id.h"
#include "absl/utility/utility.h"
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
@ -30,14 +32,57 @@ namespace random_internal {
// to intercept such calls. // to intercept such calls.
template <typename URBG> template <typename URBG>
struct DistributionCaller { struct DistributionCaller {
// Call the provided distribution type. The parameters are expected // SFINAE to detect whether the URBG type includes a member matching
// to be explicitly specified. // bool InvokeMock(base_internal::FastTypeIdType, void*, void*).
// DistrT is the distribution type. //
// These live inside BitGenRef so that they have friend access
// to MockingBitGen. (see similar methods in DistributionCaller).
template <template <class...> class Trait, class AlwaysVoid, class... Args>
struct detector : std::false_type {};
template <template <class...> class Trait, class... Args>
struct detector<Trait, absl::void_t<Trait<Args...>>, Args...>
: std::true_type {};
template <class T>
using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock(
std::declval<::absl::base_internal::FastTypeIdType>(),
std::declval<void*>(), std::declval<void*>()));
using HasInvokeMock = typename detector<invoke_mock_t, void, URBG>::type;
// Default implementation of distribution caller.
template <typename DistrT, typename... Args> template <typename DistrT, typename... Args>
static typename DistrT::result_type Call(URBG* urbg, Args&&... args) { static typename DistrT::result_type Impl(std::false_type, URBG* urbg,
Args&&... args) {
DistrT dist(std::forward<Args>(args)...); DistrT dist(std::forward<Args>(args)...);
return dist(*urbg); return dist(*urbg);
} }
// Mock implementation of distribution caller.
// The underlying KeyT must match the KeyT constructed by MockOverloadSet.
template <typename DistrT, typename... Args>
static typename DistrT::result_type Impl(std::true_type, URBG* urbg,
Args&&... args) {
using ResultT = typename DistrT::result_type;
using ArgTupleT = std::tuple<absl::decay_t<Args>...>;
using KeyT = ResultT(DistrT, ArgTupleT);
ArgTupleT arg_tuple(std::forward<Args>(args)...);
ResultT result;
if (!urbg->InvokeMock(::absl::base_internal::FastTypeId<KeyT>(), &arg_tuple,
&result)) {
auto dist = absl::make_from_tuple<DistrT>(arg_tuple);
result = dist(*urbg);
}
return result;
}
// Default implementation of distribution caller.
template <typename DistrT, typename... Args>
static typename DistrT::result_type Call(URBG* urbg, Args&&... args) {
return Impl<DistrT, Args...>(HasInvokeMock{}, urbg,
std::forward<Args>(args)...);
}
}; };
} // namespace random_internal } // namespace random_internal

View file

@ -1,52 +0,0 @@
// Copyright 2019 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
//
// https://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.
#ifndef ABSL_RANDOM_INTERNAL_DISTRIBUTIONS_H_
#define ABSL_RANDOM_INTERNAL_DISTRIBUTIONS_H_
#include <type_traits>
#include "absl/meta/type_traits.h"
#include "absl/random/internal/distribution_caller.h"
#include "absl/random/internal/traits.h"
#include "absl/random/internal/uniform_helper.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace random_internal {
// In the absence of an explicitly provided return-type, the template
// "uniform_inferred_return_t<A, B>" is used to derive a suitable type, based on
// the data-types of the endpoint-arguments {A lo, B hi}.
//
// Given endpoints {A lo, B hi}, one of {A, B} will be chosen as the
// return-type, if one type can be implicitly converted into the other, in a
// lossless way. The template "is_widening_convertible" implements the
// compile-time logic for deciding if such a conversion is possible.
//
// If no such conversion between {A, B} exists, then the overload for
// absl::Uniform() will be discarded, and the call will be ill-formed.
// Return-type for absl::Uniform() when the return-type is inferred.
template <typename A, typename B>
using uniform_inferred_return_t =
absl::enable_if_t<absl::disjunction<is_widening_convertible<A, B>,
is_widening_convertible<B, A>>::value,
typename std::conditional<
is_widening_convertible<A, B>::value, B, A>::type>;
} // namespace random_internal
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_DISTRIBUTIONS_H_

View file

@ -0,0 +1,127 @@
//
// Copyright 2019 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
//
// https://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.
#ifndef ABSL_RANDOM_INTERNAL_MOCK_HELPERS_H_
#define ABSL_RANDOM_INTERNAL_MOCK_HELPERS_H_
#include <tuple>
#include <type_traits>
#include "absl/base/internal/fast_type_id.h"
#include "absl/types/optional.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace random_internal {
// MockHelpers works in conjunction with MockOverloadSet, MockingBitGen, and
// BitGenRef to enable the mocking capability for absl distribution functions.
//
// MockingBitGen registers mocks based on the typeid of a mock signature, KeyT,
// which is used to generate a unique id.
//
// KeyT is a signature of the form:
// result_type(discriminator_type, std::tuple<args...>)
// The mocked function signature will be composed from KeyT as:
// result_type(args...)
//
class MockHelpers {
using IdType = ::absl::base_internal::FastTypeIdType;
// Given a key signature type used to index the mock, extract the components.
// KeyT is expected to have the form:
// result_type(discriminator_type, arg_tuple_type)
template <typename KeyT>
struct KeySignature;
template <typename ResultT, typename DiscriminatorT, typename ArgTupleT>
struct KeySignature<ResultT(DiscriminatorT, ArgTupleT)> {
using result_type = ResultT;
using discriminator_type = DiscriminatorT;
using arg_tuple_type = ArgTupleT;
};
// Detector for InvokeMock.
template <class T>
using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock(
std::declval<IdType>(), std::declval<void*>(), std::declval<void*>()));
// Empty implementation of InvokeMock.
template <typename KeyT, typename ReturnT, typename ArgTupleT, typename URBG,
typename... Args>
static absl::optional<ReturnT> InvokeMockImpl(char, URBG*, Args&&...) {
return absl::nullopt;
}
// Non-empty implementation of InvokeMock.
template <typename KeyT, typename ReturnT, typename ArgTupleT, typename URBG,
typename = invoke_mock_t<URBG>, typename... Args>
static absl::optional<ReturnT> InvokeMockImpl(int, URBG* urbg,
Args&&... args) {
ArgTupleT arg_tuple(std::forward<Args>(args)...);
ReturnT result;
if (urbg->InvokeMock(::absl::base_internal::FastTypeId<KeyT>(), &arg_tuple,
&result)) {
return result;
}
return absl::nullopt;
}
public:
// Invoke a mock for the KeyT (may or may not be a signature).
//
// KeyT is used to generate a typeid-based lookup key for the mock.
// KeyT is a signature of the form:
// result_type(discriminator_type, std::tuple<args...>)
// The mocked function signature will be composed from KeyT as:
// result_type(args...)
//
// An instance of arg_tuple_type must be constructable from Args..., since
// the underlying mechanism requires a pointer to an argument tuple.
template <typename KeyT, typename URBG, typename... Args>
static auto MaybeInvokeMock(URBG* urbg, Args&&... args)
-> absl::optional<typename KeySignature<KeyT>::result_type> {
// Use function overloading to dispatch to the implemenation since
// more modern patterns (e.g. require + constexpr) are not supported in all
// compiler configurations.
return InvokeMockImpl<KeyT, typename KeySignature<KeyT>::result_type,
typename KeySignature<KeyT>::arg_tuple_type, URBG>(
0, urbg, std::forward<Args>(args)...);
}
// Acquire a mock for the KeyT (may or may not be a signature).
//
// KeyT is used to generate a typeid-based lookup for the mock.
// KeyT is a signature of the form:
// result_type(discriminator_type, std::tuple<args...>)
// The mocked function signature will be composed from KeyT as:
// result_type(args...)
template <typename KeyT, typename MockURBG>
static auto MockFor(MockURBG& m) -> decltype(
std::declval<MockURBG>()
.template RegisterMock<typename KeySignature<KeyT>::result_type,
typename KeySignature<KeyT>::arg_tuple_type>(
std::declval<IdType>())) {
return m.template RegisterMock<typename KeySignature<KeyT>::result_type,
typename KeySignature<KeyT>::arg_tuple_type>(
::absl::base_internal::FastTypeId<KeyT>());
}
};
} // namespace random_internal
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_MOCK_HELPERS_H_

View file

@ -20,6 +20,7 @@
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "absl/random/internal/mock_helpers.h"
#include "absl/random/mocking_bit_gen.h" #include "absl/random/mocking_bit_gen.h"
namespace absl { namespace absl {
@ -35,17 +36,20 @@ struct MockSingleOverload;
// EXPECT_CALL(mock_single_overload, Call(...))` will expand to a call to // EXPECT_CALL(mock_single_overload, Call(...))` will expand to a call to
// `mock_single_overload.gmock_Call(...)`. Because expectations are stored on // `mock_single_overload.gmock_Call(...)`. Because expectations are stored on
// the MockingBitGen (an argument passed inside `Call(...)`), this forwards to // the MockingBitGen (an argument passed inside `Call(...)`), this forwards to
// arguments to Mocking::Register. // arguments to MockingBitGen::Register.
//
// The underlying KeyT must match the KeyT constructed by DistributionCaller.
template <typename DistrT, typename Ret, typename... Args> template <typename DistrT, typename Ret, typename... Args>
struct MockSingleOverload<DistrT, Ret(MockingBitGen&, Args...)> { struct MockSingleOverload<DistrT, Ret(MockingBitGen&, Args...)> {
static_assert(std::is_same<typename DistrT::result_type, Ret>::value, static_assert(std::is_same<typename DistrT::result_type, Ret>::value,
"Overload signature must have return type matching the " "Overload signature must have return type matching the "
"distributions result type."); "distribution result_type.");
using KeyT = Ret(DistrT, std::tuple<Args...>);
auto gmock_Call( auto gmock_Call(
absl::MockingBitGen& gen, // NOLINT(google-runtime-references) absl::MockingBitGen& gen, // NOLINT(google-runtime-references)
const ::testing::Matcher<Args>&... args) const ::testing::Matcher<Args>&... matchers)
-> decltype(gen.Register<DistrT, Args...>(args...)) { -> decltype(MockHelpers::MockFor<KeyT>(gen).gmock_Call(matchers...)) {
return gen.Register<DistrT, Args...>(args...); return MockHelpers::MockFor<KeyT>(gen).gmock_Call(matchers...);
} }
}; };
@ -53,13 +57,15 @@ template <typename DistrT, typename Ret, typename Arg, typename... Args>
struct MockSingleOverload<DistrT, Ret(Arg, MockingBitGen&, Args...)> { struct MockSingleOverload<DistrT, Ret(Arg, MockingBitGen&, Args...)> {
static_assert(std::is_same<typename DistrT::result_type, Ret>::value, static_assert(std::is_same<typename DistrT::result_type, Ret>::value,
"Overload signature must have return type matching the " "Overload signature must have return type matching the "
"distributions result type."); "distribution result_type.");
using KeyT = Ret(DistrT, std::tuple<Arg, Args...>);
auto gmock_Call( auto gmock_Call(
const ::testing::Matcher<Arg>& arg, const ::testing::Matcher<Arg>& matcher,
absl::MockingBitGen& gen, // NOLINT(google-runtime-references) absl::MockingBitGen& gen, // NOLINT(google-runtime-references)
const ::testing::Matcher<Args>&... args) const ::testing::Matcher<Args>&... matchers)
-> decltype(gen.Register<DistrT, Arg, Args...>(arg, args...)) { -> decltype(MockHelpers::MockFor<KeyT>(gen).gmock_Call(matcher,
return gen.Register<DistrT, Arg, Args...>(arg, args...); matchers...)) {
return MockHelpers::MockFor<KeyT>(gen).gmock_Call(matcher, matchers...);
} }
}; };

View file

@ -1,85 +0,0 @@
//
// 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
//
// https://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.
//
#ifndef ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_
#define ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_
#include <string>
#include <typeinfo>
#include "absl/random/random.h"
#include "absl/strings/str_cat.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace random_internal {
class MockingBitGenBase {
template <typename>
friend struct DistributionCaller;
using generator_type = absl::BitGen;
public:
// URBG interface
using result_type = generator_type::result_type;
static constexpr result_type(min)() { return (generator_type::min)(); }
static constexpr result_type(max)() { return (generator_type::max)(); }
result_type operator()() { return gen_(); }
virtual ~MockingBitGenBase() = default;
protected:
// CallImpl is the type-erased virtual dispatch.
// The type of dist is always distribution<T>,
// The type of result is always distribution<T>::result_type.
virtual bool CallImpl(const std::type_info& distr_type, void* dist_args,
void* result) = 0;
template <typename DistrT, typename ArgTupleT>
static const std::type_info& GetTypeId() {
return typeid(std::pair<absl::decay_t<DistrT>, absl::decay_t<ArgTupleT>>);
}
// Call the generating distribution function.
// Invoked by DistributionCaller<>::Call<DistT>.
// DistT is the distribution type.
template <typename DistrT, typename... Args>
typename DistrT::result_type Call(Args&&... args) {
using distr_result_type = typename DistrT::result_type;
using ArgTupleT = std::tuple<absl::decay_t<Args>...>;
ArgTupleT arg_tuple(std::forward<Args>(args)...);
auto dist = absl::make_from_tuple<DistrT>(arg_tuple);
distr_result_type result{};
bool found_match =
CallImpl(GetTypeId<DistrT, ArgTupleT>(), &arg_tuple, &result);
if (!found_match) {
result = dist(gen_);
}
return result;
}
private:
generator_type gen_;
}; // namespace random_internal
} // namespace random_internal
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_

View file

@ -19,10 +19,13 @@
#include <limits> #include <limits>
#include <type_traits> #include <type_traits>
#include "absl/base/config.h"
#include "absl/meta/type_traits.h" #include "absl/meta/type_traits.h"
#include "absl/random/internal/traits.h"
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
template <typename IntType> template <typename IntType>
class uniform_int_distribution; class uniform_int_distribution;
@ -58,6 +61,26 @@ struct IntervalOpenOpenTag
: public random_internal::TagTypeCompare<IntervalOpenOpenTag> {}; : public random_internal::TagTypeCompare<IntervalOpenOpenTag> {};
namespace random_internal { namespace random_internal {
// In the absence of an explicitly provided return-type, the template
// "uniform_inferred_return_t<A, B>" is used to derive a suitable type, based on
// the data-types of the endpoint-arguments {A lo, B hi}.
//
// Given endpoints {A lo, B hi}, one of {A, B} will be chosen as the
// return-type, if one type can be implicitly converted into the other, in a
// lossless way. The template "is_widening_convertible" implements the
// compile-time logic for deciding if such a conversion is possible.
//
// If no such conversion between {A, B} exists, then the overload for
// absl::Uniform() will be discarded, and the call will be ill-formed.
// Return-type for absl::Uniform() when the return-type is inferred.
template <typename A, typename B>
using uniform_inferred_return_t =
absl::enable_if_t<absl::disjunction<is_widening_convertible<A, B>,
is_widening_convertible<B, A>>::value,
typename std::conditional<
is_widening_convertible<A, B>::value, B, A>::type>;
// The functions // The functions
// uniform_lower_bound(tag, a, b) // uniform_lower_bound(tag, a, b)
// and // and
@ -149,12 +172,19 @@ uniform_upper_bound(Tag, FloatType, FloatType b) {
return std::nextafter(b, (std::numeric_limits<FloatType>::max)()); return std::nextafter(b, (std::numeric_limits<FloatType>::max)());
} }
// UniformDistribution selects either absl::uniform_int_distribution
// or absl::uniform_real_distribution depending on the NumType parameter.
template <typename NumType> template <typename NumType>
using UniformDistribution = using UniformDistribution =
typename std::conditional<std::is_integral<NumType>::value, typename std::conditional<std::is_integral<NumType>::value,
absl::uniform_int_distribution<NumType>, absl::uniform_int_distribution<NumType>,
absl::uniform_real_distribution<NumType>>::type; absl::uniform_real_distribution<NumType>>::type;
// UniformDistributionWrapper is used as the underlying distribution type
// by the absl::Uniform template function. It selects the proper Abseil
// uniform distribution and provides constructor overloads that match the
// expected parameter order as well as adjusting distribtuion bounds based
// on the tag.
template <typename NumType> template <typename NumType>
struct UniformDistributionWrapper : public UniformDistribution<NumType> { struct UniformDistributionWrapper : public UniformDistribution<NumType> {
template <typename TagType> template <typename TagType>

View file

@ -27,6 +27,11 @@
// More information about the Googletest testing framework is available at // More information about the Googletest testing framework is available at
// https://github.com/google/googletest // https://github.com/google/googletest
// //
// EXPECT_CALL and ON_CALL need to be made within the same DLL component as
// the call to absl::Uniform and related methods, otherwise mocking will fail
// since the underlying implementation creates a type-specific pointer which
// will be distinct across different DLL boundaries.
//
// Example: // Example:
// //
// absl::MockingBitGen mock; // absl::MockingBitGen mock;

View file

@ -33,17 +33,16 @@
#include <memory> #include <memory>
#include <tuple> #include <tuple>
#include <type_traits> #include <type_traits>
#include <typeindex>
#include <typeinfo>
#include <utility> #include <utility>
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "absl/base/internal/fast_type_id.h"
#include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_map.h"
#include "absl/meta/type_traits.h" #include "absl/meta/type_traits.h"
#include "absl/random/distributions.h" #include "absl/random/distributions.h"
#include "absl/random/internal/distribution_caller.h" #include "absl/random/internal/distribution_caller.h"
#include "absl/random/internal/mocking_bit_gen_base.h" #include "absl/random/random.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h" #include "absl/strings/str_join.h"
#include "absl/types/span.h" #include "absl/types/span.h"
@ -54,11 +53,12 @@ namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
namespace random_internal { namespace random_internal {
template <typename>
template <typename, typename> struct DistributionCaller;
struct MockSingleOverload; class MockHelpers;
} // namespace random_internal } // namespace random_internal
class BitGenRef;
// MockingBitGen // MockingBitGen
// //
@ -96,102 +96,132 @@ struct MockSingleOverload;
// At this time, only mock distributions supplied within the Abseil random // At this time, only mock distributions supplied within the Abseil random
// library are officially supported. // library are officially supported.
// //
class MockingBitGen : public absl::random_internal::MockingBitGenBase { // EXPECT_CALL and ON_CALL need to be made within the same DLL component as
// the call to absl::Uniform and related methods, otherwise mocking will fail
// since the underlying implementation creates a type-specific pointer which
// will be distinct across different DLL boundaries.
//
class MockingBitGen {
public: public:
MockingBitGen() {} MockingBitGen() = default;
~MockingBitGen() override { ~MockingBitGen() {
for (const auto& del : deleters_) del(); for (const auto& del : deleters_) del();
} }
// URBG interface
using result_type = absl::BitGen::result_type;
static constexpr result_type(min)() { return (absl::BitGen::min)(); }
static constexpr result_type(max)() { return (absl::BitGen::max)(); }
result_type operator()() { return gen_(); }
private: private:
template <typename DistrT, typename... Args> using match_impl_fn = void (*)(void* mock_fn, void* t_erased_arg_tuple,
using MockFnType =
::testing::MockFunction<typename DistrT::result_type(Args...)>;
// MockingBitGen::Register
//
// Register<DistrT, FormatT, ArgTupleT> is the main extension point for
// extending the MockingBitGen framework. It provides a mechanism to install a
// mock expectation for the distribution `distr_t` onto the MockingBitGen
// context.
//
// The returned MockFunction<...> type can be used to setup additional
// distribution parameters of the expectation.
template <typename DistrT, typename... Args, typename... Ms>
decltype(std::declval<MockFnType<DistrT, Args...>>().gmock_Call(
std::declval<Ms>()...))
Register(Ms&&... matchers) {
auto& mock =
mocks_[std::type_index(GetTypeId<DistrT, std::tuple<Args...>>())];
if (!mock.mock_fn) {
auto* mock_fn = new MockFnType<DistrT, Args...>;
mock.mock_fn = mock_fn;
mock.match_impl = &MatchImpl<DistrT, Args...>;
deleters_.emplace_back([mock_fn] { delete mock_fn; });
}
return static_cast<MockFnType<DistrT, Args...>*>(mock.mock_fn)
->gmock_Call(std::forward<Ms>(matchers)...);
}
mutable std::vector<std::function<void()>> deleters_;
using match_impl_fn = void (*)(void* mock_fn, void* t_erased_dist_args,
void* t_erased_result); void* t_erased_result);
struct MockData { struct MockData {
void* mock_fn = nullptr; void* mock_fn = nullptr;
match_impl_fn match_impl = nullptr; match_impl_fn match_impl = nullptr;
}; };
mutable absl::flat_hash_map<std::type_index, MockData> mocks_; // GetMockFnType returns the testing::MockFunction for a result and tuple.
// This method only exists for type deduction and is otherwise unimplemented.
template <typename ResultT, typename... Args>
static auto GetMockFnType(ResultT, std::tuple<Args...>)
-> ::testing::MockFunction<ResultT(Args...)>;
template <typename DistrT, typename... Args> // MockFnCaller is a helper method for use with absl::apply to
static void MatchImpl(void* mock_fn, void* dist_args, void* result) { // apply an ArgTupleT to a compatible MockFunction.
using result_type = typename DistrT::result_type; // NOTE: MockFnCaller is essentially equivalent to the lambda:
*static_cast<result_type*>(result) = absl::apply( // [fn](auto... args) { return fn->Call(std::move(args)...)}
[mock_fn](Args... args) -> result_type { // however that fails to build on some supported platforms.
return (*static_cast<MockFnType<DistrT, Args...>*>(mock_fn)) template <typename ResultT, typename MockFnType, typename Tuple>
.Call(std::move(args)...); struct MockFnCaller;
}, // specialization for std::tuple.
*static_cast<std::tuple<Args...>*>(dist_args)); template <typename ResultT, typename MockFnType, typename... Args>
struct MockFnCaller<ResultT, MockFnType, std::tuple<Args...>> {
MockFnType* fn;
inline ResultT operator()(Args... args) {
return fn->Call(std::move(args)...);
}
};
// MockingBitGen::RegisterMock
//
// RegisterMock<ResultT, ArgTupleT>(FastTypeIdType) is the main extension
// point for extending the MockingBitGen framework. It provides a mechanism to
// install a mock expectation for a function like ResultT(Args...) keyed by
// type_idex onto the MockingBitGen context. The key is that the type_index
// used to register must match the type index used to call the mock.
//
// The returned MockFunction<...> type can be used to setup additional
// distribution parameters of the expectation.
template <typename ResultT, typename ArgTupleT>
auto RegisterMock(base_internal::FastTypeIdType type)
-> decltype(GetMockFnType(std::declval<ResultT>(),
std::declval<ArgTupleT>()))& {
using MockFnType = decltype(
GetMockFnType(std::declval<ResultT>(), std::declval<ArgTupleT>()));
auto& mock = mocks_[type];
if (!mock.mock_fn) {
auto* mock_fn = new MockFnType;
mock.mock_fn = mock_fn;
mock.match_impl = &MatchImpl<ResultT, ArgTupleT>;
deleters_.emplace_back([mock_fn] { delete mock_fn; });
}
return *static_cast<MockFnType*>(mock.mock_fn);
} }
// Looks for an appropriate mock - Returns the mocked result if one is found. // MockingBitGen::MatchImpl<> is a dispatch function which converts the
// Otherwise, returns a random value generated by the underlying URBG. // generic type-erased parameters into a specific mock invocation call.
bool CallImpl(const std::type_info& key_type, void* dist_args, // Requires tuple_args to point to a ArgTupleT, which is a std::tuple<Args...>
void* result) override { // used to invoke the mock function.
// Requires result to point to a ResultT, which is the result of the call.
template <typename ResultT, typename ArgTupleT>
static void MatchImpl(/*MockFnType<ResultT, Args...>*/ void* mock_fn,
/*ArgTupleT*/ void* args_tuple,
/*ResultT*/ void* result) {
using MockFnType = decltype(
GetMockFnType(std::declval<ResultT>(), std::declval<ArgTupleT>()));
*static_cast<ResultT*>(result) = absl::apply(
MockFnCaller<ResultT, MockFnType, ArgTupleT>{
static_cast<MockFnType*>(mock_fn)},
*static_cast<ArgTupleT*>(args_tuple));
}
// MockingBitGen::InvokeMock
//
// InvokeMock(FastTypeIdType, args, result) is the entrypoint for invoking
// mocks registered on MockingBitGen.
//
// When no mocks are registered on the provided FastTypeIdType, returns false.
// Otherwise attempts to invoke the mock function ResultT(Args...) that
// was previously registered via the type_index.
// Requires tuple_args to point to a ArgTupleT, which is a std::tuple<Args...>
// used to invoke the mock function.
// Requires result to point to a ResultT, which is the result of the call.
inline bool InvokeMock(base_internal::FastTypeIdType type, void* args_tuple,
void* result) {
// Trigger a mock, if there exists one that matches `param`. // Trigger a mock, if there exists one that matches `param`.
auto it = mocks_.find(std::type_index(key_type)); auto it = mocks_.find(type);
if (it == mocks_.end()) return false; if (it == mocks_.end()) return false;
auto* mock_data = static_cast<MockData*>(&it->second); auto* mock_data = static_cast<MockData*>(&it->second);
mock_data->match_impl(mock_data->mock_fn, dist_args, result); mock_data->match_impl(mock_data->mock_fn, args_tuple, result);
return true; return true;
} }
template <typename, typename> absl::flat_hash_map<base_internal::FastTypeIdType, MockData> mocks_;
friend struct ::absl::random_internal::MockSingleOverload; std::vector<std::function<void()>> deleters_;
friend struct ::absl::random_internal::DistributionCaller< absl::BitGen gen_;
absl::MockingBitGen>;
template <typename>
friend struct ::absl::random_internal::DistributionCaller; // for InvokeMock
friend class ::absl::BitGenRef; // for InvokeMock
friend class ::absl::random_internal::MockHelpers; // for RegisterMock,
// InvokeMock
}; };
// -----------------------------------------------------------------------------
// Implementation Details Only Below
// -----------------------------------------------------------------------------
namespace random_internal {
template <>
struct DistributionCaller<absl::MockingBitGen> {
template <typename DistrT, typename... Args>
static typename DistrT::result_type Call(absl::MockingBitGen* gen,
Args&&... args) {
return gen->template Call<DistrT>(std::forward<Args>(args)...);
}
};
} // namespace random_internal
ABSL_NAMESPACE_END ABSL_NAMESPACE_END
} // namespace absl } // namespace absl

View file

@ -96,7 +96,6 @@ template <typename URBG>
void TestReproducibleVariateSequencesForNonsecureURBG() { void TestReproducibleVariateSequencesForNonsecureURBG() {
const size_t kNumVariates = 1000; const size_t kNumVariates = 1000;
// Master RNG instance.
URBG rng; URBG rng;
// Reused for both RNG instances. // Reused for both RNG instances.
auto reusable_seed = absl::CreateSeedSeqFrom(&rng); auto reusable_seed = absl::CreateSeedSeqFrom(&rng);

View file

@ -27,8 +27,6 @@
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
// The implementation was intentionally kept same as util::error::Code_Name()
// to ease the migration.
std::string StatusCodeToString(StatusCode code) { std::string StatusCodeToString(StatusCode code) {
switch (code) { switch (code) {
case StatusCode::kOk: case StatusCode::kOk:

View file

@ -705,6 +705,37 @@ Cord::Cord(absl::string_view src) {
} }
} }
template <typename T, Cord::EnableIfString<T>>
Cord::Cord(T&& src) {
if (
// String is short: copy data to avoid external block overhead.
src.size() <= kMaxBytesToCopy ||
// String is wasteful: copy data to avoid pinning too much unused memory.
src.size() < src.capacity() / 2
) {
if (src.size() <= InlineRep::kMaxInline) {
contents_.set_data(src.data(), src.size(), false);
} else {
contents_.set_tree(NewTree(src.data(), src.size(), 0));
}
} else {
struct StringReleaser {
void operator()(absl::string_view /* data */) {}
std::string data;
};
const absl::string_view original_data = src;
CordRepExternal* rep =
static_cast<CordRepExternal*>(absl::cord_internal::NewExternalRep(
original_data, StringReleaser{std::move(src)}));
// Moving src may have invalidated its data pointer, so adjust it.
rep->base =
static_cast<StringReleaser*>(GetExternalReleaser(rep))->data.data();
contents_.set_tree(rep);
}
}
template Cord::Cord(std::string&& src);
// The destruction code is separate so that the compiler can determine // The destruction code is separate so that the compiler can determine
// that it does not need to call the destructor on a moved-from Cord. // that it does not need to call the destructor on a moved-from Cord.
void Cord::DestroyCordSlow() { void Cord::DestroyCordSlow() {
@ -742,6 +773,18 @@ Cord& Cord::operator=(absl::string_view src) {
return *this; return *this;
} }
template <typename T, Cord::EnableIfString<T>>
Cord& Cord::operator=(T&& src) {
if (src.size() <= kMaxBytesToCopy) {
*this = absl::string_view(src);
} else {
*this = Cord(std::move(src));
}
return *this;
}
template Cord& Cord::operator=(std::string&& src);
// TODO(sanjay): Move to Cord::InlineRep section of file. For now, // TODO(sanjay): Move to Cord::InlineRep section of file. For now,
// we keep it here to make diffs easier. // we keep it here to make diffs easier.
void Cord::InlineRep::AppendArray(const char* src_data, size_t src_size) { void Cord::InlineRep::AppendArray(const char* src_data, size_t src_size) {
@ -853,6 +896,17 @@ void Cord::Append(const Cord& src) { AppendImpl(src); }
void Cord::Append(Cord&& src) { AppendImpl(std::move(src)); } void Cord::Append(Cord&& src) { AppendImpl(std::move(src)); }
template <typename T, Cord::EnableIfString<T>>
void Cord::Append(T&& src) {
if (src.size() <= kMaxBytesToCopy) {
Append(absl::string_view(src));
} else {
Append(Cord(std::move(src)));
}
}
template void Cord::Append(std::string&& src);
void Cord::Prepend(const Cord& src) { void Cord::Prepend(const Cord& src) {
CordRep* src_tree = src.contents_.tree(); CordRep* src_tree = src.contents_.tree();
if (src_tree != nullptr) { if (src_tree != nullptr) {
@ -882,6 +936,17 @@ void Cord::Prepend(absl::string_view src) {
} }
} }
template <typename T, Cord::EnableIfString<T>>
inline void Cord::Prepend(T&& src) {
if (src.size() <= kMaxBytesToCopy) {
Prepend(absl::string_view(src));
} else {
Prepend(Cord(std::move(src)));
}
}
template void Cord::Prepend(std::string&& src);
static CordRep* RemovePrefixFrom(CordRep* node, size_t n) { static CordRep* RemovePrefixFrom(CordRep* node, size_t n) {
if (n >= node->length) return nullptr; if (n >= node->length) return nullptr;
if (n == 0) return Ref(node); if (n == 0) return Ref(node);

View file

@ -147,11 +147,8 @@ class Cord {
// Creates a Cord from a `std::string&&` rvalue. These constructors are // Creates a Cord from a `std::string&&` rvalue. These constructors are
// templated to avoid ambiguities for types that are convertible to both // templated to avoid ambiguities for types that are convertible to both
// `absl::string_view` and `std::string`, such as `const char*`. // `absl::string_view` and `std::string`, such as `const char*`.
//
// Note that these functions reserve the right to use the `string&&`'s
// memory and that they will do so in the future.
template <typename T, EnableIfString<T> = 0> template <typename T, EnableIfString<T> = 0>
explicit Cord(T&& src) : Cord(absl::string_view(src)) {} explicit Cord(T&& src);
template <typename T, EnableIfString<T> = 0> template <typename T, EnableIfString<T> = 0>
Cord& operator=(T&& src); Cord& operator=(T&& src);
@ -1048,11 +1045,8 @@ inline Cord& Cord::operator=(Cord&& x) noexcept {
return *this; return *this;
} }
template <typename T, Cord::EnableIfString<T>> extern template Cord::Cord(std::string&& src);
inline Cord& Cord::operator=(T&& src) { extern template Cord& Cord::operator=(std::string&& src);
*this = absl::string_view(src);
return *this;
}
inline size_t Cord::size() const { inline size_t Cord::size() const {
// Length is 1st field in str.rep_ // Length is 1st field in str.rep_
@ -1098,19 +1092,8 @@ inline void Cord::Append(absl::string_view src) {
contents_.AppendArray(src.data(), src.size()); contents_.AppendArray(src.data(), src.size());
} }
template <typename T, Cord::EnableIfString<T>> extern template void Cord::Append(std::string&& src);
inline void Cord::Append(T&& src) { extern template void Cord::Prepend(std::string&& src);
// Note that this function reserves the right to reuse the `string&&`'s
// memory and that it will do so in the future.
Append(absl::string_view(src));
}
template <typename T, Cord::EnableIfString<T>>
inline void Cord::Prepend(T&& src) {
// Note that this function reserves the right to reuse the `string&&`'s
// memory and that it will do so in the future.
Prepend(absl::string_view(src));
}
inline int Cord::Compare(const Cord& rhs) const { inline int Cord::Compare(const Cord& rhs) const {
if (!contents_.is_tree() && !rhs.contents_.is_tree()) { if (!contents_.is_tree() && !rhs.contents_.is_tree()) {

View file

@ -764,6 +764,12 @@ TEST_F(FormatConvertTest, LongDouble) {
} }
} }
// Regression tests
//
// Using a string literal because not all platforms support hex literals or it
// might be out of range.
doubles.push_back(std::strtold("-0xf.ffffffb5feafffbp-16324L", nullptr));
for (const char *fmt : kFormats) { for (const char *fmt : kFormats) {
for (char f : {'f', 'F', // for (char f : {'f', 'F', //
'g', 'G', // 'g', 'G', //

View file

@ -224,13 +224,13 @@ class FractionalDigitGenerator {
// This function will allocate enough stack space to perform the conversion. // This function will allocate enough stack space to perform the conversion.
static void RunConversion( static void RunConversion(
uint128 v, int exp, absl::FunctionRef<void(FractionalDigitGenerator)> f) { uint128 v, int exp, absl::FunctionRef<void(FractionalDigitGenerator)> f) {
using Limits = std::numeric_limits<long double>;
assert(-exp < 0); assert(-exp < 0);
assert(-exp >= std::numeric_limits<long double>::min_exponent - 128); assert(-exp >= Limits::min_exponent - 128);
static_assert( static_assert(StackArray::kMaxCapacity >=
StackArray::kMaxCapacity >= (Limits::digits + 128 - Limits::min_exponent + 31) / 32,
(128 - std::numeric_limits<long double>::min_exponent + 31) / 32, "");
""); StackArray::RunWithCapacity((Limits::digits + exp + 31) / 32,
StackArray::RunWithCapacity((exp + 31) / 32,
[=](absl::Span<uint32_t> input) { [=](absl::Span<uint32_t> input) {
f(FractionalDigitGenerator(input, v, exp)); f(FractionalDigitGenerator(input, v, exp));
}); });

View file

@ -19,7 +19,7 @@
// //
// The `str_format` library is a typesafe replacement for the family of // The `str_format` library is a typesafe replacement for the family of
// `printf()` string formatting routines within the `<cstdio>` standard library // `printf()` string formatting routines within the `<cstdio>` standard library
// header. Like the `printf` family, the `str_format` uses a "format string" to // header. Like the `printf` family, `str_format` uses a "format string" to
// perform argument substitutions based on types. See the `FormatSpec` section // perform argument substitutions based on types. See the `FormatSpec` section
// below for format string documentation. // below for format string documentation.
// //

View file

@ -8,6 +8,7 @@
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "absl/strings/cord.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
@ -353,6 +354,7 @@ TEST(StrFormat, BehavesAsDocumented) {
EXPECT_EQ(StrFormat("%s", "C"), "C"); EXPECT_EQ(StrFormat("%s", "C"), "C");
EXPECT_EQ(StrFormat("%s", std::string("C++")), "C++"); EXPECT_EQ(StrFormat("%s", std::string("C++")), "C++");
EXPECT_EQ(StrFormat("%s", string_view("view")), "view"); EXPECT_EQ(StrFormat("%s", string_view("view")), "view");
EXPECT_EQ(StrFormat("%s", absl::Cord("cord")), "cord");
// Integral Conversion // Integral Conversion
// These format integral types: char, int, long, uint64_t, etc. // These format integral types: char, int, long, uint64_t, etc.
EXPECT_EQ(StrFormat("%d", char{10}), "10"); EXPECT_EQ(StrFormat("%d", char{10}), "10");

View file

@ -44,6 +44,7 @@
#include <vector> #include <vector>
#include "absl/base/internal/raw_logging.h" #include "absl/base/internal/raw_logging.h"
#include "absl/base/macros.h"
#include "absl/strings/internal/str_split_internal.h" #include "absl/strings/internal/str_split_internal.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "absl/strings/strip.h" #include "absl/strings/strip.h"

View file

@ -98,26 +98,26 @@ bool ParseLenient(string_view s, CivilT* c) {
} // namespace } // namespace
std::string FormatCivilTime(CivilSecond c) { std::string FormatCivilTime(CivilSecond c) {
return FormatYearAnd("-%m-%dT%H:%M:%S", c); return FormatYearAnd("-%m-%d%ET%H:%M:%S", c);
} }
std::string FormatCivilTime(CivilMinute c) { std::string FormatCivilTime(CivilMinute c) {
return FormatYearAnd("-%m-%dT%H:%M", c); return FormatYearAnd("-%m-%d%ET%H:%M", c);
} }
std::string FormatCivilTime(CivilHour c) { std::string FormatCivilTime(CivilHour c) {
return FormatYearAnd("-%m-%dT%H", c); return FormatYearAnd("-%m-%d%ET%H", c);
} }
std::string FormatCivilTime(CivilDay c) { return FormatYearAnd("-%m-%d", c); } std::string FormatCivilTime(CivilDay c) { return FormatYearAnd("-%m-%d", c); }
std::string FormatCivilTime(CivilMonth c) { return FormatYearAnd("-%m", c); } std::string FormatCivilTime(CivilMonth c) { return FormatYearAnd("-%m", c); }
std::string FormatCivilTime(CivilYear c) { return FormatYearAnd("", c); } std::string FormatCivilTime(CivilYear c) { return FormatYearAnd("", c); }
bool ParseCivilTime(string_view s, CivilSecond* c) { bool ParseCivilTime(string_view s, CivilSecond* c) {
return ParseYearAnd("-%m-%dT%H:%M:%S", s, c); return ParseYearAnd("-%m-%d%ET%H:%M:%S", s, c);
} }
bool ParseCivilTime(string_view s, CivilMinute* c) { bool ParseCivilTime(string_view s, CivilMinute* c) {
return ParseYearAnd("-%m-%dT%H:%M", s, c); return ParseYearAnd("-%m-%d%ET%H:%M", s, c);
} }
bool ParseCivilTime(string_view s, CivilHour* c) { bool ParseCivilTime(string_view s, CivilHour* c) {
return ParseYearAnd("-%m-%dT%H", s, c); return ParseYearAnd("-%m-%d%ET%H", s, c);
} }
bool ParseCivilTime(string_view s, CivilDay* c) { bool ParseCivilTime(string_view s, CivilDay* c) {
return ParseYearAnd("-%m-%d", s, c); return ParseYearAnd("-%m-%d", s, c);

View file

@ -27,14 +27,11 @@ namespace cctz = absl::time_internal::cctz;
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
ABSL_DLL extern const char RFC3339_full[] = ABSL_DLL extern const char RFC3339_full[] = "%Y-%m-%d%ET%H:%M:%E*S%Ez";
"%Y-%m-%dT%H:%M:%E*S%Ez"; ABSL_DLL extern const char RFC3339_sec[] = "%Y-%m-%d%ET%H:%M:%S%Ez";
ABSL_DLL extern const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez";
ABSL_DLL extern const char RFC1123_full[] = ABSL_DLL extern const char RFC1123_full[] = "%a, %d %b %E4Y %H:%M:%S %z";
"%a, %d %b %E4Y %H:%M:%S %z"; ABSL_DLL extern const char RFC1123_no_wday[] = "%d %b %E4Y %H:%M:%S %z";
ABSL_DLL extern const char RFC1123_no_wday[] =
"%d %b %E4Y %H:%M:%S %z";
namespace { namespace {

View file

@ -26,7 +26,7 @@ const char* const kFormats[] = {
absl::RFC1123_no_wday, // 1 absl::RFC1123_no_wday, // 1
absl::RFC3339_full, // 2 absl::RFC3339_full, // 2
absl::RFC3339_sec, // 3 absl::RFC3339_sec, // 3
"%Y-%m-%dT%H:%M:%S", // 4 "%Y-%m-%d%ET%H:%M:%S", // 4
"%Y-%m-%d", // 5 "%Y-%m-%d", // 5
}; };
const int kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]); const int kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]);

View file

@ -106,54 +106,64 @@ CONSTEXPR_F int days_per_month(year_t y, month_t m) noexcept {
CONSTEXPR_F fields n_day(year_t y, month_t m, diff_t d, diff_t cd, hour_t hh, CONSTEXPR_F fields n_day(year_t y, month_t m, diff_t d, diff_t cd, hour_t hh,
minute_t mm, second_t ss) noexcept { minute_t mm, second_t ss) noexcept {
y += (cd / 146097) * 400; year_t ey = y % 400;
const year_t oey = ey;
ey += (cd / 146097) * 400;
cd %= 146097; cd %= 146097;
if (cd < 0) { if (cd < 0) {
y -= 400; ey -= 400;
cd += 146097; cd += 146097;
} }
y += (d / 146097) * 400; ey += (d / 146097) * 400;
d = d % 146097 + cd; d = d % 146097 + cd;
if (d > 0) { if (d > 0) {
if (d > 146097) { if (d > 146097) {
y += 400; ey += 400;
d -= 146097; d -= 146097;
} }
} else { } else {
if (d > -365) { if (d > -365) {
// We often hit the previous year when stepping a civil time backwards, // We often hit the previous year when stepping a civil time backwards,
// so special case it to avoid counting up by 100/4/1-year chunks. // so special case it to avoid counting up by 100/4/1-year chunks.
y -= 1; ey -= 1;
d += days_per_year(y, m); d += days_per_year(ey, m);
} else { } else {
y -= 400; ey -= 400;
d += 146097; d += 146097;
} }
} }
if (d > 365) { if (d > 365) {
for (int n = days_per_century(y, m); d > n; n = days_per_century(y, m)) { for (;;) {
int n = days_per_century(ey, m);
if (d <= n) break;
d -= n; d -= n;
y += 100; ey += 100;
} }
for (int n = days_per_4years(y, m); d > n; n = days_per_4years(y, m)) { for (;;) {
int n = days_per_4years(ey, m);
if (d <= n) break;
d -= n; d -= n;
y += 4; ey += 4;
} }
for (int n = days_per_year(y, m); d > n; n = days_per_year(y, m)) { for (;;) {
int n = days_per_year(ey, m);
if (d <= n) break;
d -= n; d -= n;
++y; ++ey;
} }
} }
if (d > 28) { if (d > 28) {
for (int n = days_per_month(y, m); d > n; n = days_per_month(y, m)) { for (;;) {
int n = days_per_month(ey, m);
if (d <= n) break;
d -= n; d -= n;
if (++m > 12) { if (++m > 12) {
++y; ++ey;
m = 1; m = 1;
} }
} }
} }
return fields(y, m, static_cast<day_t>(d), hh, mm, ss); return fields(y + (ey - oey), m, static_cast<day_t>(d), hh, mm, ss);
} }
CONSTEXPR_F fields n_mon(year_t y, diff_t m, diff_t d, diff_t cd, hour_t hh, CONSTEXPR_F fields n_mon(year_t y, diff_t m, diff_t d, diff_t cd, hour_t hh,
minute_t mm, second_t ss) noexcept { minute_t mm, second_t ss) noexcept {

View file

@ -292,6 +292,7 @@ bool parse(const std::string&, const std::string&, const time_zone&,
// - %E#f - Fractional seconds with # digits of precision // - %E#f - Fractional seconds with # digits of precision
// - %E*f - Fractional seconds with full precision (a literal '*') // - %E*f - Fractional seconds with full precision (a literal '*')
// - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999) // - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999)
// - %ET - The RFC3339 "date-time" separator "T"
// //
// Note that %E0S behaves like %S, and %E0f produces no characters. In // Note that %E0S behaves like %S, and %E0f produces no characters. In
// contrast %E*f always produces at least one digit, which may be '0'. // contrast %E*f always produces at least one digit, which may be '0'.
@ -321,7 +322,8 @@ inline std::string format(const std::string& fmt, const time_point<D>& tp,
// returns the corresponding time_point. Uses strftime()-like formatting // returns the corresponding time_point. Uses strftime()-like formatting
// options, with the same extensions as cctz::format(), but with the // options, with the same extensions as cctz::format(), but with the
// exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f. %Ez // exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f. %Ez
// and %E*z also accept the same inputs. // and %E*z also accept the same inputs, which (along with %z) includes
// 'z' and 'Z' as synonyms for +00:00. %ET accepts either 'T' or 't'.
// //
// %Y consumes as many numeric characters as it can, so the matching data // %Y consumes as many numeric characters as it can, so the matching data
// should always be terminated with a non-numeric. %E4Y always consumes // should always be terminated with a non-numeric. %E4Y always consumes

View file

@ -97,8 +97,8 @@ void BM_PrevWeekday(benchmark::State& state) {
} }
BENCHMARK(BM_PrevWeekday); BENCHMARK(BM_PrevWeekday);
const char RFC3339_full[] = "%Y-%m-%dT%H:%M:%E*S%Ez"; const char RFC3339_full[] = "%Y-%m-%d%ET%H:%M:%E*S%Ez";
const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez"; const char RFC3339_sec[] = "%Y-%m-%d%ET%H:%M:%S%Ez";
const char RFC1123_full[] = "%a, %d %b %Y %H:%M:%S %z"; const char RFC1123_full[] = "%a, %d %b %Y %H:%M:%S %z";
const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z"; const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z";
@ -991,12 +991,12 @@ void BM_Time_FromCivilDay0_Libc(benchmark::State& state) {
BENCHMARK(BM_Time_FromCivilDay0_Libc); BENCHMARK(BM_Time_FromCivilDay0_Libc);
const char* const kFormats[] = { const char* const kFormats[] = {
RFC1123_full, // 0 RFC1123_full, // 0
RFC1123_no_wday, // 1 RFC1123_no_wday, // 1
RFC3339_full, // 2 RFC3339_full, // 2
RFC3339_sec, // 3 RFC3339_sec, // 3
"%Y-%m-%dT%H:%M:%S", // 4 "%Y-%m-%d%ET%H:%M:%S", // 4
"%Y-%m-%d", // 5 "%Y-%m-%d", // 5
}; };
const int kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]); const int kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]);

View file

@ -234,6 +234,16 @@ TEST(CivilTime, Difference) {
static_assert(diff == 365, "Difference"); static_assert(diff == 365, "Difference");
} }
// NOTE: Run this with --copt=-ftrapv to detect overflow problems.
TEST(CivilTime, ConstructionWithHugeYear) {
constexpr civil_hour h(-9223372036854775807, 1, 1, -1);
static_assert(h.year() == -9223372036854775807 - 1,
"ConstructionWithHugeYear");
static_assert(h.month() == 12, "ConstructionWithHugeYear");
static_assert(h.day() == 31, "ConstructionWithHugeYear");
static_assert(h.hour() == 23, "ConstructionWithHugeYear");
}
// NOTE: Run this with --copt=-ftrapv to detect overflow problems. // NOTE: Run this with --copt=-ftrapv to detect overflow problems.
TEST(CivilTime, DifferenceWithHugeYear) { TEST(CivilTime, DifferenceWithHugeYear) {
{ {

View file

@ -67,6 +67,48 @@ char* strptime(const char* s, const char* fmt, std::tm* tm) {
} }
#endif #endif
// Convert a cctz::weekday to a tm_wday value (0-6, Sunday = 0).
int ToTmWday(weekday wd) {
switch (wd) {
case weekday::sunday:
return 0;
case weekday::monday:
return 1;
case weekday::tuesday:
return 2;
case weekday::wednesday:
return 3;
case weekday::thursday:
return 4;
case weekday::friday:
return 5;
case weekday::saturday:
return 6;
}
return 0; /*NOTREACHED*/
}
// Convert a tm_wday value (0-6, Sunday = 0) to a cctz::weekday.
weekday FromTmWday(int tm_wday) {
switch (tm_wday) {
case 0:
return weekday::sunday;
case 1:
return weekday::monday;
case 2:
return weekday::tuesday;
case 3:
return weekday::wednesday;
case 4:
return weekday::thursday;
case 5:
return weekday::friday;
case 6:
return weekday::saturday;
}
return weekday::sunday; /*NOTREACHED*/
}
std::tm ToTM(const time_zone::absolute_lookup& al) { std::tm ToTM(const time_zone::absolute_lookup& al) {
std::tm tm{}; std::tm tm{};
tm.tm_sec = al.cs.second(); tm.tm_sec = al.cs.second();
@ -84,34 +126,19 @@ std::tm ToTM(const time_zone::absolute_lookup& al) {
tm.tm_year = static_cast<int>(al.cs.year() - 1900); tm.tm_year = static_cast<int>(al.cs.year() - 1900);
} }
switch (get_weekday(al.cs)) { tm.tm_wday = ToTmWday(get_weekday(al.cs));
case weekday::sunday:
tm.tm_wday = 0;
break;
case weekday::monday:
tm.tm_wday = 1;
break;
case weekday::tuesday:
tm.tm_wday = 2;
break;
case weekday::wednesday:
tm.tm_wday = 3;
break;
case weekday::thursday:
tm.tm_wday = 4;
break;
case weekday::friday:
tm.tm_wday = 5;
break;
case weekday::saturday:
tm.tm_wday = 6;
break;
}
tm.tm_yday = get_yearday(al.cs) - 1; tm.tm_yday = get_yearday(al.cs) - 1;
tm.tm_isdst = al.is_dst ? 1 : 0; tm.tm_isdst = al.is_dst ? 1 : 0;
return tm; return tm;
} }
// Returns the week of the year [0:53] given a civil day and the day on
// which weeks are defined to start.
int ToWeek(const civil_day& cd, weekday week_start) {
const civil_day d(cd.year() % 400, cd.month(), cd.day());
return static_cast<int>((d - prev_weekday(civil_year(d), week_start)) / 7);
}
const char kDigits[] = "0123456789"; const char kDigits[] = "0123456789";
// Formats a 64-bit integer in the given field width. Note that it is up // Formats a 64-bit integer in the given field width. Note that it is up
@ -290,6 +317,7 @@ const std::int_fast64_t kExp10[kDigits10_64 + 1] = {
// - %E#S - Seconds with # digits of fractional precision // - %E#S - Seconds with # digits of fractional precision
// - %E*S - Seconds with full fractional precision (a literal '*') // - %E*S - Seconds with full fractional precision (a literal '*')
// - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999) // - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999)
// - %ET - The RFC3339 "date-time" separator "T"
// //
// The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are // The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are
// handled internally for performance reasons. strftime(3) is slow due to // handled internally for performance reasons. strftime(3) is slow due to
@ -354,7 +382,7 @@ std::string format(const std::string& format, const time_point<seconds>& tp,
if (cur == end || (cur - percent) % 2 == 0) continue; if (cur == end || (cur - percent) % 2 == 0) continue;
// Simple specifiers that we handle ourselves. // Simple specifiers that we handle ourselves.
if (strchr("YmdeHMSzZs%", *cur)) { if (strchr("YmdeUuWwHMSzZs%", *cur)) {
if (cur - 1 != pending) { if (cur - 1 != pending) {
FormatTM(&result, std::string(pending, cur - 1), tm); FormatTM(&result, std::string(pending, cur - 1), tm);
} }
@ -375,6 +403,22 @@ std::string format(const std::string& format, const time_point<seconds>& tp,
if (*cur == 'e' && *bp == '0') *bp = ' '; // for Windows if (*cur == 'e' && *bp == '0') *bp = ' '; // for Windows
result.append(bp, static_cast<std::size_t>(ep - bp)); result.append(bp, static_cast<std::size_t>(ep - bp));
break; break;
case 'U':
bp = Format02d(ep, ToWeek(civil_day(al.cs), weekday::sunday));
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
case 'u':
bp = Format64(ep, 0, tm.tm_wday ? tm.tm_wday : 7);
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
case 'W':
bp = Format02d(ep, ToWeek(civil_day(al.cs), weekday::monday));
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
case 'w':
bp = Format64(ep, 0, tm.tm_wday);
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
case 'H': case 'H':
bp = Format02d(ep, al.cs.hour()); bp = Format02d(ep, al.cs.hour());
result.append(bp, static_cast<std::size_t>(ep - bp)); result.append(bp, static_cast<std::size_t>(ep - bp));
@ -448,7 +492,14 @@ std::string format(const std::string& format, const time_point<seconds>& tp,
if (*cur != 'E' || ++cur == end) continue; if (*cur != 'E' || ++cur == end) continue;
// Format our extensions. // Format our extensions.
if (*cur == 'z') { if (*cur == 'T') {
// Formats %ET.
if (cur - 2 != pending) {
FormatTM(&result, std::string(pending, cur - 2), tm);
}
result.append("T");
pending = ++cur;
} else if (*cur == 'z') {
// Formats %Ez. // Formats %Ez.
if (cur - 2 != pending) { if (cur - 2 != pending) {
FormatTM(&result, std::string(pending, cur - 2), tm); FormatTM(&result, std::string(pending, cur - 2), tm);
@ -551,7 +602,7 @@ const char* ParseOffset(const char* dp, const char* mode, int* offset) {
} else { } else {
dp = nullptr; dp = nullptr;
} }
} else if (first == 'Z') { // Zulu } else if (first == 'Z' || first == 'z') { // Zulu
*offset = 0; *offset = 0;
} else { } else {
dp = nullptr; dp = nullptr;
@ -602,12 +653,23 @@ const char* ParseTM(const char* dp, const char* fmt, std::tm* tm) {
return dp; return dp;
} }
// Sets year, tm_mon and tm_mday given the year, week_num, and tm_wday,
// and the day on which weeks are defined to start.
void FromWeek(int week_num, weekday week_start, year_t* year, std::tm* tm) {
const civil_year y(*year % 400);
civil_day cd = prev_weekday(y, week_start); // week 0
cd = next_weekday(cd - 1, FromTmWday(tm->tm_wday)) + (week_num * 7);
*year += cd.year() - y.year();
tm->tm_mon = cd.month() - 1;
tm->tm_mday = cd.day();
}
} // namespace } // namespace
// Uses strptime(3) to parse the given input. Supports the same extended // Uses strptime(3) to parse the given input. Supports the same extended
// format specifiers as format(), although %E#S and %E*S are treated // format specifiers as format(), although %E#S and %E*S are treated
// identically (and similarly for %E#f and %E*f). %Ez and %E*z also accept // identically (and similarly for %E#f and %E*f). %Ez and %E*z also accept
// the same inputs. // the same inputs. %ET accepts either 'T' or 't'.
// //
// The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are // The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are
// handled internally so that we can normally avoid strptime() altogether // handled internally so that we can normally avoid strptime() altogether
@ -651,6 +713,8 @@ bool parse(const std::string& format, const std::string& input,
const char* fmt = format.c_str(); // NUL terminated const char* fmt = format.c_str(); // NUL terminated
bool twelve_hour = false; bool twelve_hour = false;
bool afternoon = false; bool afternoon = false;
int week_num = -1;
weekday week_start = weekday::sunday;
bool saw_percent_s = false; bool saw_percent_s = false;
std::int_fast64_t percent_s = 0; std::int_fast64_t percent_s = 0;
@ -689,10 +753,27 @@ bool parse(const std::string& format, const std::string& input,
case 'm': case 'm':
data = ParseInt(data, 2, 1, 12, &tm.tm_mon); data = ParseInt(data, 2, 1, 12, &tm.tm_mon);
if (data != nullptr) tm.tm_mon -= 1; if (data != nullptr) tm.tm_mon -= 1;
week_num = -1;
continue; continue;
case 'd': case 'd':
case 'e': case 'e':
data = ParseInt(data, 2, 1, 31, &tm.tm_mday); data = ParseInt(data, 2, 1, 31, &tm.tm_mday);
week_num = -1;
continue;
case 'U':
data = ParseInt(data, 0, 0, 53, &week_num);
week_start = weekday::sunday;
continue;
case 'W':
data = ParseInt(data, 0, 0, 53, &week_num);
week_start = weekday::monday;
continue;
case 'u':
data = ParseInt(data, 0, 1, 7, &tm.tm_wday);
if (data != nullptr) tm.tm_wday %= 7;
continue;
case 'w':
data = ParseInt(data, 0, 0, 6, &tm.tm_wday);
continue; continue;
case 'H': case 'H':
data = ParseInt(data, 2, 0, 23, &tm.tm_hour); data = ParseInt(data, 2, 0, 23, &tm.tm_hour);
@ -742,6 +823,15 @@ bool parse(const std::string& format, const std::string& input,
data = (*data == '%' ? data + 1 : nullptr); data = (*data == '%' ? data + 1 : nullptr);
continue; continue;
case 'E': case 'E':
if (fmt[0] == 'T') {
if (*data == 'T' || *data == 't') {
++data;
++fmt;
} else {
data = nullptr;
}
continue;
}
if (fmt[0] == 'z' || (fmt[0] == '*' && fmt[1] == 'z')) { if (fmt[0] == 'z' || (fmt[0] == '*' && fmt[1] == 'z')) {
data = ParseOffset(data, ":", &offset); data = ParseOffset(data, ":", &offset);
if (data != nullptr) saw_offset = true; if (data != nullptr) saw_offset = true;
@ -874,6 +964,9 @@ bool parse(const std::string& format, const std::string& input,
year += 1900; year += 1900;
} }
// Compute year, tm.tm_mon and tm.tm_mday if we parsed a week number.
if (week_num != -1) FromWeek(week_num, week_start, &year, &tm);
const int month = tm.tm_mon + 1; const int month = tm.tm_mon + 1;
civil_second cs(year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); civil_second cs(year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);

View file

@ -48,8 +48,8 @@ namespace {
EXPECT_STREQ(zone, al.abbr); \ EXPECT_STREQ(zone, al.abbr); \
} while (0) } while (0)
const char RFC3339_full[] = "%Y-%m-%dT%H:%M:%E*S%Ez"; const char RFC3339_full[] = "%Y-%m-%d%ET%H:%M:%E*S%Ez";
const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez"; const char RFC3339_sec[] = "%Y-%m-%d%ET%H:%M:%S%Ez";
const char RFC1123_full[] = "%a, %d %b %Y %H:%M:%S %z"; const char RFC1123_full[] = "%a, %d %b %Y %H:%M:%S %z";
const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z"; const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z";
@ -679,6 +679,34 @@ TEST(Format, RFC1123Format) { // locale specific
EXPECT_EQ("28 Jun 1977 09:08:07 -0700", format(RFC1123_no_wday, tp, tz)); EXPECT_EQ("28 Jun 1977 09:08:07 -0700", format(RFC1123_no_wday, tp, tz));
} }
TEST(Format, Week) {
const time_zone utc = utc_time_zone();
auto tp = convert(civil_second(2017, 1, 1, 0, 0, 0), utc);
EXPECT_EQ("2017-01-7", format("%Y-%U-%u", tp, utc));
EXPECT_EQ("2017-00-0", format("%Y-%W-%w", tp, utc));
tp = convert(civil_second(2017, 12, 31, 0, 0, 0), utc);
EXPECT_EQ("2017-53-7", format("%Y-%U-%u", tp, utc));
EXPECT_EQ("2017-52-0", format("%Y-%W-%w", tp, utc));
tp = convert(civil_second(2018, 1, 1, 0, 0, 0), utc);
EXPECT_EQ("2018-00-1", format("%Y-%U-%u", tp, utc));
EXPECT_EQ("2018-01-1", format("%Y-%W-%w", tp, utc));
tp = convert(civil_second(2018, 12, 31, 0, 0, 0), utc);
EXPECT_EQ("2018-52-1", format("%Y-%U-%u", tp, utc));
EXPECT_EQ("2018-53-1", format("%Y-%W-%w", tp, utc));
tp = convert(civil_second(2019, 1, 1, 0, 0, 0), utc);
EXPECT_EQ("2019-00-2", format("%Y-%U-%u", tp, utc));
EXPECT_EQ("2019-00-2", format("%Y-%W-%w", tp, utc));
tp = convert(civil_second(2019, 12, 31, 0, 0, 0), utc);
EXPECT_EQ("2019-52-2", format("%Y-%U-%u", tp, utc));
EXPECT_EQ("2019-52-2", format("%Y-%W-%w", tp, utc));
}
// //
// Testing parse() // Testing parse()
// //
@ -1379,10 +1407,80 @@ TEST(Parse, RFC3339Format) {
EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00+00:00", tz, &tp)); EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00+00:00", tz, &tp));
ExpectTime(tp, tz, 2014, 2, 12, 20, 21, 0, 0, false, "UTC"); ExpectTime(tp, tz, 2014, 2, 12, 20, 21, 0, 0, false, "UTC");
// Check that %Ez also accepts "Z" as a synonym for "+00:00". // Check that %ET also accepts "t".
time_point<chrono::nanoseconds> tp2; time_point<chrono::nanoseconds> tp2;
EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00Z", tz, &tp2)); EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12t20:21:00+00:00", tz, &tp2));
EXPECT_EQ(tp, tp2); EXPECT_EQ(tp, tp2);
// Check that %Ez also accepts "Z" as a synonym for "+00:00".
time_point<chrono::nanoseconds> tp3;
EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00Z", tz, &tp3));
EXPECT_EQ(tp, tp3);
// Check that %Ez also accepts "z" as a synonym for "+00:00".
time_point<chrono::nanoseconds> tp4;
EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00z", tz, &tp4));
EXPECT_EQ(tp, tp4);
}
TEST(Parse, Week) {
const time_zone utc = utc_time_zone();
time_point<absl::time_internal::cctz::seconds> tp;
auto exp = convert(civil_second(2017, 1, 1, 0, 0, 0), utc);
EXPECT_TRUE(parse("%Y-%U-%u", "2017-01-7", utc, &tp));
EXPECT_EQ(exp, tp);
EXPECT_TRUE(parse("%Y-%W-%w", "2017-00-0", utc, &tp));
EXPECT_EQ(exp, tp);
exp = convert(civil_second(2017, 12, 31, 0, 0, 0), utc);
EXPECT_TRUE(parse("%Y-%U-%u", "2017-53-7", utc, &tp));
EXPECT_EQ(exp, tp);
EXPECT_TRUE(parse("%Y-%W-%w", "2017-52-0", utc, &tp));
EXPECT_EQ(exp, tp);
exp = convert(civil_second(2018, 1, 1, 0, 0, 0), utc);
EXPECT_TRUE(parse("%Y-%U-%u", "2018-00-1", utc, &tp));
EXPECT_EQ(exp, tp);
EXPECT_TRUE(parse("%Y-%W-%w", "2018-01-1", utc, &tp));
EXPECT_EQ(exp, tp);
exp = convert(civil_second(2018, 12, 31, 0, 0, 0), utc);
EXPECT_TRUE(parse("%Y-%U-%u", "2018-52-1", utc, &tp));
EXPECT_EQ(exp, tp);
EXPECT_TRUE(parse("%Y-%W-%w", "2018-53-1", utc, &tp));
EXPECT_EQ(exp, tp);
exp = convert(civil_second(2019, 1, 1, 0, 0, 0), utc);
EXPECT_TRUE(parse("%Y-%U-%u", "2019-00-2", utc, &tp));
EXPECT_EQ(exp, tp);
EXPECT_TRUE(parse("%Y-%W-%w", "2019-00-2", utc, &tp));
EXPECT_EQ(exp, tp);
exp = convert(civil_second(2019, 12, 31, 0, 0, 0), utc);
EXPECT_TRUE(parse("%Y-%U-%u", "2019-52-2", utc, &tp));
EXPECT_EQ(exp, tp);
EXPECT_TRUE(parse("%Y-%W-%w", "2019-52-2", utc, &tp));
EXPECT_EQ(exp, tp);
}
TEST(Parse, WeekYearShift) {
// %U/%W conversions with week values in {0, 52, 53} can slip
// into the previous/following calendar years.
const time_zone utc = utc_time_zone();
time_point<absl::time_internal::cctz::seconds> tp;
auto exp = convert(civil_second(2019, 12, 31, 0, 0, 0), utc);
EXPECT_TRUE(parse("%Y-%U-%u", "2020-00-2", utc, &tp));
EXPECT_EQ(exp, tp);
EXPECT_TRUE(parse("%Y-%W-%w", "2020-00-2", utc, &tp));
EXPECT_EQ(exp, tp);
exp = convert(civil_second(2021, 1, 1, 0, 0, 0), utc);
EXPECT_TRUE(parse("%Y-%U-%u", "2020-52-5", utc, &tp));
EXPECT_EQ(exp, tp);
EXPECT_TRUE(parse("%Y-%W-%w", "2020-52-5", utc, &tp));
EXPECT_EQ(exp, tp);
} }
TEST(Parse, MaxRange) { TEST(Parse, MaxRange) {

View file

@ -15,6 +15,7 @@
#include "time_zone_impl.h" #include "time_zone_impl.h"
#include <deque> #include <deque>
#include <memory>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
@ -48,17 +49,16 @@ std::mutex& TimeZoneMutex() {
time_zone time_zone::Impl::UTC() { return time_zone(UTCImpl()); } time_zone time_zone::Impl::UTC() { return time_zone(UTCImpl()); }
bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) { bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) {
const time_zone::Impl* const utc_impl = UTCImpl(); const Impl* const utc_impl = UTCImpl();
// First check for UTC (which is never a key in time_zone_map). // Check for UTC (which is never a key in time_zone_map).
auto offset = seconds::zero(); auto offset = seconds::zero();
if (FixedOffsetFromName(name, &offset) && offset == seconds::zero()) { if (FixedOffsetFromName(name, &offset) && offset == seconds::zero()) {
*tz = time_zone(utc_impl); *tz = time_zone(utc_impl);
return true; return true;
} }
// Then check, under a shared lock, whether the time zone has already // Check whether the time zone has already been loaded.
// been loaded. This is the common path. TODO: Move to shared_mutex.
{ {
std::lock_guard<std::mutex> lock(TimeZoneMutex()); std::lock_guard<std::mutex> lock(TimeZoneMutex());
if (time_zone_map != nullptr) { if (time_zone_map != nullptr) {
@ -70,20 +70,15 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) {
} }
} }
// Now check again, under an exclusive lock. // Load the new time zone (outside the lock).
std::unique_ptr<const Impl> new_impl(new Impl(name));
// Add the new time zone to the map.
std::lock_guard<std::mutex> lock(TimeZoneMutex()); std::lock_guard<std::mutex> lock(TimeZoneMutex());
if (time_zone_map == nullptr) time_zone_map = new TimeZoneImplByName; if (time_zone_map == nullptr) time_zone_map = new TimeZoneImplByName;
const Impl*& impl = (*time_zone_map)[name]; const Impl*& impl = (*time_zone_map)[name];
if (impl == nullptr) { if (impl == nullptr) { // this thread won any load race
// The first thread in loads the new time zone. impl = new_impl->zone_ ? new_impl.release() : utc_impl;
Impl* new_impl = new Impl(name);
new_impl->zone_ = TimeZoneIf::Load(new_impl->name_);
if (new_impl->zone_ == nullptr) {
delete new_impl; // free the nascent Impl
impl = utc_impl; // and fallback to UTC
} else {
impl = new_impl; // install new time zone
}
} }
*tz = time_zone(impl); *tz = time_zone(impl);
return impl != utc_impl; return impl != utc_impl;
@ -104,14 +99,11 @@ void time_zone::Impl::ClearTimeZoneMapTestOnly() {
} }
} }
time_zone::Impl::Impl(const std::string& name) : name_(name) {} time_zone::Impl::Impl(const std::string& name)
: name_(name), zone_(TimeZoneIf::Load(name_)) {}
const time_zone::Impl* time_zone::Impl::UTCImpl() { const time_zone::Impl* time_zone::Impl::UTCImpl() {
static Impl* utc_impl = [] { static const Impl* utc_impl = new Impl("UTC"); // never fails
Impl* impl = new Impl("UTC");
impl->zone_ = TimeZoneIf::Load(impl->name_); // never fails
return impl;
}();
return utc_impl; return utc_impl;
} }

View file

@ -933,7 +933,7 @@ TEST(MakeTime, Normalization) {
// NOTE: Run this with -ftrapv to detect overflow problems. // NOTE: Run this with -ftrapv to detect overflow problems.
TEST(MakeTime, SysSecondsLimits) { TEST(MakeTime, SysSecondsLimits) {
const char RFC3339[] = "%Y-%m-%dT%H:%M:%S%Ez"; const char RFC3339[] = "%Y-%m-%d%ET%H:%M:%S%Ez";
const time_zone utc = utc_time_zone(); const time_zone utc = utc_time_zone();
const time_zone east = fixed_time_zone(chrono::hours(14)); const time_zone east = fixed_time_zone(chrono::hours(14));
const time_zone west = fixed_time_zone(-chrono::hours(14)); const time_zone west = fixed_time_zone(-chrono::hours(14));

View file

@ -83,7 +83,8 @@ ZoneInfoSourceFactory default_factory = DefaultFactory;
"@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
"@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
"@@ZA") "@@ZA")
#elif defined(_M_IA_64) || defined(_M_AMD64) || defined(_M_ARM64) #elif defined(_M_IA_64) || defined(_M_AMD64) || defined(_M_ARM) || \
defined(_M_ARM64)
#pragma comment( \ #pragma comment( \
linker, \ linker, \
"/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \ "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \

View file

@ -1203,18 +1203,15 @@ struct tm ToTM(Time t, TimeZone tz);
// time with UTC offset. Also note the use of "%Y": RFC3339 mandates that // time with UTC offset. Also note the use of "%Y": RFC3339 mandates that
// years have exactly four digits, but we allow them to take their natural // years have exactly four digits, but we allow them to take their natural
// width. // width.
ABSL_DLL extern const char ABSL_DLL extern const char RFC3339_full[]; // %Y-%m-%d%ET%H:%M:%E*S%Ez
RFC3339_full[]; // %Y-%m-%dT%H:%M:%E*S%Ez ABSL_DLL extern const char RFC3339_sec[]; // %Y-%m-%d%ET%H:%M:%S%Ez
ABSL_DLL extern const char RFC3339_sec[]; // %Y-%m-%dT%H:%M:%S%Ez
// RFC1123_full // RFC1123_full
// RFC1123_no_wday // RFC1123_no_wday
// //
// FormatTime()/ParseTime() format specifiers for RFC1123 date/time strings. // FormatTime()/ParseTime() format specifiers for RFC1123 date/time strings.
ABSL_DLL extern const char ABSL_DLL extern const char RFC1123_full[]; // %a, %d %b %E4Y %H:%M:%S %z
RFC1123_full[]; // %a, %d %b %E4Y %H:%M:%S %z ABSL_DLL extern const char RFC1123_no_wday[]; // %d %b %E4Y %H:%M:%S %z
ABSL_DLL extern const char
RFC1123_no_wday[]; // %d %b %E4Y %H:%M:%S %z
// FormatTime() // FormatTime()
// //
@ -1229,6 +1226,7 @@ ABSL_DLL extern const char
// - %E#f - Fractional seconds with # digits of precision // - %E#f - Fractional seconds with # digits of precision
// - %E*f - Fractional seconds with full precision (a literal '*') // - %E*f - Fractional seconds with full precision (a literal '*')
// - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999) // - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999)
// - %ET - The RFC3339 "date-time" separator "T"
// //
// Note that %E0S behaves like %S, and %E0f produces no characters. In // Note that %E0S behaves like %S, and %E0f produces no characters. In
// contrast %E*f always produces at least one digit, which may be '0'. // contrast %E*f always produces at least one digit, which may be '0'.
@ -1271,7 +1269,8 @@ inline std::ostream& operator<<(std::ostream& os, Time t) {
// returns the corresponding `absl::Time`. Uses strftime()-like formatting // returns the corresponding `absl::Time`. Uses strftime()-like formatting
// options, with the same extensions as FormatTime(), but with the // options, with the same extensions as FormatTime(), but with the
// exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f. %Ez // exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f. %Ez
// and %E*z also accept the same inputs. // and %E*z also accept the same inputs, which (along with %z) includes
// 'z' and 'Z' as synonyms for +00:00. %ET accepts either 'T' or 't'.
// //
// %Y consumes as many numeric characters as it can, so the matching data // %Y consumes as many numeric characters as it can, so the matching data
// should always be terminated with a non-numeric. %E4Y always consumes // should always be terminated with a non-numeric. %E4Y always consumes

View file

@ -27,22 +27,17 @@
// `Span<const T>` when such types may be difficult to identify due to issues // `Span<const T>` when such types may be difficult to identify due to issues
// with implicit conversion. // with implicit conversion.
// //
// The C++ standards committee currently has a proposal for a `std::span` type, // The C++20 draft standard includes a `std::span` type. As of June 2020, the
// (http://wg21.link/p0122), which is not yet part of the standard (though may // differences between `absl::Span` and `std::span` are:
// become part of C++20). As of August 2017, the differences between // * `absl::Span` has `operator==` (which is likely a design bug,
// `absl::Span` and this proposal are: // per https://abseil.io/blog/20180531-regular-types)
// * `absl::Span` uses `size_t` for `size_type`
// * `absl::Span` has no `operator()`
// * `absl::Span` has no constructors for `std::unique_ptr` or
// `std::shared_ptr`
// * `absl::Span` has the factory functions `MakeSpan()` and // * `absl::Span` has the factory functions `MakeSpan()` and
// `MakeConstSpan()` // `MakeConstSpan()`
// * `absl::Span` has `front()` and `back()` methods
// * bounds-checked access to `absl::Span` is accomplished with `at()` // * bounds-checked access to `absl::Span` is accomplished with `at()`
// * `absl::Span` has compiler-provided move and copy constructors and // * `absl::Span` has compiler-provided move and copy constructors and
// assignment. This is due to them being specified as `constexpr`, but that // assignment. This is due to them being specified as `constexpr`, but that
// implies const in C++11. // implies const in C++11.
// * `absl::Span` has no `element_type` or `index_type` typedefs // * `absl::Span` has no `element_type` typedef
// * A read-only `absl::Span<const T>` can be implicitly constructed from an // * A read-only `absl::Span<const T>` can be implicitly constructed from an
// initializer list. // initializer list.
// * `absl::Span` has no `bytes()`, `size_bytes()`, `as_bytes()`, or // * `absl::Span` has no `bytes()`, `size_bytes()`, `as_bytes()`, or

View file

@ -78,7 +78,6 @@ for std in ${STD}; do
/usr/local/bin/bazel test ... \ /usr/local/bin/bazel test ... \
--compilation_mode="${compilation_mode}" \ --compilation_mode="${compilation_mode}" \
--copt="${exceptions_mode}" \ --copt="${exceptions_mode}" \
--copt="-DDYNAMIC_ANNOTATIONS_ENABLED=1" \
--copt="-DADDRESS_SANITIZER" \ --copt="-DADDRESS_SANITIZER" \
--copt="-DUNDEFINED_BEHAVIOR_SANITIZER" \ --copt="-DUNDEFINED_BEHAVIOR_SANITIZER" \
--copt="-fsanitize=address" \ --copt="-fsanitize=address" \