Squashed 'third_party/abseil_cpp/' changes from 768eb2ca2..ccdbb5941

ccdbb5941 Export of internal Abseil changes
01f5f81f9 Export of internal Abseil changes
2c92bdc7c Export of internal Abseil changes
e7ebf9803 Export of internal Abseil changes
2eba343b5 Export of internal Abseil changes
a8b03d90e Export of internal Abseil changes
1d31b5c36 Export of internal Abseil changes
da3a87690 Export of internal Abseil changes
8faf20461 Exclude empty directories (#697)
2069dc796 Export of internal Abseil changes
4832bf6bf Added a BUILD file in root to expose license. (#695)
af8f994af Export of internal Abseil changes
33caf1097 Export of internal Abseil changes
cf1a02e2d Export of internal Abseil changes

git-subtree-dir: third_party/abseil_cpp
git-subtree-split: ccdbb5941f992fabda7eae3ce72f55efc17c826a
This commit is contained in:
Vincent Ambo 2020-06-17 14:53:10 +01:00
parent 768eb2ca28
commit 8f2828c4b4
97 changed files with 3546 additions and 2316 deletions

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

View file

@ -99,10 +99,8 @@ if(${ABSL_RUN_TESTS})
# on the command line
include(CTest)
enable_testing()
endif()
## check targets
if(BUILD_TESTING)
## check targets
if (NOT ABSL_USE_EXTERNAL_GOOGLETEST)
set(absl_gtest_build_dir ${CMAKE_BINARY_DIR}/googletest-build)
if(${ABSL_USE_GOOGLETEST_HEAD})
@ -174,5 +172,7 @@ if(ABSL_ENABLE_INSTALL)
FILES_MATCHING
PATTERN "*.inc"
PATTERN "*.h"
)
PATTERN "copts" EXCLUDE
PATTERN "testdata" EXCLUDE
)
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.
http_archive(
name = "com_google_googletest",
urls = ["https://github.com/google/googletest/archive/b6cd405286ed8635ece71c72f118e659f4ade3fb.zip"], # 2019-01-07
strip_prefix = "googletest-b6cd405286ed8635ece71c72f118e659f4ade3fb",
sha256 = "ff7a82736e158c077e76188232eac77913a15dac0b22508c390ab3f88e6d6d86",
name = "com_google_googletest",
urls = ["https://github.com/google/googletest/archive/011959aafddcd30611003de96cfd8d7a7685c700.zip"], # 2020-05-14T00:36:05Z
strip_prefix = "googletest-011959aafddcd30611003de96cfd8d7a7685c700",
sha256 = "6a5d7d63cd6e0ad2a7130471105a3b83799a7a2b14ef7ec8d742b54f01a4833c",
)
# Google benchmark.

View file

@ -943,9 +943,10 @@ void c_partial_sort(
// c_partial_sort_copy()
//
// Container-based version of the <algorithm> `std::partial_sort_copy()`
// function to sort elements within a container such that elements before
// `middle` are sorted in ascending order, and return the result within an
// iterator.
// function to sort the elements in the given range `result` within the larger
// `sequence` in ascending order (and using `result` as the output parameter).
// 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>
container_algorithm_internal::ContainerIter<RandomAccessContainer>
c_partial_sort_copy(const C& sequence, RandomAccessContainer& result) {

View file

@ -115,10 +115,18 @@ cc_library(
cc_library(
name = "dynamic_annotations",
srcs = ["dynamic_annotations.cc"],
hdrs = ["dynamic_annotations.h"],
srcs = [
"dynamic_annotations.cc",
"internal/dynamic_annotations.h",
],
hdrs = [
"dynamic_annotations.h",
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":config",
],
)
cc_library(
@ -791,3 +799,16 @@ cc_test(
"@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"
SRCS
"dynamic_annotations.cc"
"internal/dynamic_annotations.h"
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
absl::config
PUBLIC
)
@ -698,3 +701,16 @@ absl_cc_test(
absl::fast_type_id
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"
#ifndef __has_feature
#define __has_feature(x) 0
#endif
/* Compiler-based ThreadSanitizer defines
DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL = 1
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
# define DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL 0
#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 __has_feature(memory_sanitizer)
#include <sanitizer/msan_interface.h>
#endif
#ifdef __cplusplus
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) {
#ifdef RUNNING_ON_VALGRIND
@ -95,21 +40,21 @@ static int GetRunningOnValgrind(void) {
return 0;
}
/* See the comments in dynamic_annotations.h */
// See the comments in dynamic_annotations.h
int RunningOnValgrind(void) {
static volatile int running_on_valgrind = -1;
int local_running_on_valgrind = running_on_valgrind;
/* C doesn't have thread-safe initialization of statics, and we
don't want to depend on pthread_once here, so hack it. */
// C doesn't have thread-safe initialization of statics, and we
// don't want to depend on pthread_once here, so hack it.
ANNOTATE_BENIGN_RACE(&running_on_valgrind, "safe hack");
if (local_running_on_valgrind == -1)
running_on_valgrind = local_running_on_valgrind = GetRunningOnValgrind();
return local_running_on_valgrind;
}
/* See the comments in dynamic_annotations.h */
// See the comments in dynamic_annotations.h
double ValgrindSlowdown(void) {
/* Same initialization hack as in RunningOnValgrind(). */
// Same initialization hack as in RunningOnValgrind().
static volatile double slowdown = 0.0;
double local_slowdown = slowdown;
ANNOTATE_BENIGN_RACE(&slowdown, "safe hack");
@ -123,7 +68,5 @@ double ValgrindSlowdown(void) {
return local_slowdown;
}
#ifdef __cplusplus
} // 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.
*
* 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.
// 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.
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. */
// 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., ABSL_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_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
# 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
#if DYNAMIC_ANNOTATIONS_ENABLED != 0
/* -------------------------------------------------------------
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. */
#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
/* 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
#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
/* Report that the lock at address "lock" is about to be destroyed. */
#define ANNOTATE_RWLOCK_DESTROY(lock) \
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) \
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
// 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
#if (DYNAMIC_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 */
#ifndef ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED
#define ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED 0
#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
extern "C" {
#endif
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);
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
#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
/* Return non-zero value if running under valgrind.
// -------------------------------------------------------------------------
// Define race annotations.
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 ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 1
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.
*/
// -------------------------------------------------------------
// 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 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);
/* ValgrindSlowdown returns:
* 1.0, if (RunningOnValgrind() == 0)
* 50.0, if (RunningOnValgrind() != 0 && getenv("VALGRIND_SLOWDOWN") == NULL)
* atof(getenv("VALGRIND_SLOWDOWN")) otherwise
This function can be used to scale timeout values:
EXAMPLE:
for (;;) {
DoExpensiveBackgroundTask();
SleepForSeconds(5 * ValgrindSlowdown());
}
*/
// ValgrindSlowdown returns:
// * 1.0, if (RunningOnValgrind() == 0)
// * 50.0, if (RunningOnValgrind() != 0 && getenv("VALGRIND_SLOWDOWN") ==
// NULL)
// * atof(getenv("VALGRIND_SLOWDOWN")) otherwise
// This function can be used to scale timeout values:
// EXAMPLE:
// for (;;) {
// DoExpensiveBackgroundTask();
// SleepForSeconds(5 * ValgrindSlowdown());
// }
//
double ValgrindSlowdown(void);
#ifdef __cplusplus
}
#endif
ABSL_INTERNAL_END_EXTERN_C
/* ANNOTATE_UNPROTECTED_READ is the preferred way to annotate racey reads.
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 */
// -------------------------------------------------------------------------
// 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. */
// 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) \
#define ABSL_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
#define ABSL_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, "")
#define ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid)
#define ABSL_ADDRESS_SANITIZER_REDZONE(name) static_assert(true, "")
#endif // ADDRESS_SANITIZER
/* Undefine the macros intended only in this file. */
#undef ANNOTALYSIS_ENABLED
#undef ANNOTATIONS_ENABLED
#undef ATTRIBUTE_IGNORE_READS_BEGIN
#undef ATTRIBUTE_IGNORE_READS_END
// -------------------------------------------------------------------------
// Undefine the macros intended only for this file.
#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));
}
// Transfers value from slot `src_i` in `src` to slot `dest_i` in `this`.
void transfer(const size_type dest_i, const size_type src_i, btree_node *src,
allocator_type *alloc) {
// 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_node, allocator_type *alloc) {
absl::container_internal::SanitizerUnpoisonObject(slot(dest_i));
params_type::transfer(alloc, slot(dest_i), src->slot(src_i));
absl::container_internal::SanitizerPoisonObject(src->slot(src_i));
params_type::transfer(alloc, slot(dest_i), src_node->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
// value j in dest_node.
void uninitialized_move_n(const size_type n, const size_type i,
const size_type j, btree_node *dest_node,
allocator_type *alloc) {
// Transfers `n` values starting at value `src_i` in `src_node` into the
// values starting at value `dest_i` in `this`.
void transfer_n(const size_type n, const size_type dest_i,
const size_type src_i, btree_node *src_node,
allocator_type *alloc) {
absl::container_internal::SanitizerUnpoisonMemoryRegion(
dest_node->slot(j), n * sizeof(slot_type));
for (slot_type *src = slot(i), *end = src + n, *dest = dest_node->slot(j);
slot(dest_i), n * sizeof(slot_type));
for (slot_type *src = src_node->slot(src_i), *end = src + n,
*dest = slot(dest_i);
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.
void value_destroy_n(const size_type i, const size_type n,
allocator_type *alloc) {
for (int j = 0; j < n; ++j) {
value_destroy(i + j, alloc);
// Same as above, except that we start at the end and work our way to the
// beginning.
void transfer_n_backward(const size_type n, const size_type dest_i,
const size_type src_i, btree_node *src_node,
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>
@ -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
// place.
if (i < finish()) {
value_init(finish(), alloc, slot(finish() - 1));
for (size_type j = finish() - 1; j > i; --j)
params_type::move(alloc, slot(j - 1), slot(j));
value_destroy(i, alloc);
transfer_n_backward(finish() - i, /*dest_i=*/i + 1, /*src_i=*/i, this,
alloc);
}
value_init(i, alloc, std::forward<Args>(args)...);
set_finish(finish() + 1);
@ -1564,7 +1581,9 @@ template <typename P>
inline void btree_node<P>::remove_values_ignore_children(
const int i, const int to_erase, allocator_type *alloc) {
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);
}
@ -1579,22 +1598,17 @@ void btree_node<P>::rebalance_right_to_left(const int to_move,
assert(to_move <= right->count());
// 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.
right->uninitialized_move_n(to_move - 1, right->start(), finish() + 1, this,
alloc);
transfer_n(to_move - 1, finish() + 1, right->start(), right, alloc);
// 3) Move the new delimiting value to the parent from the right node.
params_type::move(alloc, right->slot(to_move - 1),
parent()->slot(position()));
parent()->transfer(position(), right->start() + to_move - 1, right, alloc);
// 4) Shift the values in the right node to their correct position.
params_type::move(alloc, right->slot(to_move), right->finish_slot(),
right->start_slot());
// 5) Destroy the now-empty to_move entries in the right node.
right->value_destroy_n(right->finish() - to_move, to_move, alloc);
// 4) Shift the values in the right node to their correct positions.
right->transfer_n(right->count() - to_move, right->start(),
right->start() + to_move, right, alloc);
if (!leaf()) {
// 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
// parent, and the remaining empty left node entries are destroyed.
if (right->count() >= to_move) {
// The original location of the right->count() values are sufficient to hold
// the new to_move entries from the parent and left node.
// 1) Shift existing values in the right node to their correct positions.
right->transfer_n_backward(right->count(), right->start() + to_move,
right->start(), right, alloc);
// 1) Shift existing values in the right node to their correct positions.
right->uninitialized_move_n(to_move, right->finish() - to_move,
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.
right->transfer(right->start() + to_move - 1, position(), parent(), alloc);
// 2) Move the delimiting value in the parent to the right node.
params_type::move(alloc, parent()->slot(position()),
right->slot(to_move - 1));
// 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());
}
// 3) Move the (to_move - 1) values from the left node to the right node.
right->transfer_n(to_move - 1, right->start(), finish() - (to_move - 1), this,
alloc);
// 4) Move the new delimiting value to the parent from the left node.
params_type::move(alloc, slot(finish() - to_move),
parent()->slot(position()));
// 5) Destroy the now-empty to_move entries in the left node.
value_destroy_n(finish() - to_move, to_move, alloc);
parent()->transfer(position(), finish() - to_move, this, alloc);
if (!leaf()) {
// 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);
// Move values from the left sibling to the right sibling.
uninitialized_move_n(dest->count(), finish(), dest->start(), dest, alloc);
// Destroy the now-empty entries in the left node.
value_destroy_n(finish(), dest->count(), alloc);
dest->transfer_n(dest->count(), dest->start(), finish(), this, alloc);
// The split key is the largest value in the left sibling.
--mutable_finish();
@ -1746,11 +1722,7 @@ void btree_node<P>::merge(btree_node *src, allocator_type *alloc) {
value_init(finish(), alloc, parent()->slot(position()));
// Move the values from the right to the left node.
src->uninitialized_move_n(src->count(), src->start(), finish() + 1, this,
alloc);
// Destroy the now-empty entries in the right node.
src->value_destroy_n(src->start(), src->count(), alloc);
transfer_n(src->count(), finish() + 1, src->start(), src, alloc);
if (!leaf()) {
// 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.
node_type *old_root = root();
node_type *new_root = iter.node;
for (int i = old_root->start(), f = old_root->finish(); i < f; ++i) {
new_root->transfer(i, i, old_root, alloc);
}
new_root->transfer_n(old_root->count(), new_root->start(),
old_root->start(), old_root, alloc);
new_root->set_finish(old_root->finish());
old_root->set_finish(old_root->start());
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);
// Test that we do not move from rvalue arguments if an insertion does not

View file

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

View file

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

View file

@ -20,6 +20,10 @@
#include <unistd.h>
#endif
#ifdef __APPLE__
#include <sys/ucontext.h>
#endif
#include <csignal>
#include <cstdio>
@ -64,6 +68,32 @@ void* GetProgramCounter(void* vuc) {
return reinterpret_cast<void*>(context->uc_mcontext.gregs[16]);
#else
#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
}
#elif defined(__akaros__)

View file

@ -28,6 +28,27 @@
#define ABSL_STACKTRACE_INL_HEADER \
"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__)
#if !defined(NO_FRAME_POINTER)
@ -40,7 +61,7 @@
# elif defined(__aarch64__)
#define ABSL_STACKTRACE_INL_HEADER \
"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.
#define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_generic-inl.inc"

View file

@ -22,6 +22,7 @@
#include <cstdint>
#include "absl/base/config.h"
#include "absl/strings/string_view.h"
#ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE
#error ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE cannot be directly set
@ -45,7 +46,7 @@ namespace debugging_internal {
//
// This is not async-signal-safe.
bool ForEachSection(int fd,
const std::function<bool(const std::string& name,
const std::function<bool(absl::string_view name,
const ElfW(Shdr) &)>& callback);
// 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
#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 {
ABSL_NAMESPACE_BEGIN
namespace debugging_internal {

View file

@ -20,6 +20,8 @@
// The Windows Symbolizer only works if PDB files containing the debug info
// are available to the program at runtime.
#include "absl/debugging/symbolize_win32.inc"
#elif defined(__APPLE__)
#include "absl/debugging/symbolize_darwin.inc"
#else
#include "absl/debugging/symbolize_unimplemented.inc"
#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/debugging/internal/demangle.h"
#include "absl/debugging/internal/vdso_support.h"
#include "absl/strings/string_view.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@ -498,7 +499,7 @@ static ABSL_ATTRIBUTE_NOINLINE bool GetSectionHeaderByType(
const int kMaxSectionNameLen = 64;
bool ForEachSection(int fd,
const std::function<bool(const std::string &name,
const std::function<bool(absl::string_view name,
const ElfW(Shdr) &)> &callback) {
ElfW(Ehdr) elf_header;
if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) {
@ -520,7 +521,7 @@ bool ForEachSection(int fd,
return false;
}
off_t name_offset = shstrtab.sh_offset + out.sh_name;
char header_name[kMaxSectionNameLen + 1];
char header_name[kMaxSectionNameLen];
ssize_t n_read =
ReadFromOffset(fd, &header_name, kMaxSectionNameLen, name_offset);
if (n_read == -1) {
@ -529,9 +530,8 @@ bool ForEachSection(int fd,
// Long read?
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)) {
break;
}

View file

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

View file

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

View file

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

View file

@ -13,21 +13,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/flags/commandlineflag.h"
#include <string>
#include "absl/base/config.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/strings/string_view.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace flags_internal {
FlagStateInterface::~FlagStateInterface() {}
bool CommandLineFlag::IsRetired() const { return false; }
bool CommandLineFlag::ParseFrom(absl::string_view value, std::string* error) {
return ParseFrom(value, flags_internal::SET_FLAGS_VALUE,
flags_internal::kProgrammaticChange, error);
flags_internal::kProgrammaticChange, *error);
}
namespace flags_internal {
FlagStateInterface::~FlagStateInterface() {}
} // namespace flags_internal
ABSL_NAMESPACE_END
} // 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
// limitations under the License.
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/commandlineflag.h"
#include <memory>
#include <string>
#include "gtest/gtest.h"
#include "absl/flags/flag.h"
#include "absl/flags/internal/commandlineflag.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/memory/memory.h"
#include "absl/strings/match.h"
@ -33,6 +34,10 @@ ABSL_FLAG(std::string, string_flag, "dflt",
absl::StrCat("string_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 flags = absl::flags_internal;
@ -46,7 +51,7 @@ class CommandLineFlagTest : public testing::Test {
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(); }
private:
@ -59,56 +64,49 @@ class CommandLineFlagTest : public testing::Test {
return std::string(fname);
}
std::unique_ptr<flags::FlagSaver> flag_saver_;
std::unique_ptr<absl::FlagSaver> flag_saver_;
};
TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) {
auto* flag_01 = flags::FindCommandLineFlag("int_flag");
auto* flag_01 = absl::FindCommandLineFlag("int_flag");
ASSERT_TRUE(flag_01);
EXPECT_EQ(flag_01->Name(), "int_flag");
EXPECT_EQ(flag_01->Help(), "int_flag help");
EXPECT_TRUE(!flag_01->IsRetired());
EXPECT_TRUE(flag_01->IsOfType<int>());
EXPECT_TRUE(
absl::EndsWith(flag_01->Filename(),
"absl/flags/internal/commandlineflag_test.cc"))
EXPECT_TRUE(!flag_01->IsOfType<bool>());
EXPECT_TRUE(!flag_01->IsOfType<std::string>());
EXPECT_TRUE(absl::EndsWith(flag_01->Filename(),
"absl/flags/commandlineflag_test.cc"))
<< flag_01->Filename();
auto* flag_02 = flags::FindCommandLineFlag("string_flag");
auto* flag_02 = absl::FindCommandLineFlag("string_flag");
ASSERT_TRUE(flag_02);
EXPECT_EQ(flag_02->Name(), "string_flag");
EXPECT_EQ(flag_02->Help(), "string_flag help");
EXPECT_TRUE(!flag_02->IsRetired());
EXPECT_TRUE(flag_02->IsOfType<std::string>());
EXPECT_TRUE(
absl::EndsWith(flag_02->Filename(),
"absl/flags/internal/commandlineflag_test.cc"))
EXPECT_TRUE(!flag_02->IsOfType<bool>());
EXPECT_TRUE(!flag_02->IsOfType<int>());
EXPECT_TRUE(absl::EndsWith(flag_02->Filename(),
"absl/flags/commandlineflag_test.cc"))
<< 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) {
absl::SetFlag(&FLAGS_int_flag, 301);
auto* flag_01 = flags::FindCommandLineFlag("int_flag");
absl::SetFlag(&FLAGS_int_flag2, 301);
auto* flag_01 = absl::FindCommandLineFlag("int_flag2");
ASSERT_TRUE(flag_01);
EXPECT_EQ(flag_01->CurrentValue(), "301");
EXPECT_EQ(flag_01->DefaultValue(), "201");
absl::SetFlag(&FLAGS_string_flag, "new_str_value");
auto* flag_02 = flags::FindCommandLineFlag("string_flag");
absl::SetFlag(&FLAGS_string_flag2, "new_str_value");
auto* flag_02 = absl::FindCommandLineFlag("string_flag2");
ASSERT_TRUE(flag_02);
EXPECT_EQ(flag_02->CurrentValue(), "new_str_value");
@ -120,62 +118,62 @@ TEST_F(CommandLineFlagTest, TestValueAccessMethods) {
TEST_F(CommandLineFlagTest, TestParseFromCurrentValue) {
std::string err;
auto* flag_01 = flags::FindCommandLineFlag("int_flag");
auto* flag_01 = absl::FindCommandLineFlag("int_flag");
EXPECT_FALSE(
flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01));
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_FALSE(
flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01));
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
flag_01, "-123", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange,
&err));
*flag_01, "-123", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange,
err));
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123);
EXPECT_FALSE(
flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01));
EXPECT_TRUE(!flags::PrivateHandleAccessor::ParseFrom(
flag_01, "xyz", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange,
&err));
*flag_01, "xyz", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange,
err));
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123);
EXPECT_EQ(err, "Illegal value 'xyz' specified for flag 'int_flag'");
EXPECT_FALSE(
flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01));
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(err, "Illegal value 'A1' specified for flag 'int_flag'");
EXPECT_FALSE(
flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01));
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
flag_01, "0x10", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange,
&err));
*flag_01, "0x10", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange,
err));
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 16);
EXPECT_FALSE(
flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01));
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_TRUE(flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01));
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'");
auto* flag_02 = flags::FindCommandLineFlag("string_flag");
auto* flag_02 = absl::FindCommandLineFlag("string_flag");
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
flag_02, "xyz", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange,
&err));
*flag_02, "xyz", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange,
err));
EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "xyz");
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), "");
}
@ -184,18 +182,18 @@ TEST_F(CommandLineFlagTest, TestParseFromCurrentValue) {
TEST_F(CommandLineFlagTest, TestParseFromDefaultValue) {
std::string err;
auto* flag_01 = flags::FindCommandLineFlag("int_flag");
auto* flag_01 = absl::FindCommandLineFlag("int_flag");
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
flag_01, "111", flags::SET_FLAGS_DEFAULT, flags::kProgrammaticChange,
&err));
*flag_01, "111", flags::SET_FLAGS_DEFAULT, flags::kProgrammaticChange,
err));
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(
flag_02, "abc", flags::SET_FLAGS_DEFAULT, flags::kProgrammaticChange,
&err));
*flag_02, "abc", flags::SET_FLAGS_DEFAULT, flags::kProgrammaticChange,
err));
EXPECT_EQ(flag_02->DefaultValue(), "abc");
}
@ -204,28 +202,28 @@ TEST_F(CommandLineFlagTest, TestParseFromDefaultValue) {
TEST_F(CommandLineFlagTest, TestParseFromIfDefault) {
std::string err;
auto* flag_01 = flags::FindCommandLineFlag("int_flag");
auto* flag_01 = absl::FindCommandLineFlag("int_flag");
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
flag_01, "22", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange,
&err))
*flag_01, "22", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange,
err))
<< err;
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 22);
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
flag_01, "33", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange,
&err));
*flag_01, "33", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange,
err));
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 22);
// EXPECT_EQ(err, "ERROR: int_flag is already set to 22");
// Reset back to default value
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
flag_01, "201", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange,
&err));
*flag_01, "201", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange,
err));
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
flag_01, "33", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange,
&err));
*flag_01, "33", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange,
err));
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 201);
// EXPECT_EQ(err, "ERROR: int_flag is already set to 201");
}

View file

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

View file

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

View file

@ -33,14 +33,11 @@
#include <type_traits>
#include "absl/base/attributes.h"
#include "absl/base/casts.h"
#include "absl/base/config.h"
#include "absl/base/optimization.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/registry.h"
#include "absl/flags/marshalling.h"
#include "absl/strings/string_view.h"
namespace absl {
@ -111,12 +108,12 @@ class Flag {
impl_(nullptr) {}
#endif
flags_internal::Flag<T>* GetImpl() const {
flags_internal::Flag<T>& GetImpl() const {
if (!inited_.load(std::memory_order_acquire)) {
absl::MutexLock l(flags_internal::GetGlobalConstructionGuard());
if (inited_.load(std::memory_order_acquire)) {
return impl_;
return *impl_;
}
impl_ = new flags_internal::Flag<T>(
@ -128,28 +125,30 @@ class Flag {
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.
// See https://abseil.io/docs/cpp/guides/flags
bool IsRetired() const { return GetImpl()->IsRetired(); }
absl::string_view Name() const { return GetImpl()->Name(); }
std::string Help() const { return GetImpl()->Help(); }
bool IsModified() const { return GetImpl()->IsModified(); }
bool IsRetired() const { return GetImpl().IsRetired(); }
absl::string_view Name() const { return GetImpl().Name(); }
std::string Help() const { return GetImpl().Help(); }
bool IsModified() const { return GetImpl().IsModified(); }
bool IsSpecifiedOnCommandLine() const {
return GetImpl()->IsSpecifiedOnCommandLine();
return GetImpl().IsSpecifiedOnCommandLine();
}
std::string Filename() const { return GetImpl()->Filename(); }
std::string DefaultValue() const { return GetImpl()->DefaultValue(); }
std::string CurrentValue() const { return GetImpl()->CurrentValue(); }
std::string Filename() const { return GetImpl().Filename(); }
std::string DefaultValue() const { return GetImpl().DefaultValue(); }
std::string CurrentValue() const { return GetImpl().CurrentValue(); }
template <typename U>
inline bool IsOfType() const {
return GetImpl()->template IsOfType<U>();
return GetImpl().template IsOfType<U>();
}
T Get() const { return GetImpl()->Get(); }
void Set(const T& v) { GetImpl()->Set(v); }
void InvokeCallback() { GetImpl()->InvokeCallback(); }
T Get() const { return GetImpl().Get(); }
void Set(const T& v) { GetImpl().Set(v); }
void InvokeCallback() { GetImpl().InvokeCallback(); }
const CommandLineFlag& Reflect() const { return GetImpl().Reflect(); }
// The data members are logically private, but they need to be public for
// this to be an aggregate type.
@ -205,6 +204,21 @@ void SetFlag(absl::Flag<T>* flag, const V& v) {
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
} // namespace absl
@ -265,27 +279,29 @@ ABSL_NAMESPACE_END
// -----------------------------------------------------------------------------
// 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
#define ABSL_FLAG_IMPL_FLAGNAME(txt) ""
#define ABSL_FLAG_IMPL_FILENAME() ""
#if !defined(_MSC_VER) || defined(__clang__)
#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
absl::flags_internal::FlagRegistrar<T, false>(&flag)
#else
#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
absl::flags_internal::FlagRegistrar<T, false>(flag.GetImpl())
#endif
absl::flags_internal::FlagRegistrar<T, false>(ABSL_FLAG_IMPL_FLAG_PTR(flag))
#else
#define ABSL_FLAG_IMPL_FLAGNAME(txt) txt
#define ABSL_FLAG_IMPL_FILENAME() __FILE__
#if !defined(_MSC_VER) || defined(__clang__)
#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
absl::flags_internal::FlagRegistrar<T, true>(&flag)
#else
#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
absl::flags_internal::FlagRegistrar<T, true>(flag.GetImpl())
#endif
absl::flags_internal::FlagRegistrar<T, true>(ABSL_FLAG_IMPL_FLAG_PTR(flag))
#endif
// 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.
// If help message expression is constexpr evaluable compiler will optimize
// away this whole struct.
#define ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, txt) \
struct AbslFlagHelpGenFor##name { \
template <typename T = void> \
static constexpr const char* Const() { \
return absl::flags_internal::HelpConstexprWrap( \
ABSL_FLAG_IMPL_FLAGHELP(txt)); \
} \
static std::string NonConst() { return ABSL_FLAG_IMPL_FLAGHELP(txt); } \
}
// TODO(rogeeff): place these generated structs into local namespace and apply
// ABSL_INTERNAL_UNIQUE_SHORT_NAME.
// TODO(rogeeff): Apply __attribute__((nodebug)) to FLAGS_help_storage_##name
#define ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, txt) \
struct AbslFlagHelpGenFor##name { \
/* The expression is run in the caller as part of the */ \
/* default value argument. That keeps temporaries alive */ \
/* 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) \
struct AbslFlagDefaultGenFor##name { \
@ -317,40 +342,23 @@ ABSL_NAMESPACE_END
static void Gen(void* p) { \
new (p) Type(AbslFlagDefaultGenFor##name{}.value); \
} \
}
};
// ABSL_FLAG_IMPL
//
// 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
// 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 */ {} \
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(), \
absl::flags_internal::HelpArg<AbslFlagHelpGenFor##name>(0), \
absl::flags_internal::DefaultArg<Type, AbslFlagDefaultGenFor##name>(0)}; \
extern absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name; \
absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name = \
#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(), \
ABSL_FLAG_IMPL_HELP_ARG(name), ABSL_FLAG_IMPL_DEFAULT_ARG(Type, name)}; \
extern absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name; \
absl::flags_internal::FlagRegistrarEmpty FLAGS_no##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
//

View file

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

View file

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

View file

@ -16,14 +16,8 @@
#ifndef ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
#define ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
#include <memory>
#include <string>
#include "absl/base/config.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 {
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 we'll have to resort to using actual RTTI implementation if it is
// available.
using FlagFastTypeId = base_internal::FastTypeIdType;
using FlagFastTypeId = absl::base_internal::FastTypeIdType;
// Options that control SetCommandLineOptionWithMode.
enum FlagSettingMode {
@ -67,117 +61,6 @@ class FlagStateInterface {
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
ABSL_NAMESPACE_END
} // namespace absl

View file

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

View file

@ -16,31 +16,36 @@
#ifndef ABSL_FLAGS_INTERNAL_FLAG_H_
#define ABSL_FLAGS_INTERNAL_FLAG_H_
#include <stddef.h>
#include <stdint.h>
#include <atomic>
#include <cstring>
#include <memory>
#include <new>
#include <string>
#include <type_traits>
#include <typeinfo>
#include "absl/base/attributes.h"
#include "absl/base/call_once.h"
#include "absl/base/config.h"
#include "absl/base/optimization.h"
#include "absl/base/thread_annotations.h"
#include "absl/flags/commandlineflag.h"
#include "absl/flags/config.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/registry.h"
#include "absl/flags/marshalling.h"
#include "absl/memory/memory.h"
#include "absl/meta/type_traits.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "absl/utility/utility.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
///////////////////////////////////////////////////////////////////////////////
// Forward declaration of absl::Flag<T> public API.
namespace flags_internal {
template <typename T>
@ -64,12 +69,15 @@ void SetFlag(absl::Flag<T>* flag, const T& v);
template <typename T, typename 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
// by function specific to that type with a signature matching FlagOpFn.
namespace flags_internal {
enum class FlagOp {
kAlloc,
kDelete,
@ -168,6 +176,28 @@ inline const std::type_info* GenRuntimeTypeId() {
// cases.
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 {
constexpr explicit FlagHelpMsg(const char* help_msg) : literal(help_msg) {}
constexpr explicit FlagHelpMsg(HelpGenFunc help_gen) : gen_func(help_gen) {}
@ -185,40 +215,28 @@ struct FlagHelpArg {
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
// 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
// first overload if possible. If T::Const is evaluatable on constexpr
// context (see non template int parameter below) we'll choose first overload.
// In this case the help message expression is immediately evaluated and is used
// to construct the absl::Flag. No additionl code is generated by ABSL_FLAG.
// Otherwise SFINAE kicks in and first overload is dropped from the
// AbslFlagHelpGenFor##name as Gen and integer 0 as a single argument to prefer
// first overload if possible. If help message is evaluatable on constexpr
// context We'll be able to make FixedCharArray out of it and we'll choose first
// overload. In this case the help message expression is immediately evaluated
// and is used to construct the absl::Flag. No additionl code is generated by
// 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
// overload does not attempt to evaluate the help message expression
// immediately and instead delays the evaluation by returing the function
// pointer (&T::NonConst) genering the help message when necessary. This is
// evaluatable in constexpr context, but the cost is an extra function being
// generated in the ABSL_FLAG code.
template <typename T, int = (T::Const(), 1)>
constexpr FlagHelpArg HelpArg(int) {
return {FlagHelpMsg(T::Const()), FlagHelpKind::kLiteral};
template <typename Gen, size_t N>
constexpr FlagHelpArg HelpArg(const FixedCharArray<N>& value) {
return {FlagHelpMsg(value.value), FlagHelpKind::kLiteral};
}
template <typename T>
constexpr FlagHelpArg HelpArg(char) {
return {FlagHelpMsg(&T::NonConst), FlagHelpKind::kGenFunc};
template <typename Gen>
constexpr FlagHelpArg HelpArg(std::false_type) {
return {FlagHelpMsg(&Gen::NonConst), FlagHelpKind::kGenFunc};
}
///////////////////////////////////////////////////////////////////////////////
@ -364,31 +382,31 @@ struct FlagValue;
template <typename T>
struct FlagValue<T, FlagValueStorageKind::kAlignedBuffer> {
bool Get(T*) const { return false; }
bool Get(T&) const { return false; }
alignas(T) char value[sizeof(T)];
};
template <typename T>
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);
if (ABSL_PREDICT_FALSE(one_word_val == UninitializedFlagValue())) {
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;
}
};
template <typename T>
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);
if (ABSL_PREDICT_FALSE(!two_words_val.IsInitialized())) {
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;
}
};
@ -419,7 +437,7 @@ struct DynValueDeleter {
class FlagState;
class FlagImpl final : public flags_internal::CommandLineFlag {
class FlagImpl final : public CommandLineFlag {
public:
constexpr FlagImpl(const char* name, const char* filename, FlagOpFn op,
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,
// returns new value. Otherwise returns nullptr.
std::unique_ptr<void, DynValueDeleter> TryParse(absl::string_view value,
std::string* err) const
std::string& err) const
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
// Stores the flag value based on the pointer to the source.
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());
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());
// Immutable flag's state.
@ -641,7 +659,7 @@ class Flag {
impl_.AssertValidType(base_internal::FastTypeId<T>(), &GenRuntimeTypeId<T>);
#endif
if (!value_.Get(&u.value)) impl_.Read(&u.value);
if (!value_.Get(u.value)) impl_.Read(&u.value);
return std::move(u.value);
}
void Set(const T& v) {
@ -649,6 +667,13 @@ class Flag {
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
// 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
@ -720,12 +745,12 @@ struct FlagRegistrarEmpty {};
template <typename T, bool do_register>
class FlagRegistrar {
public:
explicit FlagRegistrar(Flag<T>* flag) : flag_(flag) {
if (do_register) flags_internal::RegisterCommandLineFlag(&flag_->impl_);
explicit FlagRegistrar(Flag<T>& flag) : flag_(flag) {
if (do_register) flags_internal::RegisterCommandLineFlag(flag_.impl_);
}
FlagRegistrar OnUpdate(FlagCallbackFunc cb) && {
flag_->impl_.SetCallback(cb);
flag_.impl_.SetCallback(cb);
return *this;
}
@ -735,7 +760,7 @@ class FlagRegistrar {
operator FlagRegistrarEmpty() const { return {}; } // NOLINT
private:
Flag<T>* flag_; // Flag being registered (not owned).
Flag<T>& flag_; // Flag being registered (not owned).
};
} // namespace flags_internal

View file

@ -21,6 +21,7 @@
#include "absl/base/config.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>, fromenv);

View file

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

View file

@ -15,6 +15,14 @@
#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 {
ABSL_NAMESPACE_BEGIN
namespace flags_internal {
@ -24,8 +32,8 @@ FlagFastTypeId PrivateHandleAccessor::TypeId(const CommandLineFlag& flag) {
}
std::unique_ptr<FlagStateInterface> PrivateHandleAccessor::SaveState(
CommandLineFlag* flag) {
return flag->SaveState();
CommandLineFlag& flag) {
return flag.SaveState();
}
bool PrivateHandleAccessor::IsSpecifiedOnCommandLine(
@ -43,12 +51,12 @@ void PrivateHandleAccessor::CheckDefaultValueParsingRoundtrip(
flag.CheckDefaultValueParsingRoundtrip();
}
bool PrivateHandleAccessor::ParseFrom(CommandLineFlag* flag,
bool PrivateHandleAccessor::ParseFrom(CommandLineFlag& flag,
absl::string_view value,
flags_internal::FlagSettingMode set_mode,
flags_internal::ValueSource source,
std::string* error) {
return flag->ParseFrom(value, set_mode, source, error);
std::string& error) {
return flag.ParseFrom(value, set_mode, source, error);
}
} // namespace flags_internal

View file

@ -16,7 +16,13 @@
#ifndef 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/strings/string_view.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@ -31,7 +37,7 @@ class PrivateHandleAccessor {
static FlagFastTypeId TypeId(const CommandLineFlag& flag);
// Access to CommandLineFlag::SaveState.
static std::unique_ptr<FlagStateInterface> SaveState(CommandLineFlag* flag);
static std::unique_ptr<FlagStateInterface> SaveState(CommandLineFlag& flag);
// Access to CommandLineFlag::IsSpecifiedOnCommandLine.
static bool IsSpecifiedOnCommandLine(const CommandLineFlag& flag);
@ -43,9 +49,9 @@ class PrivateHandleAccessor {
// Access to CommandLineFlag::CheckDefaultValueParsingRoundtrip.
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::ValueSource source, std::string* error);
flags_internal::ValueSource source, std::string& error);
};
} // namespace flags_internal

View file

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

View file

@ -17,11 +17,9 @@
#define ABSL_FLAGS_INTERNAL_REGISTRY_H_
#include <functional>
#include <map>
#include <string>
#include "absl/base/config.h"
#include "absl/base/macros.h"
#include "absl/flags/commandlineflag.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/strings/string_view.h"
@ -32,19 +30,16 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
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.
// 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
// 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:
@ -87,36 +82,6 @@ inline bool RetiredFlag(const char* flag_name) {
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
ABSL_NAMESPACE_END
} // 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 <stdint.h>
#include <functional>
#include <map>
#include <ostream>
@ -23,8 +25,8 @@
#include <vector>
#include "absl/base/config.h"
#include "absl/flags/commandlineflag.h"
#include "absl/flags/flag.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/flag.h"
#include "absl/flags/internal/path_util.h"
#include "absl/flags/internal/private_handle_accessor.h"
@ -107,8 +109,8 @@ class FlagHelpPrettyPrinter {
public:
// Pretty printer holds on to the std::ostream& reference to direct an output
// to that stream.
FlagHelpPrettyPrinter(int max_line_len, std::ostream* out)
: out_(*out),
FlagHelpPrettyPrinter(int max_line_len, std::ostream& out)
: out_(out),
max_line_len_(max_line_len),
line_len_(0),
first_line_(true) {}
@ -182,8 +184,7 @@ class FlagHelpPrettyPrinter {
bool first_line_;
};
void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag,
std::ostream* out) {
void FlagHelpHumanReadable(const CommandLineFlag& flag, std::ostream& out) {
FlagHelpPrettyPrinter printer(80, out); // Max line length is 80.
// 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
// name.
std::map<std::string,
std::map<std::string,
std::vector<const flags_internal::CommandLineFlag*>>>
std::map<std::string, std::vector<const absl::CommandLineFlag*>>>
matching_flags;
flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) {
std::string flag_filename = flag->Filename();
flags_internal::ForEachFlag([&](absl::CommandLineFlag& flag) {
std::string flag_filename = flag.Filename();
// Ignore retired flags.
if (flag->IsRetired()) return;
if (flag.IsRetired()) return;
// 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
if (!filter_cb || !filter_cb(flag_filename)) return;
matching_flags[std::string(flags_internal::Package(flag_filename))]
[flag_filename]
.push_back(flag);
.push_back(&flag);
});
absl::string_view
package_separator; // controls blank lines between packages.
absl::string_view file_separator; // controls blank lines between files.
absl::string_view package_separator; // controls blank lines between packages
absl::string_view file_separator; // controls blank lines between files
for (const auto& package : matching_flags) {
if (format == HelpFormat::kHumanReadable) {
out << package_separator;
@ -303,10 +302,10 @@ void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
// --------------------------------------------------------------------
// 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) {
if (format == HelpFormat::kHumanReadable)
flags_internal::FlagHelpHumanReadable(flag, &out);
flags_internal::FlagHelpHumanReadable(flag, out);
}
// --------------------------------------------------------------------

View file

@ -20,8 +20,8 @@
#include <string>
#include "absl/base/config.h"
#include "absl/flags/commandlineflag.h"
#include "absl/flags/declare.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/strings/string_view.h"
// --------------------------------------------------------------------
@ -37,7 +37,7 @@ enum class HelpFormat {
};
// 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);
// Produces the help messages for all flags matching the filter. A flag matches

View file

@ -21,15 +21,13 @@
#include <string>
#include "gtest/gtest.h"
#include "absl/flags/declare.h"
#include "absl/flags/flag.h"
#include "absl/flags/internal/parse.h"
#include "absl/flags/internal/path_util.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_config.h"
#include "absl/memory/memory.h"
#include "absl/strings/match.h"
#include "absl/strings/string_view.h"
@ -91,7 +89,7 @@ class UsageReportingTest : public testing::Test {
}
private:
flags::FlagSaver flag_saver_;
absl::FlagSaver flag_saver_;
};
// --------------------------------------------------------------------
@ -112,7 +110,7 @@ TEST_F(UsageReportingDeathTest, TestSetProgramUsageMessage) {
// --------------------------------------------------------------------
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;
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) {
const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_02");
const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_02");
std::stringstream test_buf;
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) {
const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_03");
const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_03");
std::stringstream test_buf;
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) {
const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_04");
const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_04");
std::stringstream test_buf;
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) {
const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_05");
const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_05");
std::stringstream test_buf;
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);

View file

@ -74,15 +74,16 @@ static int NumericBase(absl::string_view text) {
}
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);
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*) {
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
return false;
*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*) {
unsigned int val;
if (!ParseFlagImpl(text, &val)) return false;
if (!ParseFlagImpl(text, val)) return false;
if (static_cast<unsigned short>(val) !=
val) // worked, but number out of range
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*) {
return ParseFlagImpl(text, dst);
return ParseFlagImpl(text, *dst);
}
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*) {
return ParseFlagImpl(text, dst);
return ParseFlagImpl(text, *dst);
}
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*) {
return ParseFlagImpl(text, dst);
return ParseFlagImpl(text, *dst);
}
bool AbslParseFlag(absl::string_view text, unsigned long long* dst,
std::string*) {
return ParseFlagImpl(text, dst);
return ParseFlagImpl(text, *dst);
}
// --------------------------------------------------------------------

View file

@ -34,6 +34,7 @@
#include "absl/base/config.h"
#include "absl/base/const_init.h"
#include "absl/base/thread_annotations.h"
#include "absl/flags/commandlineflag.h"
#include "absl/flags/config.h"
#include "absl/flags/flag.h"
#include "absl/flags/internal/commandlineflag.h"
@ -41,8 +42,8 @@
#include "absl/flags/internal/parse.h"
#include "absl/flags/internal/private_handle_accessor.h"
#include "absl/flags/internal/program_name.h"
#include "absl/flags/internal/registry.h"
#include "absl/flags/internal/usage.h"
#include "absl/flags/reflection.h"
#include "absl/flags/usage.h"
#include "absl/flags/usage_config.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
// `value`. If variable is not present in environment returns false, otherwise
// returns true.
bool GetEnvVar(const char* var_name, std::string* var_value) {
bool GetEnvVar(const char* var_name, std::string& var_value) {
#ifdef _WIN32
char buf[1024];
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;
}
*var_value = std::string(buf, get_res);
var_value = std::string(buf, get_res);
#else
const char* val = ::getenv(var_name);
if (val == nullptr) {
return false;
}
*var_value = val;
var_value = val;
#endif
return true;
@ -289,11 +290,11 @@ std::tuple<absl::string_view, absl::string_view, bool> SplitNameAndValue(
// found flag or nullptr
// is negative in case of --nofoo
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;
if (!flag && absl::ConsumePrefix(&flag_name, "no")) {
flag = flags_internal::FindCommandLineFlag(flag_name);
flag = absl::FindCommandLineFlag(flag_name);
is_negative = true;
}
@ -306,17 +307,17 @@ std::tuple<CommandLineFlag*, bool> LocateFlag(absl::string_view flag_name) {
// back.
void CheckDefaultValuesParsingRoundtrip() {
#ifndef NDEBUG
flags_internal::ForEachFlag([&](CommandLineFlag* flag) {
if (flag->IsRetired()) return;
flags_internal::ForEachFlag([&](CommandLineFlag& flag) {
if (flag.IsRetired()) return;
#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)
#undef ABSL_FLAGS_INTERNAL_IGNORE_TYPE
flags_internal::PrivateHandleAccessor::CheckDefaultValueParsingRoundtrip(
*flag);
flag);
});
#endif
}
@ -329,13 +330,13 @@ void CheckDefaultValuesParsingRoundtrip() {
// the first flagfile in the input list are processed before the second flagfile
// etc.
bool ReadFlagfiles(const std::vector<std::string>& flagfiles,
std::vector<ArgsList>* input_args) {
std::vector<ArgsList>& input_args) {
bool success = true;
for (auto it = flagfiles.rbegin(); it != flagfiles.rend(); ++it) {
ArgsList al;
if (al.ReadFromFlagfile(*it)) {
input_args->push_back(al);
input_args.push_back(al);
} else {
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
// append a single ArgList at the end of the input_args.
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 success = true;
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);
std::string envval;
if (!GetEnvVar(envname.c_str(), &envval)) {
if (!GetEnvVar(envname.c_str(), envval)) {
if (fail_on_absent_in_env) {
flags_internal::ReportUsageError(
absl::StrCat(envname, " not found in environment"), true);
@ -386,7 +387,7 @@ bool ReadFlagsFromEnv(const std::vector<std::string>& flag_names,
}
if (success) {
input_args->emplace_back(args);
input_args.emplace_back(args);
}
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
// flags (flagfile, fromenv, tryfromemv) successfully.
bool HandleGeneratorFlags(std::vector<ArgsList>* input_args,
std::vector<std::string>* flagfile_value) {
bool HandleGeneratorFlags(std::vector<ArgsList>& input_args,
std::vector<std::string>& flagfile_value) {
bool success = true;
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) {
auto flagfiles = absl::GetFlag(FLAGS_flagfile);
if (input_args->size() == 1) {
flagfile_value->insert(flagfile_value->end(), flagfiles.begin(),
flagfiles.end());
if (input_args.size() == 1) {
flagfile_value.insert(flagfile_value.end(), flagfiles.begin(),
flagfiles.end());
}
success &= ReadFlagfiles(flagfiles, input_args);
@ -647,7 +648,7 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
bool success = true;
while (!input_args.empty()) {
// 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
// and re-try.
@ -733,7 +734,7 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
std::string error;
if (!flags_internal::PrivateHandleAccessor::ParseFrom(
flag, value, SET_FLAGS_VALUE, kCommandLine, &error)) {
*flag, value, SET_FLAGS_VALUE, kCommandLine, error)) {
flags_internal::ReportUsageError(error, true);
success = false;
} else {

View file

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

View file

@ -28,7 +28,7 @@
#include "absl/flags/declare.h"
#include "absl/flags/flag.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/string_view.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
// from another flagfile.
const char* GetFlagfileFlag(const std::vector<FlagfileData>& ffd,
std::string* flagfile_flag) {
*flagfile_flag = "--flagfile=";
std::string& flagfile_flag) {
flagfile_flag = "--flagfile=";
absl::string_view separator;
for (const auto& flagfile_data : ffd) {
std::string flagfile_name =
@ -183,11 +183,11 @@ const char* GetFlagfileFlag(const std::vector<FlagfileData>& ffd,
flagfile_out << absl::Substitute(line, GetTestTempDir()) << "\n";
}
absl::StrAppend(flagfile_flag, separator, flagfile_name);
absl::StrAppend(&flagfile_flag, separator, flagfile_name);
separator = ",";
}
return flagfile_flag->c_str();
return flagfile_flag.c_str();
}
} // namespace
@ -208,7 +208,7 @@ using testing::ElementsAreArray;
class ParseTest : public testing::Test {
private:
flags::FlagSaver flag_saver_;
absl::FlagSaver flag_saver_;
};
// --------------------------------------------------------------------
@ -588,14 +588,14 @@ TEST_F(ParseTest, TestSimpleValidFlagfile) {
const char* in_args1[] = {
"testbin",
GetFlagfileFlag({{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}},
&flagfile_flag),
flagfile_flag),
};
TestParse(in_args1, -1, 0.1, "q2w2 ", true);
const char* in_args2[] = {
"testbin",
GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)}},
&flagfile_flag),
flagfile_flag),
};
TestParse(in_args2, 100, 0.1, "q2w2 ", false);
}
@ -609,7 +609,7 @@ TEST_F(ParseTest, TestValidMultiFlagfile) {
"testbin",
GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)},
{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}},
&flagfile_flag),
flagfile_flag),
};
TestParse(in_args1, -1, 0.1, "q2w2 ", true);
}
@ -622,7 +622,7 @@ TEST_F(ParseTest, TestFlagfileMixedWithRegularFlags) {
const char* in_args1[] = {
"testbin", "--int_flag=3",
GetFlagfileFlag({{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}},
&flagfile_flag),
flagfile_flag),
"-double_flag=0.2"};
TestParse(in_args1, -1, 0.2, "q2w2 ", true);
}
@ -637,10 +637,14 @@ TEST_F(ParseTest, TestFlagfileInFlagfile) {
"--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[] = {
"testbin",
GetFlagfileFlag({{"parse_test.ff3", absl::MakeConstSpan(ff3_data)}},
&flagfile_flag),
flagfile_flag),
};
TestParse(in_args1, 100, 0.1, "q2w2 ", false);
}
@ -657,7 +661,7 @@ TEST_F(ParseDeathTest, TestInvalidFlagfiles) {
const char* in_args1[] = {
"testbin",
GetFlagfileFlag({{"parse_test.ff4",
absl::MakeConstSpan(ff4_data)}}, &flagfile_flag),
absl::MakeConstSpan(ff4_data)}}, flagfile_flag),
};
EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1),
"Unknown command line flag 'unknown_flag'");
@ -669,7 +673,7 @@ TEST_F(ParseDeathTest, TestInvalidFlagfiles) {
const char* in_args2[] = {
"testbin",
GetFlagfileFlag({{"parse_test.ff5",
absl::MakeConstSpan(ff5_data)}}, &flagfile_flag),
absl::MakeConstSpan(ff5_data)}}, flagfile_flag),
};
EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2),
"Unknown command line flag 'int_flag 10'");
@ -681,7 +685,7 @@ TEST_F(ParseDeathTest, TestInvalidFlagfiles) {
const char* in_args3[] = {
"testbin",
GetFlagfileFlag({{"parse_test.ff6", absl::MakeConstSpan(ff6_data)}},
&flagfile_flag),
flagfile_flag),
};
EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args3),
"Flagfile can't contain position arguments or --");
@ -702,7 +706,7 @@ TEST_F(ParseDeathTest, TestInvalidFlagfiles) {
const char* in_args5[] = {
"testbin",
GetFlagfileFlag({{"parse_test.ff7", absl::MakeConstSpan(ff7_data)}},
&flagfile_flag),
flagfile_flag),
};
EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args5),
"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");
// 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
// limitations under the License.
#include "absl/flags/internal/registry.h"
#include "absl/flags/reflection.h"
#include <assert.h>
#include <stdlib.h>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/base/config.h"
#include "absl/base/internal/raw_logging.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/registry.h"
#include "absl/flags/usage_config.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.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 {
ABSL_NAMESPACE_BEGIN
namespace flags_internal {
// --------------------------------------------------------------------
// FlagRegistry
// A FlagRegistry singleton object holds all flag objects indexed
// by their names so that if you know a flag's name (as a C
// string), you can access or set it. If the function is named
// FooLocked(), you must own the registry lock before calling
// the function; otherwise, you should *not* hold the lock, and
// the function will acquire it itself if needed.
// A FlagRegistry singleton object holds all flag objects indexed by their
// names so that if you know a flag's name, you can access or set it. If the
// function is named FooLocked(), you must own the registry lock before
// calling the function; otherwise, you should *not* hold the lock, and the
// function will acquire it itself if needed.
// --------------------------------------------------------------------
class FlagRegistry {
@ -60,8 +48,8 @@ class FlagRegistry {
FlagRegistry() = default;
~FlagRegistry() = default;
// Store a flag in this registry. Takes ownership of *flag.
void RegisterFlag(CommandLineFlag* flag);
// Store a flag in this registry. Takes ownership of *flag.
void RegisterFlag(CommandLineFlag& flag);
void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION(lock_) { lock_.Lock(); }
void Unlock() ABSL_UNLOCK_FUNCTION(lock_) { lock_.Unlock(); }
@ -74,12 +62,13 @@ class FlagRegistry {
// found or not retired. Does not emit a warning.
CommandLineFlag* FindRetiredFlagLocked(absl::string_view name);
static FlagRegistry* GlobalRegistry(); // returns a singleton registry
static FlagRegistry& GlobalRegistry(); // returns a singleton registry
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(
std::function<void(CommandLineFlag*)> visitor);
std::function<void(CommandLineFlag&)> visitor);
// The map from name to flag, for FindFlagLocked().
using FlagMap = std::map<absl::string_view, CommandLineFlag*>;
@ -94,74 +83,6 @@ class 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) {
FlagConstIterator i = flags_.find(name);
if (i == flags_.end()) {
@ -185,97 +106,92 @@ CommandLineFlag* FlagRegistry::FindRetiredFlagLocked(absl::string_view name) {
return i->second;
}
// --------------------------------------------------------------------
// 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.
// --------------------------------------------------------------------
namespace {
class FlagSaverImpl {
class FlagRegistryLock {
public:
FlagSaverImpl() = default;
FlagSaverImpl(const FlagSaverImpl&) = delete;
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();
}
}
explicit FlagRegistryLock(FlagRegistry& fr) : fr_(fr) { fr_.Lock(); }
~FlagRegistryLock() { fr_.Unlock(); }
private:
std::vector<std::unique_ptr<flags_internal::FlagStateInterface>>
backup_registry_;
FlagRegistry& fr_;
};
FlagSaver::FlagSaver() : impl_(new FlagSaverImpl) { impl_->SaveFromRegistry(); }
void DestroyRetiredFlag(CommandLineFlag& flag);
void FlagSaver::Ignore() {
delete impl_;
impl_ = nullptr;
}
} // namespace
FlagSaver::~FlagSaver() {
if (!impl_) return;
impl_->RestoreToRegistry();
delete impl_;
}
// --------------------------------------------------------------------
CommandLineFlag* FindCommandLineFlag(absl::string_view name) {
if (name.empty()) return nullptr;
FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
FlagRegistryLock frl(registry);
return registry->FindFlagLocked(name);
}
CommandLineFlag* FindRetiredFlag(absl::string_view name) {
FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
FlagRegistryLock frl(registry);
return registry->FindRetiredFlagLocked(name);
}
// --------------------------------------------------------------------
void ForEachFlagUnlocked(std::function<void(CommandLineFlag*)> visitor) {
FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
for (FlagRegistry::FlagConstIterator i = registry->flags_.begin();
i != registry->flags_.end(); ++i) {
visitor(i->second);
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);
}
}
void ForEachFlag(std::function<void(CommandLineFlag*)> visitor) {
FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
FlagRegistry& 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);
ForEachFlagUnlocked(visitor);
}
// --------------------------------------------------------------------
bool RegisterCommandLineFlag(CommandLineFlag* flag) {
FlagRegistry::GlobalRegistry()->RegisterFlag(flag);
bool RegisterCommandLineFlag(CommandLineFlag& flag) {
FlagRegistry::GlobalRegistry().RegisterFlag(flag);
return true;
}
@ -283,7 +199,7 @@ bool RegisterCommandLineFlag(CommandLineFlag* flag) {
namespace {
class RetiredFlagObj final : public flags_internal::CommandLineFlag {
class RetiredFlagObj final : public CommandLineFlag {
public:
constexpr RetiredFlagObj(const char* name, FlagFastTypeId 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,
flags_internal::ValueSource, std::string*) override {
flags_internal::ValueSource, std::string&) override {
return false;
}
@ -319,32 +235,74 @@ class RetiredFlagObj final : public flags_internal::CommandLineFlag {
const FlagFastTypeId type_id_;
};
void DestroyRetiredFlag(flags_internal::CommandLineFlag* flag) {
assert(flag->IsRetired());
delete static_cast<RetiredFlagObj*>(flag);
void DestroyRetiredFlag(CommandLineFlag& flag) {
assert(flag.IsRetired());
delete static_cast<RetiredFlagObj*>(&flag);
}
} // namespace
bool Retire(const char* name, FlagFastTypeId type_id) {
auto* flag = new flags_internal::RetiredFlagObj(name, type_id);
FlagRegistry::GlobalRegistry()->RegisterFlag(flag);
FlagRegistry::GlobalRegistry().RegisterFlag(*flag);
return true;
}
// --------------------------------------------------------------------
bool IsRetiredFlag(absl::string_view name, bool* type_is_bool) {
assert(!name.empty());
CommandLineFlag* flag = flags_internal::FindRetiredFlag(name);
if (flag == nullptr) {
return false;
class FlagSaverImpl {
public:
FlagSaverImpl() = default;
FlagSaverImpl(const FlagSaverImpl&) = delete;
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>();
return true;
}
// Restores the saved flag states into the flag registry.
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
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
} // namespace absl

85
absl/flags/reflection.h Normal file
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 <functional>
#include <iostream>
#include <string>

View file

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

View file

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

View file

@ -15,6 +15,7 @@
#include "absl/numeric/int128.h"
#include <stddef.h>
#include <cassert>
#include <iomanip>
#include <ostream> // NOLINT(readability/streams)
@ -22,6 +23,9 @@
#include <string>
#include <type_traits>
#include "absl/base/internal/bits.h"
#include "absl/base/optimization.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@ -31,44 +35,26 @@ ABSL_DLL const uint128 kuint128max = MakeUint128(
namespace {
// 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:
// Given: 5 (decimal) == 101 (binary)
// Returns: 2
#define STEP(T, n, pos, sh) \
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) {
inline ABSL_ATTRIBUTE_ALWAYS_INLINE int Fls128(uint128 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
// division algorithm adapted from:
// https://stackoverflow.com/questions/5386377/division-without-using
void DivModImpl(uint128 dividend, uint128 divisor, uint128* quotient_ret,
uint128* remainder_ret) {
inline void DivModImpl(uint128 dividend, uint128 divisor, uint128* quotient_ret,
uint128* remainder_ret) {
assert(divisor != 0);
if (divisor > dividend) {

View file

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

View file

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

View file

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

View file

@ -24,11 +24,11 @@
#ifndef 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/meta/type_traits.h"
#include "absl/random/internal/distribution_caller.h"
#include "absl/random/internal/fast_uniform_bits.h"
#include "absl/random/internal/mocking_bit_gen_base.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@ -51,6 +51,10 @@ struct is_urbg<
typename std::decay<decltype(std::declval<URBG>()())>::type>::value>>
: std::true_type {};
template <typename>
struct DistributionCaller;
class MockHelpers;
} // namespace random_internal
// -----------------------------------------------------------------------------
@ -77,23 +81,50 @@ struct is_urbg<
// }
//
class BitGenRef {
public:
using result_type = uint64_t;
// SFINAE to detect whether the URBG type includes a member matching
// 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;
BitGenRef(absl::BitGenRef&&) = default;
BitGenRef& operator=(const absl::BitGenRef&) = default;
BitGenRef& operator=(absl::BitGenRef&&) = default;
template <class T>
using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock(
std::declval<base_internal::FastTypeIdType>(), std::declval<void*>(),
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,
typename absl::enable_if_t<
(!std::is_same<URBG, BitGenRef>::value &&
random_internal::is_urbg<URBG>::value)>* = nullptr>
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
: mocked_gen_ptr_(MakeMockPointer(&gen)),
t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)),
generate_impl_fn_(ImplFn<URBG>) {
}
: t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)),
mock_call_(&MockCall<URBG>),
generate_impl_fn_(ImplFn<URBG>) {}
using result_type = uint64_t;
static constexpr 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_); }
private:
friend struct absl::random_internal::DistributionCaller<absl::BitGenRef>;
using impl_fn = result_type (*)(uintptr_t);
using mocker_base_t = absl::random_internal::MockingBitGenBase;
// 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; }
using mock_call_fn = bool (*)(uintptr_t, base_internal::FastTypeIdType, void*,
void*);
template <typename URBG>
static result_type ImplFn(uintptr_t ptr) {
@ -123,29 +149,32 @@ class BitGenRef {
return fast_uniform_bits(*reinterpret_cast<URBG*>(ptr));
}
mocker_base_t* mocked_gen_ptr_;
uintptr_t t_erased_gen_ptr_;
impl_fn generate_impl_fn_;
};
namespace random_internal {
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)...);
}
// Get a type-erased InvokeMock pointer.
template <typename URBG>
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,
arg_tuple);
}
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
} // namespace absl

View file

@ -17,30 +17,31 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/internal/fast_type_id.h"
#include "absl/random/internal/sequence_urbg.h"
#include "absl/random/random.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
class ConstBitGen : public absl::random_internal::MockingBitGenBase {
bool CallImpl(const std::type_info&, void*, void* result) override {
class ConstBitGen {
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;
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 {
int FnTest(absl::BitGenRef gen_ref) { return absl::Uniform(gen_ref, 1, 7); }
template <typename T>

View file

@ -57,7 +57,7 @@
#include "absl/random/beta_distribution.h"
#include "absl/random/exponential_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/log_uniform_int_distribution.h"
#include "absl/random/poisson_distribution.h"

View file

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

View file

@ -20,6 +20,8 @@
#include <utility>
#include "absl/base/config.h"
#include "absl/base/internal/fast_type_id.h"
#include "absl/utility/utility.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@ -30,14 +32,57 @@ namespace random_internal {
// to intercept such calls.
template <typename URBG>
struct DistributionCaller {
// Call the provided distribution type. The parameters are expected
// to be explicitly specified.
// DistrT is the distribution type.
// SFINAE to detect whether the URBG type includes a member matching
// 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 {};
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>
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)...);
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

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 "gtest/gtest.h"
#include "absl/random/internal/mock_helpers.h"
#include "absl/random/mocking_bit_gen.h"
namespace absl {
@ -35,17 +36,20 @@ struct MockSingleOverload;
// EXPECT_CALL(mock_single_overload, Call(...))` will expand to a call to
// `mock_single_overload.gmock_Call(...)`. Because expectations are stored on
// 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>
struct MockSingleOverload<DistrT, Ret(MockingBitGen&, Args...)> {
static_assert(std::is_same<typename DistrT::result_type, Ret>::value,
"Overload signature must have return type matching the "
"distributions result type.");
"distribution result_type.");
using KeyT = Ret(DistrT, std::tuple<Args...>);
auto gmock_Call(
absl::MockingBitGen& gen, // NOLINT(google-runtime-references)
const ::testing::Matcher<Args>&... args)
-> decltype(gen.Register<DistrT, Args...>(args...)) {
return gen.Register<DistrT, Args...>(args...);
const ::testing::Matcher<Args>&... matchers)
-> decltype(MockHelpers::MockFor<KeyT>(gen).gmock_Call(matchers...)) {
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...)> {
static_assert(std::is_same<typename DistrT::result_type, Ret>::value,
"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(
const ::testing::Matcher<Arg>& arg,
const ::testing::Matcher<Arg>& matcher,
absl::MockingBitGen& gen, // NOLINT(google-runtime-references)
const ::testing::Matcher<Args>&... args)
-> decltype(gen.Register<DistrT, Arg, Args...>(arg, args...)) {
return gen.Register<DistrT, Arg, Args...>(arg, args...);
const ::testing::Matcher<Args>&... matchers)
-> decltype(MockHelpers::MockFor<KeyT>(gen).gmock_Call(matcher,
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 <type_traits>
#include "absl/base/config.h"
#include "absl/meta/type_traits.h"
#include "absl/random/internal/traits.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
template <typename IntType>
class uniform_int_distribution;
@ -58,6 +61,26 @@ struct IntervalOpenOpenTag
: public random_internal::TagTypeCompare<IntervalOpenOpenTag> {};
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
// uniform_lower_bound(tag, a, b)
// and
@ -149,12 +172,19 @@ uniform_upper_bound(Tag, FloatType, FloatType b) {
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>
using UniformDistribution =
typename std::conditional<std::is_integral<NumType>::value,
absl::uniform_int_distribution<NumType>,
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>
struct UniformDistributionWrapper : public UniformDistribution<NumType> {
template <typename TagType>

View file

@ -27,6 +27,11 @@
// More information about the Googletest testing framework is available at
// 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:
//
// absl::MockingBitGen mock;

View file

@ -33,17 +33,16 @@
#include <memory>
#include <tuple>
#include <type_traits>
#include <typeindex>
#include <typeinfo>
#include <utility>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/internal/fast_type_id.h"
#include "absl/container/flat_hash_map.h"
#include "absl/meta/type_traits.h"
#include "absl/random/distributions.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_join.h"
#include "absl/types/span.h"
@ -54,11 +53,12 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
namespace random_internal {
template <typename, typename>
struct MockSingleOverload;
template <typename>
struct DistributionCaller;
class MockHelpers;
} // namespace random_internal
class BitGenRef;
// MockingBitGen
//
@ -96,102 +96,132 @@ struct MockSingleOverload;
// At this time, only mock distributions supplied within the Abseil random
// 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:
MockingBitGen() {}
MockingBitGen() = default;
~MockingBitGen() override {
~MockingBitGen() {
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:
template <typename DistrT, typename... Args>
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,
using match_impl_fn = void (*)(void* mock_fn, void* t_erased_arg_tuple,
void* t_erased_result);
struct MockData {
void* mock_fn = 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>
static void MatchImpl(void* mock_fn, void* dist_args, void* result) {
using result_type = typename DistrT::result_type;
*static_cast<result_type*>(result) = absl::apply(
[mock_fn](Args... args) -> result_type {
return (*static_cast<MockFnType<DistrT, Args...>*>(mock_fn))
.Call(std::move(args)...);
},
*static_cast<std::tuple<Args...>*>(dist_args));
// MockFnCaller is a helper method for use with absl::apply to
// apply an ArgTupleT to a compatible MockFunction.
// NOTE: MockFnCaller is essentially equivalent to the lambda:
// [fn](auto... args) { return fn->Call(std::move(args)...)}
// however that fails to build on some supported platforms.
template <typename ResultT, typename MockFnType, typename Tuple>
struct MockFnCaller;
// specialization for std::tuple.
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.
// Otherwise, returns a random value generated by the underlying URBG.
bool CallImpl(const std::type_info& key_type, void* dist_args,
void* result) override {
// MockingBitGen::MatchImpl<> is a dispatch function which converts the
// generic type-erased parameters into a specific mock invocation call.
// 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.
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`.
auto it = mocks_.find(std::type_index(key_type));
auto it = mocks_.find(type);
if (it == mocks_.end()) return false;
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;
}
template <typename, typename>
friend struct ::absl::random_internal::MockSingleOverload;
friend struct ::absl::random_internal::DistributionCaller<
absl::MockingBitGen>;
absl::flat_hash_map<base_internal::FastTypeIdType, MockData> mocks_;
std::vector<std::function<void()>> deleters_;
absl::BitGen gen_;
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
} // namespace absl

View file

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

View file

@ -27,8 +27,6 @@
namespace absl {
ABSL_NAMESPACE_BEGIN
// The implementation was intentionally kept same as util::error::Code_Name()
// to ease the migration.
std::string StatusCodeToString(StatusCode code) {
switch (code) {
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
// that it does not need to call the destructor on a moved-from Cord.
void Cord::DestroyCordSlow() {
@ -742,6 +773,18 @@ Cord& Cord::operator=(absl::string_view src) {
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,
// we keep it here to make diffs easier.
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)); }
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) {
CordRep* src_tree = src.contents_.tree();
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) {
if (n >= node->length) return nullptr;
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
// templated to avoid ambiguities for types that are convertible to both
// `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>
explicit Cord(T&& src) : Cord(absl::string_view(src)) {}
explicit Cord(T&& src);
template <typename T, EnableIfString<T> = 0>
Cord& operator=(T&& src);
@ -1048,11 +1045,8 @@ inline Cord& Cord::operator=(Cord&& x) noexcept {
return *this;
}
template <typename T, Cord::EnableIfString<T>>
inline Cord& Cord::operator=(T&& src) {
*this = absl::string_view(src);
return *this;
}
extern template Cord::Cord(std::string&& src);
extern template Cord& Cord::operator=(std::string&& src);
inline size_t Cord::size() const {
// 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());
}
template <typename T, Cord::EnableIfString<T>>
inline void Cord::Append(T&& 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));
}
extern template void Cord::Append(std::string&& src);
extern template void Cord::Prepend(std::string&& src);
inline int Cord::Compare(const Cord& rhs) const {
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 (char f : {'f', 'F', //
'g', 'G', //

View file

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

View file

@ -19,7 +19,7 @@
//
// The `str_format` library is a typesafe replacement for the family of
// `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
// below for format string documentation.
//

View file

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

View file

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

View file

@ -98,26 +98,26 @@ bool ParseLenient(string_view s, CivilT* c) {
} // namespace
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) {
return FormatYearAnd("-%m-%dT%H:%M", c);
return FormatYearAnd("-%m-%d%ET%H:%M", 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(CivilMonth c) { return FormatYearAnd("-%m", c); }
std::string FormatCivilTime(CivilYear c) { return FormatYearAnd("", 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) {
return ParseYearAnd("-%m-%dT%H:%M", s, c);
return ParseYearAnd("-%m-%d%ET%H:%M", s, 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) {
return ParseYearAnd("-%m-%d", s, c);

View file

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

View file

@ -26,7 +26,7 @@ const char* const kFormats[] = {
absl::RFC1123_no_wday, // 1
absl::RFC3339_full, // 2
absl::RFC3339_sec, // 3
"%Y-%m-%dT%H:%M:%S", // 4
"%Y-%m-%d%ET%H:%M:%S", // 4
"%Y-%m-%d", // 5
};
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,
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;
if (cd < 0) {
y -= 400;
ey -= 400;
cd += 146097;
}
y += (d / 146097) * 400;
ey += (d / 146097) * 400;
d = d % 146097 + cd;
if (d > 0) {
if (d > 146097) {
y += 400;
ey += 400;
d -= 146097;
}
} else {
if (d > -365) {
// 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.
y -= 1;
d += days_per_year(y, m);
ey -= 1;
d += days_per_year(ey, m);
} else {
y -= 400;
ey -= 400;
d += 146097;
}
}
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;
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;
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;
++y;
++ey;
}
}
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;
if (++m > 12) {
++y;
++ey;
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,
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 full precision (a literal '*')
// - %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
// 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
// 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
// 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
// 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);
const char RFC3339_full[] = "%Y-%m-%dT%H:%M:%E*S%Ez";
const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez";
const char RFC3339_full[] = "%Y-%m-%d%ET%H:%M:%E*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_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);
const char* const kFormats[] = {
RFC1123_full, // 0
RFC1123_no_wday, // 1
RFC3339_full, // 2
RFC3339_sec, // 3
"%Y-%m-%dT%H:%M:%S", // 4
"%Y-%m-%d", // 5
RFC1123_full, // 0
RFC1123_no_wday, // 1
RFC3339_full, // 2
RFC3339_sec, // 3
"%Y-%m-%d%ET%H:%M:%S", // 4
"%Y-%m-%d", // 5
};
const int kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]);

View file

@ -234,6 +234,16 @@ TEST(CivilTime, 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.
TEST(CivilTime, DifferenceWithHugeYear) {
{

View file

@ -67,6 +67,48 @@ char* strptime(const char* s, const char* fmt, std::tm* tm) {
}
#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 tm{};
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);
}
switch (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_wday = ToTmWday(get_weekday(al.cs));
tm.tm_yday = get_yearday(al.cs) - 1;
tm.tm_isdst = al.is_dst ? 1 : 0;
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";
// 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 full fractional precision (a literal '*')
// - %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
// 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;
// Simple specifiers that we handle ourselves.
if (strchr("YmdeHMSzZs%", *cur)) {
if (strchr("YmdeUuWwHMSzZs%", *cur)) {
if (cur - 1 != pending) {
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
result.append(bp, static_cast<std::size_t>(ep - bp));
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':
bp = Format02d(ep, al.cs.hour());
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;
// 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.
if (cur - 2 != pending) {
FormatTM(&result, std::string(pending, cur - 2), tm);
@ -551,7 +602,7 @@ const char* ParseOffset(const char* dp, const char* mode, int* offset) {
} else {
dp = nullptr;
}
} else if (first == 'Z') { // Zulu
} else if (first == 'Z' || first == 'z') { // Zulu
*offset = 0;
} else {
dp = nullptr;
@ -602,12 +653,23 @@ const char* ParseTM(const char* dp, const char* fmt, std::tm* tm) {
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
// Uses strptime(3) to parse the given input. Supports the same extended
// 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
// 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
// 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
bool twelve_hour = false;
bool afternoon = false;
int week_num = -1;
weekday week_start = weekday::sunday;
bool saw_percent_s = false;
std::int_fast64_t percent_s = 0;
@ -689,10 +753,27 @@ bool parse(const std::string& format, const std::string& input,
case 'm':
data = ParseInt(data, 2, 1, 12, &tm.tm_mon);
if (data != nullptr) tm.tm_mon -= 1;
week_num = -1;
continue;
case 'd':
case 'e':
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;
case 'H':
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);
continue;
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')) {
data = ParseOffset(data, ":", &offset);
if (data != nullptr) saw_offset = true;
@ -874,6 +964,9 @@ bool parse(const std::string& format, const std::string& input,
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;
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); \
} while (0)
const char RFC3339_full[] = "%Y-%m-%dT%H:%M:%E*S%Ez";
const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez";
const char RFC3339_full[] = "%Y-%m-%d%ET%H:%M:%E*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_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));
}
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()
//
@ -1379,10 +1407,80 @@ TEST(Parse, RFC3339Format) {
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");
// Check that %Ez also accepts "Z" as a synonym for "+00:00".
// Check that %ET also accepts "t".
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);
// 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) {

View file

@ -15,6 +15,7 @@
#include "time_zone_impl.h"
#include <deque>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
@ -48,17 +49,16 @@ std::mutex& TimeZoneMutex() {
time_zone time_zone::Impl::UTC() { return time_zone(UTCImpl()); }
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();
if (FixedOffsetFromName(name, &offset) && offset == seconds::zero()) {
*tz = time_zone(utc_impl);
return true;
}
// Then check, under a shared lock, whether the time zone has already
// been loaded. This is the common path. TODO: Move to shared_mutex.
// Check whether the time zone has already been loaded.
{
std::lock_guard<std::mutex> lock(TimeZoneMutex());
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());
if (time_zone_map == nullptr) time_zone_map = new TimeZoneImplByName;
const Impl*& impl = (*time_zone_map)[name];
if (impl == nullptr) {
// The first thread in loads the new time zone.
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
}
if (impl == nullptr) { // this thread won any load race
impl = new_impl->zone_ ? new_impl.release() : utc_impl;
}
*tz = time_zone(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() {
static Impl* utc_impl = [] {
Impl* impl = new Impl("UTC");
impl->zone_ = TimeZoneIf::Load(impl->name_); // never fails
return impl;
}();
static const Impl* utc_impl = new Impl("UTC"); // never fails
return utc_impl;
}

View file

@ -933,7 +933,7 @@ TEST(MakeTime, Normalization) {
// NOTE: Run this with -ftrapv to detect overflow problems.
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 east = 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 \
"@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
"@@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( \
linker, \
"/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
// years have exactly four digits, but we allow them to take their natural
// width.
ABSL_DLL extern const char
RFC3339_full[]; // %Y-%m-%dT%H:%M:%E*S%Ez
ABSL_DLL extern const char RFC3339_sec[]; // %Y-%m-%dT%H:%M:%S%Ez
ABSL_DLL extern const char RFC3339_full[]; // %Y-%m-%d%ET%H:%M:%E*S%Ez
ABSL_DLL extern const char RFC3339_sec[]; // %Y-%m-%d%ET%H:%M:%S%Ez
// RFC1123_full
// RFC1123_no_wday
//
// FormatTime()/ParseTime() format specifiers for RFC1123 date/time strings.
ABSL_DLL extern const char
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_full[]; // %a, %d %b %E4Y %H:%M:%S %z
ABSL_DLL extern const char RFC1123_no_wday[]; // %d %b %E4Y %H:%M:%S %z
// FormatTime()
//
@ -1229,6 +1226,7 @@ ABSL_DLL extern const char
// - %E#f - Fractional seconds with # digits of precision
// - %E*f - Fractional seconds with full precision (a literal '*')
// - %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
// 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
// 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
// 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
// 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
// with implicit conversion.
//
// The C++ standards committee currently has a proposal for a `std::span` type,
// (http://wg21.link/p0122), which is not yet part of the standard (though may
// become part of C++20). As of August 2017, the differences between
// `absl::Span` and this proposal are:
// * `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`
// The C++20 draft standard includes a `std::span` type. As of June 2020, the
// differences between `absl::Span` and `std::span` are:
// * `absl::Span` has `operator==` (which is likely a design bug,
// per https://abseil.io/blog/20180531-regular-types)
// * `absl::Span` has the factory functions `MakeSpan()` and
// `MakeConstSpan()`
// * `absl::Span` has `front()` and `back()` methods
// * bounds-checked access to `absl::Span` is accomplished with `at()`
// * `absl::Span` has compiler-provided move and copy constructors and
// assignment. This is due to them being specified as `constexpr`, but that
// 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
// initializer list.
// * `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 ... \
--compilation_mode="${compilation_mode}" \
--copt="${exceptions_mode}" \
--copt="-DDYNAMIC_ANNOTATIONS_ENABLED=1" \
--copt="-DADDRESS_SANITIZER" \
--copt="-DUNDEFINED_BEHAVIOR_SANITIZER" \
--copt="-fsanitize=address" \