Commit 1d31b5c3 by Abseil Team Committed by Gennadiy Rozental

Export of internal Abseil changes

--
9e8b4a286d70df9487bff080816bd07ae38af5f8 by Evan Brown <ezb@google.com>:

Add btree_node::transfer_n/transfer_n_backward and replace usage of uninitialized_move_n and value_destroy_n.

PiperOrigin-RevId: 314600027

--
6c452aa1ee7e46ab941ba7d1fa636da8ea3d7370 by Laramie Leavitt <lar@google.com>:

Remove the MockingBitGenBase base class in favor of type-erasure in BitGenRef.

In Abseil random, mocking was split across two different classes,
MockingBitGenBase and MockingBitGen. This split existed because Google Mock is a
test-only library that we don't link into production, so MockingBitGenBase
provided a low-overhead scaffold used to lookup mocks when in test code, but
which is unused in production code.

That has been replaced by type-erasure which looks for a method named
CallImpl with the correct signature.

Weaken the coupling between MockingBitGen, DistributionCaller, and MockOverloadSet.

Rename CallImpl to InvokeMock()

Previously, the implementation of DistributionCaller was also split across different files using explicit instantiation of the DistributionCaller struct and some details in the Mocking classes. Now Distribution caller uses the presence of the InvokeMock() method to choose whether to use the mockable call path or the default call path.

PiperOrigin-RevId: 314584095

--
07853c47dc98698d67d65a3b9b662a65ab9def0a by Abseil Team <absl-team@google.com>:

Add PC / backtrace / symbolization support for Apple platforms.

Full backtrace support requires iOS 9+

PiperOrigin-RevId: 314415072

--
43889f17a132b31f6558c6482721cbbc776128fd by Gennadiy Rozental <rogeeff@google.com>:

Consolidate all reflection interface in the new module 'reflection' and expose interface to locate reflection handle by name.

PiperOrigin-RevId: 314390358
GitOrigin-RevId: 9e8b4a286d70df9487bff080816bd07ae38af5f8
Change-Id: I8e0910437740cf9ea9da5000adddfcef127e1158
parent da3a8769
...@@ -147,7 +147,6 @@ set(ABSL_INTERNAL_DLL_FILES ...@@ -147,7 +147,6 @@ set(ABSL_INTERNAL_DLL_FILES
"random/internal/platform.h" "random/internal/platform.h"
"random/internal/pool_urbg.cc" "random/internal/pool_urbg.cc"
"random/internal/pool_urbg.h" "random/internal/pool_urbg.h"
"random/internal/randen_round_keys.cc"
"random/internal/randen.cc" "random/internal/randen.cc"
"random/internal/randen.h" "random/internal/randen.h"
"random/internal/randen_detect.cc" "random/internal/randen_detect.cc"
...@@ -155,6 +154,7 @@ set(ABSL_INTERNAL_DLL_FILES ...@@ -155,6 +154,7 @@ set(ABSL_INTERNAL_DLL_FILES
"random/internal/randen_engine.h" "random/internal/randen_engine.h"
"random/internal/randen_hwaes.cc" "random/internal/randen_hwaes.cc"
"random/internal/randen_hwaes.h" "random/internal/randen_hwaes.h"
"random/internal/randen_round_keys.cc"
"random/internal/randen_slow.cc" "random/internal/randen_slow.cc"
"random/internal/randen_slow.h" "random/internal/randen_slow.h"
"random/internal/randen_traits.h" "random/internal/randen_traits.h"
......
...@@ -817,33 +817,52 @@ class btree_node { ...@@ -817,33 +817,52 @@ class btree_node {
absl::container_internal::SanitizerPoisonObject(slot(i)); absl::container_internal::SanitizerPoisonObject(slot(i));
} }
// Transfers value from slot `src_i` in `src` to slot `dest_i` in `this`. // Transfers value from slot `src_i` in `src_node` to slot `dest_i` in `this`.
void transfer(const size_type dest_i, const size_type src_i, btree_node *src, void transfer(const size_type dest_i, const size_type src_i,
allocator_type *alloc) { btree_node *src_node, allocator_type *alloc) {
absl::container_internal::SanitizerUnpoisonObject(slot(dest_i)); absl::container_internal::SanitizerUnpoisonObject(slot(dest_i));
params_type::transfer(alloc, slot(dest_i), src->slot(src_i)); params_type::transfer(alloc, slot(dest_i), src_node->slot(src_i));
absl::container_internal::SanitizerPoisonObject(src->slot(src_i)); absl::container_internal::SanitizerPoisonObject(src_node->slot(src_i));
} }
// Move n values starting at value i in this node into the values starting at // Transfers `n` values starting at value `src_i` in `src_node` into the
// value j in dest_node. // values starting at value `dest_i` in `this`.
void uninitialized_move_n(const size_type n, const size_type i, void transfer_n(const size_type n, const size_type dest_i,
const size_type j, btree_node *dest_node, const size_type src_i, btree_node *src_node,
allocator_type *alloc) { allocator_type *alloc) {
absl::container_internal::SanitizerUnpoisonMemoryRegion( absl::container_internal::SanitizerUnpoisonMemoryRegion(
dest_node->slot(j), n * sizeof(slot_type)); slot(dest_i), n * sizeof(slot_type));
for (slot_type *src = slot(i), *end = src + n, *dest = dest_node->slot(j); for (slot_type *src = src_node->slot(src_i), *end = src + n,
*dest = slot(dest_i);
src != end; ++src, ++dest) { src != end; ++src, ++dest) {
params_type::construct(alloc, dest, src); params_type::transfer(alloc, dest, src);
} }
// We take care to avoid poisoning transferred-to nodes in case of overlap.
const size_type overlap =
this == src_node ? (std::max)(src_i, dest_i + n) - src_i : 0;
assert(n >= overlap);
absl::container_internal::SanitizerPoisonMemoryRegion(
src_node->slot(src_i + overlap), (n - overlap) * sizeof(slot_type));
} }
// Destroys a range of n values, starting at index i. // Same as above, except that we start at the end and work our way to the
void value_destroy_n(const size_type i, const size_type n, // beginning.
allocator_type *alloc) { void transfer_n_backward(const size_type n, const size_type dest_i,
for (int j = 0; j < n; ++j) { const size_type src_i, btree_node *src_node,
value_destroy(i + j, alloc); allocator_type *alloc) {
absl::container_internal::SanitizerUnpoisonMemoryRegion(
slot(dest_i), n * sizeof(slot_type));
for (slot_type *src = src_node->slot(src_i + n - 1), *end = src - n,
*dest = slot(dest_i + n - 1);
src != end; --src, --dest) {
params_type::transfer(alloc, dest, src);
} }
// We take care to avoid poisoning transferred-to nodes in case of overlap.
assert(this != src_node || dest_i >= src_i);
const size_type num_to_poison =
this == src_node ? (std::min)(n, dest_i - src_i) : n;
absl::container_internal::SanitizerPoisonMemoryRegion(
src_node->slot(src_i), num_to_poison * sizeof(slot_type));
} }
template <typename P> template <typename P>
...@@ -1531,10 +1550,8 @@ inline void btree_node<P>::emplace_value(const size_type i, ...@@ -1531,10 +1550,8 @@ inline void btree_node<P>::emplace_value(const size_type i,
// Shift old values to create space for new value and then construct it in // Shift old values to create space for new value and then construct it in
// place. // place.
if (i < finish()) { if (i < finish()) {
value_init(finish(), alloc, slot(finish() - 1)); transfer_n_backward(finish() - i, /*dest_i=*/i + 1, /*src_i=*/i, this,
for (size_type j = finish() - 1; j > i; --j) alloc);
params_type::move(alloc, slot(j - 1), slot(j));
value_destroy(i, alloc);
} }
value_init(i, alloc, std::forward<Args>(args)...); value_init(i, alloc, std::forward<Args>(args)...);
set_finish(finish() + 1); set_finish(finish() + 1);
...@@ -1564,7 +1581,9 @@ template <typename P> ...@@ -1564,7 +1581,9 @@ template <typename P>
inline void btree_node<P>::remove_values_ignore_children( inline void btree_node<P>::remove_values_ignore_children(
const int i, const int to_erase, allocator_type *alloc) { const int i, const int to_erase, allocator_type *alloc) {
params_type::move(alloc, slot(i + to_erase), finish_slot(), slot(i)); params_type::move(alloc, slot(i + to_erase), finish_slot(), slot(i));
value_destroy_n(finish() - to_erase, to_erase, alloc); for (int j = finish() - to_erase; j < finish(); ++j) {
value_destroy(j, alloc);
}
set_finish(finish() - to_erase); set_finish(finish() - to_erase);
} }
...@@ -1579,22 +1598,17 @@ void btree_node<P>::rebalance_right_to_left(const int to_move, ...@@ -1579,22 +1598,17 @@ void btree_node<P>::rebalance_right_to_left(const int to_move,
assert(to_move <= right->count()); assert(to_move <= right->count());
// 1) Move the delimiting value in the parent to the left node. // 1) Move the delimiting value in the parent to the left node.
value_init(finish(), alloc, parent()->slot(position())); transfer(finish(), position(), parent(), alloc);
// 2) Move the (to_move - 1) values from the right node to the left node. // 2) Move the (to_move - 1) values from the right node to the left node.
right->uninitialized_move_n(to_move - 1, right->start(), finish() + 1, this, transfer_n(to_move - 1, finish() + 1, right->start(), right, alloc);
alloc);
// 3) Move the new delimiting value to the parent from the right node. // 3) Move the new delimiting value to the parent from the right node.
params_type::move(alloc, right->slot(to_move - 1), parent()->transfer(position(), right->start() + to_move - 1, right, alloc);
parent()->slot(position()));
// 4) Shift the values in the right node to their correct position.
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. // 4) Shift the values in the right node to their correct positions.
right->value_destroy_n(right->finish() - to_move, to_move, alloc); right->transfer_n(right->count() - to_move, right->start(),
right->start() + to_move, right, alloc);
if (!leaf()) { if (!leaf()) {
// Move the child pointers from the right to the left node. // Move the child pointers from the right to the left node.
...@@ -1629,54 +1643,19 @@ void btree_node<P>::rebalance_left_to_right(const int to_move, ...@@ -1629,54 +1643,19 @@ void btree_node<P>::rebalance_left_to_right(const int to_move,
// Lastly, a new delimiting value is moved from the left node into the // Lastly, a new delimiting value is moved from the left node into the
// parent, and the remaining empty left node entries are destroyed. // parent, and the remaining empty left node entries are destroyed.
if (right->count() >= to_move) { // 1) Shift existing values in the right node to their correct positions.
// The original location of the right->count() values are sufficient to hold right->transfer_n_backward(right->count(), right->start() + to_move,
// the new to_move entries from the parent and left node. right->start(), right, alloc);
// 1) Shift existing values in the right node to their correct positions.
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.
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. // 2) Move the delimiting value in the parent to the right node.
params_type::move(alloc, slot(finish() - (to_move - 1)), finish_slot(), right->transfer(right->start() + to_move - 1, position(), parent(), alloc);
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. // 3) Move the (to_move - 1) values from the left node to the right node.
right->value_init(to_move - 1, alloc, parent()->slot(position())); right->transfer_n(to_move - 1, right->start(), finish() - (to_move - 1), this,
alloc);
// 3) Move the (to_move - 1) values from the left node to the right node.
const size_type uninitialized_remaining = to_move - right->count() - 1;
uninitialized_move_n(uninitialized_remaining,
finish() - uninitialized_remaining, right->finish(),
right, alloc);
params_type::move(alloc, slot(finish() - (to_move - 1)),
slot(finish() - uninitialized_remaining),
right->start_slot());
}
// 4) Move the new delimiting value to the parent from the left node. // 4) Move the new delimiting value to the parent from the left node.
params_type::move(alloc, slot(finish() - to_move), parent()->transfer(position(), finish() - to_move, this, alloc);
parent()->slot(position()));
// 5) Destroy the now-empty to_move entries in the left node.
value_destroy_n(finish() - to_move, to_move, alloc);
if (!leaf()) { if (!leaf()) {
// Move the child pointers from the left to the right node. // Move the child pointers from the left to the right node.
...@@ -1716,10 +1695,7 @@ void btree_node<P>::split(const int insert_position, btree_node *dest, ...@@ -1716,10 +1695,7 @@ void btree_node<P>::split(const int insert_position, btree_node *dest,
assert(count() >= 1); assert(count() >= 1);
// Move values from the left sibling to the right sibling. // Move values from the left sibling to the right sibling.
uninitialized_move_n(dest->count(), finish(), dest->start(), dest, alloc); dest->transfer_n(dest->count(), dest->start(), finish(), this, alloc);
// Destroy the now-empty entries in the left node.
value_destroy_n(finish(), dest->count(), alloc);
// The split key is the largest value in the left sibling. // The split key is the largest value in the left sibling.
--mutable_finish(); --mutable_finish();
...@@ -1746,11 +1722,7 @@ void btree_node<P>::merge(btree_node *src, allocator_type *alloc) { ...@@ -1746,11 +1722,7 @@ void btree_node<P>::merge(btree_node *src, allocator_type *alloc) {
value_init(finish(), alloc, parent()->slot(position())); value_init(finish(), alloc, parent()->slot(position()));
// Move the values from the right to the left node. // Move the values from the right to the left node.
src->uninitialized_move_n(src->count(), src->start(), finish() + 1, this, transfer_n(src->count(), finish() + 1, src->start(), src, alloc);
alloc);
// Destroy the now-empty entries in the right node.
src->value_destroy_n(src->start(), src->count(), alloc);
if (!leaf()) { if (!leaf()) {
// Move the child pointers from the right to the left node. // Move the child pointers from the right to the left node.
...@@ -2474,9 +2446,8 @@ inline auto btree<P>::internal_emplace(iterator iter, Args &&... args) ...@@ -2474,9 +2446,8 @@ inline auto btree<P>::internal_emplace(iterator iter, Args &&... args)
// Transfer the values from the old root to the new root. // Transfer the values from the old root to the new root.
node_type *old_root = root(); node_type *old_root = root();
node_type *new_root = iter.node; node_type *new_root = iter.node;
for (int i = old_root->start(), f = old_root->finish(); i < f; ++i) { new_root->transfer_n(old_root->count(), new_root->start(),
new_root->transfer(i, i, old_root, alloc); old_root->start(), old_root, alloc);
}
new_root->set_finish(old_root->finish()); new_root->set_finish(old_root->finish());
old_root->set_finish(old_root->start()); old_root->set_finish(old_root->start());
delete_leaf_node(old_root); delete_leaf_node(old_root);
......
...@@ -55,6 +55,7 @@ cc_library( ...@@ -55,6 +55,7 @@ cc_library(
name = "symbolize", name = "symbolize",
srcs = [ srcs = [
"symbolize.cc", "symbolize.cc",
"symbolize_darwin.inc",
"symbolize_elf.inc", "symbolize_elf.inc",
"symbolize_unimplemented.inc", "symbolize_unimplemented.inc",
"symbolize_win32.inc", "symbolize_win32.inc",
...@@ -77,6 +78,7 @@ cc_library( ...@@ -77,6 +78,7 @@ cc_library(
"//absl/base:dynamic_annotations", "//absl/base:dynamic_annotations",
"//absl/base:malloc_internal", "//absl/base:malloc_internal",
"//absl/base:raw_logging_internal", "//absl/base:raw_logging_internal",
"//absl/strings",
], ],
) )
......
...@@ -46,6 +46,7 @@ absl_cc_library( ...@@ -46,6 +46,7 @@ absl_cc_library(
"internal/symbolize.h" "internal/symbolize.h"
SRCS SRCS
"symbolize.cc" "symbolize.cc"
"symbolize_darwin.inc"
"symbolize_elf.inc" "symbolize_elf.inc"
"symbolize_unimplemented.inc" "symbolize_unimplemented.inc"
"symbolize_win32.inc" "symbolize_win32.inc"
...@@ -63,6 +64,7 @@ absl_cc_library( ...@@ -63,6 +64,7 @@ absl_cc_library(
absl::dynamic_annotations absl::dynamic_annotations
absl::malloc_internal absl::malloc_internal
absl::raw_logging_internal absl::raw_logging_internal
absl::strings
PUBLIC PUBLIC
) )
......
...@@ -20,6 +20,10 @@ ...@@ -20,6 +20,10 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
#ifdef __APPLE__
#include <sys/ucontext.h>
#endif
#include <csignal> #include <csignal>
#include <cstdio> #include <cstdio>
...@@ -66,6 +70,32 @@ void* GetProgramCounter(void* vuc) { ...@@ -66,6 +70,32 @@ void* GetProgramCounter(void* vuc) {
#error "Undefined Architecture." #error "Undefined Architecture."
#endif #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__) #elif defined(__akaros__)
auto* ctx = reinterpret_cast<struct user_context*>(vuc); auto* ctx = reinterpret_cast<struct user_context*>(vuc);
return reinterpret_cast<void*>(get_user_ctx_pc(ctx)); return reinterpret_cast<void*>(get_user_ctx_pc(ctx));
......
...@@ -28,6 +28,27 @@ ...@@ -28,6 +28,27 @@
#define ABSL_STACKTRACE_INL_HEADER \ #define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_win32-inl.inc" "absl/debugging/internal/stacktrace_win32-inl.inc"
#elif defined(__APPLE__)
// Thread local support required for UnwindImpl.
// Notes:
// * Xcode's clang did not support `thread_local` until version 8, and
// even then not for all iOS < 9.0.
// * Xcode 9.3 started disallowing `thread_local` for 32-bit iOS simulator
// targeting iOS 9.x.
// * Xcode 10 moves the deployment target check for iOS < 9.0 to link time
// making __has_feature unreliable there.
//
// Otherwise, `__has_feature` is only supported by Clang so it has be inside
// `defined(__APPLE__)` check.
#if __has_feature(cxx_thread_local) && \
!(TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0)
#define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_generic-inl.inc"
#else
#define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_unimplemented-inl.inc"
#endif
#elif defined(__linux__) && !defined(__ANDROID__) #elif defined(__linux__) && !defined(__ANDROID__)
#if !defined(NO_FRAME_POINTER) #if !defined(NO_FRAME_POINTER)
......
...@@ -59,6 +59,12 @@ ABSL_NAMESPACE_END ...@@ -59,6 +59,12 @@ ABSL_NAMESPACE_END
#endif // ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE #endif // ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE
#ifdef ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE
#error ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE cannot be directly set
#elif defined(__APPLE__)
#define ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE 1
#endif
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
namespace debugging_internal { namespace debugging_internal {
......
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
// The Windows Symbolizer only works if PDB files containing the debug info // The Windows Symbolizer only works if PDB files containing the debug info
// are available to the program at runtime. // are available to the program at runtime.
#include "absl/debugging/symbolize_win32.inc" #include "absl/debugging/symbolize_win32.inc"
#elif defined(__APPLE__)
#include "absl/debugging/symbolize_darwin.inc"
#else #else
#include "absl/debugging/symbolize_unimplemented.inc" #include "absl/debugging/symbolize_unimplemented.inc"
#endif #endif
// 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
...@@ -144,7 +144,8 @@ static const char *TrySymbolize(void *pc) { ...@@ -144,7 +144,8 @@ static const char *TrySymbolize(void *pc) {
return TrySymbolizeWithLimit(pc, sizeof(try_symbolize_buffer)); return TrySymbolizeWithLimit(pc, sizeof(try_symbolize_buffer));
} }
#ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE #if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE) || \
defined(ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE)
TEST(Symbolize, Cached) { TEST(Symbolize, Cached) {
// Compilers should give us pointers to them. // Compilers should give us pointers to them.
...@@ -258,6 +259,7 @@ TEST(Symbolize, SymbolizeWithDemanglingStackConsumption) { ...@@ -258,6 +259,7 @@ TEST(Symbolize, SymbolizeWithDemanglingStackConsumption) {
#endif // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION #endif // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
#ifndef ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE
// Use a 64K page size for PPC. // Use a 64K page size for PPC.
const size_t kPageSize = 64 << 10; const size_t kPageSize = 64 << 10;
// We place a read-only symbols into the .text section and verify that we can // We place a read-only symbols into the .text section and verify that we can
...@@ -413,6 +415,7 @@ TEST(Symbolize, ForEachSection) { ...@@ -413,6 +415,7 @@ TEST(Symbolize, ForEachSection) {
close(fd); close(fd);
} }
#endif // !ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE
// x86 specific tests. Uses some inline assembler. // x86 specific tests. Uses some inline assembler.
extern "C" { extern "C" {
...@@ -541,7 +544,8 @@ int main(int argc, char **argv) { ...@@ -541,7 +544,8 @@ int main(int argc, char **argv) {
absl::InitializeSymbolizer(argv[0]); absl::InitializeSymbolizer(argv[0]);
testing::InitGoogleTest(&argc, argv); testing::InitGoogleTest(&argc, argv);
#ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE #if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE) || \
defined(ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE)
TestWithPCInsideInlineFunction(); TestWithPCInsideInlineFunction();
TestWithPCInsideNonInlineFunction(); TestWithPCInsideNonInlineFunction();
TestWithReturnAddress(); TestWithReturnAddress();
......
...@@ -159,14 +159,13 @@ cc_library( ...@@ -159,14 +159,13 @@ cc_library(
) )
cc_library( cc_library(
name = "registry", name = "reflection",
srcs = [ srcs = [
"internal/registry.cc", "reflection.cc",
"internal/type_erased.cc",
], ],
hdrs = [ hdrs = [
"internal/registry.h", "internal/registry.h",
"internal/type_erased.h", "reflection.h",
], ],
copts = ABSL_DEFAULT_COPTS, copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS, linkopts = ABSL_DEFAULT_LINKOPTS,
...@@ -180,7 +179,6 @@ cc_library( ...@@ -180,7 +179,6 @@ cc_library(
":private_handle_accessor", ":private_handle_accessor",
"//absl/base:config", "//absl/base:config",
"//absl/base:core_headers", "//absl/base:core_headers",
"//absl/base:raw_logging_internal",
"//absl/strings", "//absl/strings",
"//absl/synchronization", "//absl/synchronization",
], ],
...@@ -202,7 +200,7 @@ cc_library( ...@@ -202,7 +200,7 @@ cc_library(
":commandlineflag_internal", ":commandlineflag_internal",
":config", ":config",
":marshalling", ":marshalling",
":registry", ":reflection",
"//absl/base", "//absl/base",
"//absl/base:config", "//absl/base:config",
"//absl/base:core_headers", "//absl/base:core_headers",
...@@ -228,7 +226,7 @@ cc_library( ...@@ -228,7 +226,7 @@ cc_library(
deps = [ deps = [
":config", ":config",
":flag_internal", ":flag_internal",
":registry", ":reflection",
"//absl/base", "//absl/base",
"//absl/base:config", "//absl/base:config",
"//absl/base:core_headers", "//absl/base:core_headers",
...@@ -257,7 +255,7 @@ cc_library( ...@@ -257,7 +255,7 @@ cc_library(
":path_util", ":path_util",
":private_handle_accessor", ":private_handle_accessor",
":program_name", ":program_name",
":registry", ":reflection",
"//absl/base:config", "//absl/base:config",
"//absl/base:core_headers", "//absl/base:core_headers",
"//absl/strings", "//absl/strings",
...@@ -300,7 +298,7 @@ cc_library( ...@@ -300,7 +298,7 @@ cc_library(
":flag_internal", ":flag_internal",
":private_handle_accessor", ":private_handle_accessor",
":program_name", ":program_name",
":registry", ":reflection",
":usage", ":usage",
":usage_internal", ":usage_internal",
"//absl/base:config", "//absl/base:config",
...@@ -327,7 +325,7 @@ cc_test( ...@@ -327,7 +325,7 @@ cc_test(
":config", ":config",
":flag", ":flag",
":private_handle_accessor", ":private_handle_accessor",
":registry", ":reflection",
"//absl/memory", "//absl/memory",
"//absl/strings", "//absl/strings",
"@com_google_googletest//:gtest_main", "@com_google_googletest//:gtest_main",
...@@ -362,7 +360,7 @@ cc_test( ...@@ -362,7 +360,7 @@ cc_test(
":flag", ":flag",
":flag_internal", ":flag_internal",
":marshalling", ":marshalling",
":registry", ":reflection",
"//absl/base:core_headers", "//absl/base:core_headers",
"//absl/base:malloc_internal", "//absl/base:malloc_internal",
"//absl/strings", "//absl/strings",
...@@ -415,7 +413,7 @@ cc_test( ...@@ -415,7 +413,7 @@ cc_test(
deps = [ deps = [
":flag", ":flag",
":parse", ":parse",
":registry", ":reflection",
"//absl/base:raw_logging_internal", "//absl/base:raw_logging_internal",
"//absl/base:scoped_set_env", "//absl/base:scoped_set_env",
"//absl/strings", "//absl/strings",
...@@ -454,17 +452,18 @@ cc_test( ...@@ -454,17 +452,18 @@ cc_test(
) )
cc_test( cc_test(
name = "type_erased_test", name = "reflection_test",
size = "small", size = "small",
srcs = [ srcs = [
"internal/type_erased_test.cc", "reflection_test.cc",
], ],
copts = ABSL_TEST_COPTS, copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS, linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [ deps = [
":commandlineflag_internal", ":commandlineflag_internal",
":flag", ":flag",
":registry", ":marshalling",
":reflection",
"//absl/memory", "//absl/memory",
"@com_google_googletest//:gtest_main", "@com_google_googletest//:gtest_main",
], ],
...@@ -501,7 +500,7 @@ cc_test( ...@@ -501,7 +500,7 @@ cc_test(
":parse", ":parse",
":path_util", ":path_util",
":program_name", ":program_name",
":registry", ":reflection",
":usage", ":usage",
":usage_internal", ":usage_internal",
"//absl/strings", "//absl/strings",
......
...@@ -144,16 +144,14 @@ absl_cc_library( ...@@ -144,16 +144,14 @@ absl_cc_library(
absl::strings absl::strings
) )
# Internal-only target, do not depend on directly.
absl_cc_library( absl_cc_library(
NAME NAME
flags_registry flags_reflection
SRCS SRCS
"internal/registry.cc" "reflection.cc"
"internal/type_erased.cc"
HDRS HDRS
"reflection.h"
"internal/registry.h" "internal/registry.h"
"internal/type_erased.h"
COPTS COPTS
${ABSL_DEFAULT_COPTS} ${ABSL_DEFAULT_COPTS}
LINKOPTS LINKOPTS
...@@ -161,10 +159,8 @@ absl_cc_library( ...@@ -161,10 +159,8 @@ absl_cc_library(
DEPS DEPS
absl::config absl::config
absl::flags_commandlineflag absl::flags_commandlineflag
absl::flags_config
absl::flags_private_handle_accessor absl::flags_private_handle_accessor
absl::core_headers absl::flags_config
absl::raw_logging_internal
absl::strings absl::strings
absl::synchronization absl::synchronization
) )
...@@ -187,7 +183,6 @@ absl_cc_library( ...@@ -187,7 +183,6 @@ absl_cc_library(
absl::flags_commandlineflag_internal absl::flags_commandlineflag_internal
absl::flags_config absl::flags_config
absl::flags_marshalling absl::flags_marshalling
absl::flags_registry
absl::synchronization absl::synchronization
absl::meta absl::meta
absl::utility absl::utility
...@@ -211,7 +206,7 @@ absl_cc_library( ...@@ -211,7 +206,7 @@ absl_cc_library(
absl::flags_commandlineflag absl::flags_commandlineflag
absl::flags_config absl::flags_config
absl::flags_internal absl::flags_internal
absl::flags_registry absl::flags_reflection
absl::base absl::base
absl::core_headers absl::core_headers
absl::strings absl::strings
...@@ -238,7 +233,7 @@ absl_cc_library( ...@@ -238,7 +233,7 @@ absl_cc_library(
absl::flags_path_util absl::flags_path_util
absl::flags_private_handle_accessor absl::flags_private_handle_accessor
absl::flags_program_name absl::flags_program_name
absl::flags_registry absl::flags_reflection
absl::strings absl::strings
absl::synchronization absl::synchronization
) )
...@@ -284,7 +279,7 @@ absl_cc_library( ...@@ -284,7 +279,7 @@ absl_cc_library(
absl::flags_internal absl::flags_internal
absl::flags_private_handle_accessor absl::flags_private_handle_accessor
absl::flags_program_name absl::flags_program_name
absl::flags_registry absl::flags_reflection
absl::flags_usage absl::flags_usage
absl::strings absl::strings
absl::synchronization absl::synchronization
...@@ -306,7 +301,7 @@ absl_cc_test( ...@@ -306,7 +301,7 @@ absl_cc_test(
absl::flags_commandlineflag_internal absl::flags_commandlineflag_internal
absl::flags_config absl::flags_config
absl::flags_private_handle_accessor absl::flags_private_handle_accessor
absl::flags_registry absl::flags_reflection
absl::memory absl::memory
absl::strings absl::strings
gtest_main gtest_main
...@@ -338,7 +333,7 @@ absl_cc_test( ...@@ -338,7 +333,7 @@ absl_cc_test(
absl::flags_config absl::flags_config
absl::flags_internal absl::flags_internal
absl::flags_marshalling absl::flags_marshalling
absl::flags_registry absl::flags_reflection
absl::strings absl::strings
absl::time absl::time
gtest_main gtest_main
...@@ -366,7 +361,7 @@ absl_cc_test( ...@@ -366,7 +361,7 @@ absl_cc_test(
DEPS DEPS
absl::flags absl::flags
absl::flags_parse absl::flags_parse
absl::flags_registry absl::flags_reflection
absl::raw_logging_internal absl::raw_logging_internal
absl::scoped_set_env absl::scoped_set_env
absl::span absl::span
...@@ -401,15 +396,15 @@ absl_cc_test( ...@@ -401,15 +396,15 @@ absl_cc_test(
absl_cc_test( absl_cc_test(
NAME NAME
flags_type_erased_test flags_reflection_test
SRCS SRCS
"internal/type_erased_test.cc" "reflection_test.cc"
COPTS COPTS
${ABSL_TEST_COPTS} ${ABSL_TEST_COPTS}
DEPS DEPS
absl::flags_commandlineflag_internal absl::flags_commandlineflag_internal
absl::flags absl::flags
absl::flags_registry absl::flags_reflection
absl::memory absl::memory
absl::strings absl::strings
gtest_main gtest_main
...@@ -443,7 +438,7 @@ absl_cc_test( ...@@ -443,7 +438,7 @@ absl_cc_test(
absl::flags_path_util absl::flags_path_util
absl::flags_program_name absl::flags_program_name
absl::flags_parse absl::flags_parse
absl::flags_registry absl::flags_reflection
absl::flags_usage absl::flags_usage
absl::strings absl::strings
gtest gtest
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
#include "absl/flags/flag.h" #include "absl/flags/flag.h"
#include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/private_handle_accessor.h" #include "absl/flags/internal/private_handle_accessor.h"
#include "absl/flags/internal/registry.h" #include "absl/flags/reflection.h"
#include "absl/flags/usage_config.h" #include "absl/flags/usage_config.h"
#include "absl/memory/memory.h" #include "absl/memory/memory.h"
#include "absl/strings/match.h" #include "absl/strings/match.h"
...@@ -64,7 +64,7 @@ class CommandLineFlagTest : public testing::Test { ...@@ -64,7 +64,7 @@ class CommandLineFlagTest : public testing::Test {
}; };
TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) { TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) {
auto* flag_01 = flags::FindCommandLineFlag("int_flag"); auto* flag_01 = absl::FindCommandLineFlag("int_flag");
ASSERT_TRUE(flag_01); ASSERT_TRUE(flag_01);
EXPECT_EQ(flag_01->Name(), "int_flag"); EXPECT_EQ(flag_01->Name(), "int_flag");
...@@ -77,7 +77,7 @@ TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) { ...@@ -77,7 +77,7 @@ TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) {
"absl/flags/commandlineflag_test.cc")) "absl/flags/commandlineflag_test.cc"))
<< flag_01->Filename(); << flag_01->Filename();
auto* flag_02 = flags::FindCommandLineFlag("string_flag"); auto* flag_02 = absl::FindCommandLineFlag("string_flag");
ASSERT_TRUE(flag_02); ASSERT_TRUE(flag_02);
EXPECT_EQ(flag_02->Name(), "string_flag"); EXPECT_EQ(flag_02->Name(), "string_flag");
...@@ -89,31 +89,20 @@ TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) { ...@@ -89,31 +89,20 @@ TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) {
EXPECT_TRUE(absl::EndsWith(flag_02->Filename(), EXPECT_TRUE(absl::EndsWith(flag_02->Filename(),
"absl/flags/commandlineflag_test.cc")) "absl/flags/commandlineflag_test.cc"))
<< flag_02->Filename(); << flag_02->Filename();
auto* flag_03 = flags::FindRetiredFlag("bool_retired_flag");
ASSERT_TRUE(flag_03);
EXPECT_EQ(flag_03->Name(), "bool_retired_flag");
EXPECT_EQ(flag_03->Help(), "");
EXPECT_TRUE(flag_03->IsRetired());
EXPECT_TRUE(flag_03->IsOfType<bool>());
EXPECT_TRUE(!flag_03->IsOfType<int>());
EXPECT_TRUE(!flag_03->IsOfType<std::string>());
EXPECT_EQ(flag_03->Filename(), "RETIRED");
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
TEST_F(CommandLineFlagTest, TestValueAccessMethods) { TEST_F(CommandLineFlagTest, TestValueAccessMethods) {
absl::SetFlag(&FLAGS_int_flag, 301); absl::SetFlag(&FLAGS_int_flag, 301);
auto* flag_01 = flags::FindCommandLineFlag("int_flag"); auto* flag_01 = absl::FindCommandLineFlag("int_flag");
ASSERT_TRUE(flag_01); ASSERT_TRUE(flag_01);
EXPECT_EQ(flag_01->CurrentValue(), "301"); EXPECT_EQ(flag_01->CurrentValue(), "301");
EXPECT_EQ(flag_01->DefaultValue(), "201"); EXPECT_EQ(flag_01->DefaultValue(), "201");
absl::SetFlag(&FLAGS_string_flag, "new_str_value"); absl::SetFlag(&FLAGS_string_flag, "new_str_value");
auto* flag_02 = flags::FindCommandLineFlag("string_flag"); auto* flag_02 = absl::FindCommandLineFlag("string_flag");
ASSERT_TRUE(flag_02); ASSERT_TRUE(flag_02);
EXPECT_EQ(flag_02->CurrentValue(), "new_str_value"); EXPECT_EQ(flag_02->CurrentValue(), "new_str_value");
...@@ -125,7 +114,7 @@ TEST_F(CommandLineFlagTest, TestValueAccessMethods) { ...@@ -125,7 +114,7 @@ TEST_F(CommandLineFlagTest, TestValueAccessMethods) {
TEST_F(CommandLineFlagTest, TestParseFromCurrentValue) { TEST_F(CommandLineFlagTest, TestParseFromCurrentValue) {
std::string err; std::string err;
auto* flag_01 = flags::FindCommandLineFlag("int_flag"); auto* flag_01 = absl::FindCommandLineFlag("int_flag");
EXPECT_FALSE( EXPECT_FALSE(
flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01)); flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01));
...@@ -173,7 +162,7 @@ TEST_F(CommandLineFlagTest, TestParseFromCurrentValue) { ...@@ -173,7 +162,7 @@ TEST_F(CommandLineFlagTest, TestParseFromCurrentValue) {
*flag_01, "", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, err)); *flag_01, "", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, err));
EXPECT_EQ(err, "Illegal value '' specified for flag 'int_flag'"); EXPECT_EQ(err, "Illegal value '' specified for flag 'int_flag'");
auto* flag_02 = flags::FindCommandLineFlag("string_flag"); auto* flag_02 = absl::FindCommandLineFlag("string_flag");
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
*flag_02, "xyz", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, *flag_02, "xyz", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange,
err)); err));
...@@ -189,14 +178,14 @@ TEST_F(CommandLineFlagTest, TestParseFromCurrentValue) { ...@@ -189,14 +178,14 @@ TEST_F(CommandLineFlagTest, TestParseFromCurrentValue) {
TEST_F(CommandLineFlagTest, TestParseFromDefaultValue) { TEST_F(CommandLineFlagTest, TestParseFromDefaultValue) {
std::string err; std::string err;
auto* flag_01 = flags::FindCommandLineFlag("int_flag"); auto* flag_01 = absl::FindCommandLineFlag("int_flag");
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
*flag_01, "111", flags::SET_FLAGS_DEFAULT, flags::kProgrammaticChange, *flag_01, "111", flags::SET_FLAGS_DEFAULT, flags::kProgrammaticChange,
err)); err));
EXPECT_EQ(flag_01->DefaultValue(), "111"); EXPECT_EQ(flag_01->DefaultValue(), "111");
auto* flag_02 = flags::FindCommandLineFlag("string_flag"); auto* flag_02 = absl::FindCommandLineFlag("string_flag");
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
*flag_02, "abc", flags::SET_FLAGS_DEFAULT, flags::kProgrammaticChange, *flag_02, "abc", flags::SET_FLAGS_DEFAULT, flags::kProgrammaticChange,
...@@ -209,7 +198,7 @@ TEST_F(CommandLineFlagTest, TestParseFromDefaultValue) { ...@@ -209,7 +198,7 @@ TEST_F(CommandLineFlagTest, TestParseFromDefaultValue) {
TEST_F(CommandLineFlagTest, TestParseFromIfDefault) { TEST_F(CommandLineFlagTest, TestParseFromIfDefault) {
std::string err; std::string err;
auto* flag_01 = flags::FindCommandLineFlag("int_flag"); auto* flag_01 = absl::FindCommandLineFlag("int_flag");
EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
*flag_01, "22", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange, *flag_01, "22", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange,
......
...@@ -29,8 +29,8 @@ ...@@ -29,8 +29,8 @@
#include "absl/flags/config.h" #include "absl/flags/config.h"
#include "absl/flags/declare.h" #include "absl/flags/declare.h"
#include "absl/flags/internal/flag.h" #include "absl/flags/internal/flag.h"
#include "absl/flags/internal/registry.h"
#include "absl/flags/marshalling.h" #include "absl/flags/marshalling.h"
#include "absl/flags/reflection.h"
#include "absl/flags/usage_config.h" #include "absl/flags/usage_config.h"
#include "absl/strings/match.h" #include "absl/strings/match.h"
#include "absl/strings/numbers.h" #include "absl/strings/numbers.h"
...@@ -555,29 +555,29 @@ TEST_F(FlagTest, TestGetSet) { ...@@ -555,29 +555,29 @@ TEST_F(FlagTest, TestGetSet) {
// -------------------------------------------------------------------- // --------------------------------------------------------------------
TEST_F(FlagTest, TestGetViaReflection) { TEST_F(FlagTest, TestGetViaReflection) {
auto* handle = flags::FindCommandLineFlag("test_flag_01"); auto* handle = absl::FindCommandLineFlag("test_flag_01");
EXPECT_EQ(*handle->TryGet<bool>(), true); EXPECT_EQ(*handle->TryGet<bool>(), true);
handle = flags::FindCommandLineFlag("test_flag_02"); handle = absl::FindCommandLineFlag("test_flag_02");
EXPECT_EQ(*handle->TryGet<int>(), 1234); EXPECT_EQ(*handle->TryGet<int>(), 1234);
handle = flags::FindCommandLineFlag("test_flag_03"); handle = absl::FindCommandLineFlag("test_flag_03");
EXPECT_EQ(*handle->TryGet<int16_t>(), -34); EXPECT_EQ(*handle->TryGet<int16_t>(), -34);
handle = flags::FindCommandLineFlag("test_flag_04"); handle = absl::FindCommandLineFlag("test_flag_04");
EXPECT_EQ(*handle->TryGet<uint16_t>(), 189); EXPECT_EQ(*handle->TryGet<uint16_t>(), 189);
handle = flags::FindCommandLineFlag("test_flag_05"); handle = absl::FindCommandLineFlag("test_flag_05");
EXPECT_EQ(*handle->TryGet<int32_t>(), 10765); EXPECT_EQ(*handle->TryGet<int32_t>(), 10765);
handle = flags::FindCommandLineFlag("test_flag_06"); handle = absl::FindCommandLineFlag("test_flag_06");
EXPECT_EQ(*handle->TryGet<uint32_t>(), 40000); EXPECT_EQ(*handle->TryGet<uint32_t>(), 40000);
handle = flags::FindCommandLineFlag("test_flag_07"); handle = absl::FindCommandLineFlag("test_flag_07");
EXPECT_EQ(*handle->TryGet<int64_t>(), -1234567); EXPECT_EQ(*handle->TryGet<int64_t>(), -1234567);
handle = flags::FindCommandLineFlag("test_flag_08"); handle = absl::FindCommandLineFlag("test_flag_08");
EXPECT_EQ(*handle->TryGet<uint64_t>(), 9876543); EXPECT_EQ(*handle->TryGet<uint64_t>(), 9876543);
handle = flags::FindCommandLineFlag("test_flag_09"); handle = absl::FindCommandLineFlag("test_flag_09");
EXPECT_NEAR(*handle->TryGet<double>(), -9.876e-50, 1e-55); EXPECT_NEAR(*handle->TryGet<double>(), -9.876e-50, 1e-55);
handle = flags::FindCommandLineFlag("test_flag_10"); handle = absl::FindCommandLineFlag("test_flag_10");
EXPECT_NEAR(*handle->TryGet<float>(), 1.234e12f, 1e5f); EXPECT_NEAR(*handle->TryGet<float>(), 1.234e12f, 1e5f);
handle = flags::FindCommandLineFlag("test_flag_11"); handle = absl::FindCommandLineFlag("test_flag_11");
EXPECT_EQ(*handle->TryGet<std::string>(), ""); EXPECT_EQ(*handle->TryGet<std::string>(), "");
handle = flags::FindCommandLineFlag("test_flag_12"); handle = absl::FindCommandLineFlag("test_flag_12");
EXPECT_EQ(*handle->TryGet<absl::Duration>(), absl::Minutes(10)); EXPECT_EQ(*handle->TryGet<absl::Duration>(), absl::Minutes(10));
} }
...@@ -815,14 +815,15 @@ ABSL_RETIRED_FLAG(std::string, old_str_flag, "", absl::StrCat("old ", "descr")); ...@@ -815,14 +815,15 @@ ABSL_RETIRED_FLAG(std::string, old_str_flag, "", absl::StrCat("old ", "descr"));
namespace { namespace {
TEST_F(FlagTest, TestRetiredFlagRegistration) { TEST_F(FlagTest, TestRetiredFlagRegistration) {
bool is_bool = false; auto* handle = absl::FindCommandLineFlag("old_bool_flag");
EXPECT_TRUE(flags::IsRetiredFlag("old_bool_flag", &is_bool)); EXPECT_TRUE(handle->IsOfType<bool>());
EXPECT_TRUE(is_bool); EXPECT_TRUE(handle->IsRetired());
EXPECT_TRUE(flags::IsRetiredFlag("old_int_flag", &is_bool)); handle = absl::FindCommandLineFlag("old_int_flag");
EXPECT_FALSE(is_bool); EXPECT_TRUE(handle->IsOfType<int>());
EXPECT_TRUE(flags::IsRetiredFlag("old_str_flag", &is_bool)); EXPECT_TRUE(handle->IsRetired());
EXPECT_FALSE(is_bool); handle = absl::FindCommandLineFlag("old_str_flag");
EXPECT_FALSE(flags::IsRetiredFlag("some_other_flag", &is_bool)); EXPECT_TRUE(handle->IsOfType<std::string>());
EXPECT_TRUE(handle->IsRetired());
} }
} // namespace } // namespace
......
...@@ -28,10 +28,14 @@ ...@@ -28,10 +28,14 @@
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
namespace flags_internal {
// TODO(rogeeff): remove this declaration
CommandLineFlag* FindCommandLineFlag(absl::string_view name); CommandLineFlag* FindCommandLineFlag(absl::string_view name);
CommandLineFlag* FindRetiredFlag(absl::string_view name);
namespace flags_internal {
// TODO(rogeeff): remove this alias
using absl::FindCommandLineFlag;
// Executes specified visitor for each non-retired flag in the registry. // Executes specified visitor for each non-retired flag in the registry.
// Requires the caller hold the registry lock. // Requires the caller hold the registry lock.
...@@ -85,11 +89,6 @@ inline bool RetiredFlag(const char* flag_name) { ...@@ -85,11 +89,6 @@ inline bool RetiredFlag(const char* flag_name) {
return flags_internal::Retire(flag_name, base_internal::FastTypeId<T>()); return flags_internal::Retire(flag_name, base_internal::FastTypeId<T>());
} }
// If the flag is retired, returns true and indicates in |*type_is_bool|
// whether the type of the retired flag is a bool.
// Only to be called by code that needs to explicitly ignore retired flags.
bool IsRetiredFlag(absl::string_view name, bool* type_is_bool);
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Saves the states (value, default value, whether the user has set // Saves the states (value, default value, whether the user has set
// the flag, registered validators, etc) of all flags, and restores // the flag, registered validators, etc) of all flags, and restores
......
//
// 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/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
//
// 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/commandlineflag.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_
//
// 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/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
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
#include "absl/flags/internal/parse.h" #include "absl/flags/internal/parse.h"
#include "absl/flags/internal/path_util.h" #include "absl/flags/internal/path_util.h"
#include "absl/flags/internal/program_name.h" #include "absl/flags/internal/program_name.h"
#include "absl/flags/internal/registry.h" #include "absl/flags/reflection.h"
#include "absl/flags/usage.h" #include "absl/flags/usage.h"
#include "absl/flags/usage_config.h" #include "absl/flags/usage_config.h"
#include "absl/strings/match.h" #include "absl/strings/match.h"
...@@ -110,7 +110,7 @@ TEST_F(UsageReportingDeathTest, TestSetProgramUsageMessage) { ...@@ -110,7 +110,7 @@ TEST_F(UsageReportingDeathTest, TestSetProgramUsageMessage) {
// -------------------------------------------------------------------- // --------------------------------------------------------------------
TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_01) { TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_01) {
const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_01"); const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_01");
std::stringstream test_buf; std::stringstream test_buf;
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
...@@ -122,7 +122,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_01) { ...@@ -122,7 +122,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_01) {
} }
TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_02) { TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_02) {
const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_02"); const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_02");
std::stringstream test_buf; std::stringstream test_buf;
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
...@@ -134,7 +134,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_02) { ...@@ -134,7 +134,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_02) {
} }
TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_03) { TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_03) {
const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_03"); const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_03");
std::stringstream test_buf; std::stringstream test_buf;
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
...@@ -146,7 +146,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_03) { ...@@ -146,7 +146,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_03) {
} }
TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_04) { TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_04) {
const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_04"); const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_04");
std::stringstream test_buf; std::stringstream test_buf;
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
...@@ -158,7 +158,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_04) { ...@@ -158,7 +158,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_04) {
} }
TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_05) { TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_05) {
const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_05"); const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_05");
std::stringstream test_buf; std::stringstream test_buf;
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
......
...@@ -42,8 +42,8 @@ ...@@ -42,8 +42,8 @@
#include "absl/flags/internal/parse.h" #include "absl/flags/internal/parse.h"
#include "absl/flags/internal/private_handle_accessor.h" #include "absl/flags/internal/private_handle_accessor.h"
#include "absl/flags/internal/program_name.h" #include "absl/flags/internal/program_name.h"
#include "absl/flags/internal/registry.h"
#include "absl/flags/internal/usage.h" #include "absl/flags/internal/usage.h"
#include "absl/flags/reflection.h"
#include "absl/flags/usage.h" #include "absl/flags/usage.h"
#include "absl/flags/usage_config.h" #include "absl/flags/usage_config.h"
#include "absl/strings/ascii.h" #include "absl/strings/ascii.h"
...@@ -290,11 +290,11 @@ std::tuple<absl::string_view, absl::string_view, bool> SplitNameAndValue( ...@@ -290,11 +290,11 @@ std::tuple<absl::string_view, absl::string_view, bool> SplitNameAndValue(
// found flag or nullptr // found flag or nullptr
// is negative in case of --nofoo // is negative in case of --nofoo
std::tuple<CommandLineFlag*, bool> LocateFlag(absl::string_view flag_name) { std::tuple<CommandLineFlag*, bool> LocateFlag(absl::string_view flag_name) {
CommandLineFlag* flag = flags_internal::FindCommandLineFlag(flag_name); CommandLineFlag* flag = absl::FindCommandLineFlag(flag_name);
bool is_negative = false; bool is_negative = false;
if (!flag && absl::ConsumePrefix(&flag_name, "no")) { if (!flag && absl::ConsumePrefix(&flag_name, "no")) {
flag = flags_internal::FindCommandLineFlag(flag_name); flag = absl::FindCommandLineFlag(flag_name);
is_negative = true; is_negative = true;
} }
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
#include "absl/flags/declare.h" #include "absl/flags/declare.h"
#include "absl/flags/flag.h" #include "absl/flags/flag.h"
#include "absl/flags/internal/parse.h" #include "absl/flags/internal/parse.h"
#include "absl/flags/internal/registry.h" #include "absl/flags/reflection.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "absl/strings/substitute.h" #include "absl/strings/substitute.h"
......
// //
// Copyright 2019 The Abseil Authors. // Copyright 2020 The Abseil Authors.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
...@@ -13,47 +13,34 @@ ...@@ -13,47 +13,34 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "absl/flags/internal/registry.h" #include "absl/flags/reflection.h"
#include <assert.h> #include <assert.h>
#include <stdlib.h>
#include <functional>
#include <map> #include <map>
#include <memory>
#include <string> #include <string>
#include <utility>
#include <vector>
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/thread_annotations.h" #include "absl/base/thread_annotations.h"
#include "absl/flags/commandlineflag.h" #include "absl/flags/commandlineflag.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/private_handle_accessor.h" #include "absl/flags/internal/private_handle_accessor.h"
#include "absl/flags/internal/registry.h"
#include "absl/flags/usage_config.h" #include "absl/flags/usage_config.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h" #include "absl/synchronization/mutex.h"
// --------------------------------------------------------------------
// FlagRegistry implementation
// A FlagRegistry holds all flag objects indexed
// by their names so that if you know a flag's name you can access or
// set it.
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
namespace flags_internal { namespace flags_internal {
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// FlagRegistry // FlagRegistry
// A FlagRegistry singleton object holds all flag objects indexed // A FlagRegistry singleton object holds all flag objects indexed by their
// by their names so that if you know a flag's name (as a C // names so that if you know a flag's name, you can access or set it. If the
// string), you can access or set it. If the function is named // function is named FooLocked(), you must own the registry lock before
// FooLocked(), you must own the registry lock before calling // calling the function; otherwise, you should *not* hold the lock, and the
// the function; otherwise, you should *not* hold the lock, and // function will acquire it itself if needed.
// the function will acquire it itself if needed.
// -------------------------------------------------------------------- // --------------------------------------------------------------------
class FlagRegistry { class FlagRegistry {
...@@ -95,9 +82,27 @@ class FlagRegistry { ...@@ -95,9 +82,27 @@ class FlagRegistry {
FlagRegistry& operator=(const FlagRegistry&); FlagRegistry& operator=(const FlagRegistry&);
}; };
FlagRegistry& FlagRegistry::GlobalRegistry() { CommandLineFlag* FlagRegistry::FindFlagLocked(absl::string_view name) {
static FlagRegistry* global_registry = new FlagRegistry; FlagConstIterator i = flags_.find(name);
return *global_registry; if (i == flags_.end()) {
return nullptr;
}
if (i->second->IsRetired()) {
flags_internal::ReportUsageError(
absl::StrCat("Accessing retired flag '", name, "'"), false);
}
return i->second;
}
CommandLineFlag* FlagRegistry::FindRetiredFlagLocked(absl::string_view name) {
FlagConstIterator i = flags_.find(name);
if (i == flags_.end() || !i->second->IsRetired()) {
return nullptr;
}
return i->second;
} }
namespace { namespace {
...@@ -161,98 +166,9 @@ void FlagRegistry::RegisterFlag(CommandLineFlag& flag) { ...@@ -161,98 +166,9 @@ void FlagRegistry::RegisterFlag(CommandLineFlag& flag) {
} }
} }
CommandLineFlag* FlagRegistry::FindFlagLocked(absl::string_view name) { FlagRegistry& FlagRegistry::GlobalRegistry() {
FlagConstIterator i = flags_.find(name); static FlagRegistry* global_registry = new FlagRegistry;
if (i == flags_.end()) { return *global_registry;
return nullptr;
}
if (i->second->IsRetired()) {
flags_internal::ReportUsageError(
absl::StrCat("Accessing retired flag '", name, "'"), false);
}
return i->second;
}
CommandLineFlag* FlagRegistry::FindRetiredFlagLocked(absl::string_view name) {
FlagConstIterator i = flags_.find(name);
if (i == flags_.end() || !i->second->IsRetired()) {
return nullptr;
}
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.
// --------------------------------------------------------------------
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));
}
});
}
// 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_;
};
FlagSaver::FlagSaver() : impl_(new FlagSaverImpl) { impl_->SaveFromRegistry(); }
void FlagSaver::Ignore() {
delete impl_;
impl_ = nullptr;
}
FlagSaver::~FlagSaver() {
if (!impl_) return;
impl_->RestoreToRegistry();
delete impl_;
}
// --------------------------------------------------------------------
CommandLineFlag* FindCommandLineFlag(absl::string_view name) {
if (name.empty()) return nullptr;
FlagRegistry& registry = FlagRegistry::GlobalRegistry();
FlagRegistryLock frl(registry);
return registry.FindFlagLocked(name);
}
CommandLineFlag* FindRetiredFlag(absl::string_view name) {
FlagRegistry& registry = FlagRegistry::GlobalRegistry();
FlagRegistryLock frl(registry);
return registry.FindRetiredFlagLocked(name);
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -333,17 +249,71 @@ bool Retire(const char* name, FlagFastTypeId type_id) { ...@@ -333,17 +249,71 @@ bool Retire(const char* name, FlagFastTypeId type_id) {
// -------------------------------------------------------------------- // --------------------------------------------------------------------
bool IsRetiredFlag(absl::string_view name, bool* type_is_bool) { // FlagSaver
assert(!name.empty()); // FlagSaverImpl
CommandLineFlag* flag = flags_internal::FindRetiredFlag(name); // This class stores the states of all flags at construct time,
if (flag == nullptr) { // and restores all flags to that state at destruct time.
return false; // Its major implementation challenge is that it never modifies
// pointers in the 'main' registry, so global FLAG_* vars always
// point to the right place.
// --------------------------------------------------------------------
class FlagSaverImpl {
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>(); // Restores the saved flag states into the flag registry.
return true; void RestoreToRegistry() {
for (const auto& flag_state : backup_registry_) {
flag_state->Restore();
}
}
private:
std::vector<std::unique_ptr<flags_internal::FlagStateInterface>>
backup_registry_;
};
FlagSaver::FlagSaver() : impl_(new FlagSaverImpl) { impl_->SaveFromRegistry(); }
void FlagSaver::Ignore() {
delete impl_;
impl_ = nullptr;
}
FlagSaver::~FlagSaver() {
if (!impl_) return;
impl_->RestoreToRegistry();
delete impl_;
} }
// --------------------------------------------------------------------
} // namespace flags_internal } // namespace flags_internal
CommandLineFlag* FindCommandLineFlag(absl::string_view name) {
if (name.empty()) return nullptr;
flags_internal::FlagRegistry& registry =
flags_internal::FlagRegistry::GlobalRegistry();
flags_internal::FlagRegistryLock frl(registry);
return registry.FindFlagLocked(name);
}
ABSL_NAMESPACE_END ABSL_NAMESPACE_END
} // namespace absl } // namespace absl
//
// 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
// 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);
//-----------------------------------------------------------------------------
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_REFLECTION_H_
//
// 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<flags::FlagSaver>(); }
void TearDown() override { flag_saver_.reset(); }
private:
std::unique_ptr<flags::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
...@@ -115,11 +115,12 @@ cc_library( ...@@ -115,11 +115,12 @@ cc_library(
copts = ABSL_DEFAULT_COPTS, copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS, linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [ deps = [
":random",
"//absl/base:core_headers", "//absl/base:core_headers",
"//absl/base:fast_type_id",
"//absl/meta:type_traits", "//absl/meta:type_traits",
"//absl/random/internal:distribution_caller", "//absl/random/internal:distribution_caller",
"//absl/random/internal:fast_uniform_bits", "//absl/random/internal:fast_uniform_bits",
"//absl/random/internal:mocking_bit_gen_base",
], ],
) )
...@@ -145,10 +146,11 @@ cc_library( ...@@ -145,10 +146,11 @@ cc_library(
linkopts = ABSL_DEFAULT_LINKOPTS, linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [ deps = [
":distributions", ":distributions",
":random",
"//absl/base:fast_type_id",
"//absl/container:flat_hash_map", "//absl/container:flat_hash_map",
"//absl/meta:type_traits", "//absl/meta:type_traits",
"//absl/random/internal:distribution_caller", "//absl/random/internal:distribution_caller",
"//absl/random/internal:mocking_bit_gen_base",
"//absl/strings", "//absl/strings",
"//absl/types:span", "//absl/types:span",
"//absl/types:variant", "//absl/types:variant",
...@@ -410,6 +412,7 @@ cc_test( ...@@ -410,6 +412,7 @@ cc_test(
deps = [ deps = [
":bit_gen_ref", ":bit_gen_ref",
":random", ":random",
"//absl/base:fast_type_id",
"//absl/random/internal:sequence_urbg", "//absl/random/internal:sequence_urbg",
"@com_google_googletest//:gtest_main", "@com_google_googletest//:gtest_main",
], ],
......
...@@ -45,7 +45,6 @@ absl_cc_library( ...@@ -45,7 +45,6 @@ absl_cc_library(
absl::core_headers absl::core_headers
absl::random_internal_distribution_caller absl::random_internal_distribution_caller
absl::random_internal_fast_uniform_bits absl::random_internal_fast_uniform_bits
absl::random_internal_mocking_bit_gen_base
absl::type_traits absl::type_traits
) )
...@@ -62,6 +61,7 @@ absl_cc_test( ...@@ -62,6 +61,7 @@ absl_cc_test(
absl::random_bit_gen_ref absl::random_bit_gen_ref
absl::random_random absl::random_random
absl::random_internal_sequence_urbg absl::random_internal_sequence_urbg
absl::fast_type_id
gmock gmock
gtest_main gtest_main
) )
...@@ -69,21 +69,6 @@ absl_cc_test( ...@@ -69,21 +69,6 @@ absl_cc_test(
# Internal-only target, do not depend on directly. # Internal-only target, do not depend on directly.
absl_cc_library( absl_cc_library(
NAME NAME
random_internal_mocking_bit_gen_base
HDRS
"internal/mocking_bit_gen_base.h"
COPTS
${ABSL_DEFAULT_COPTS}
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
absl::random_random
absl::strings
)
# Internal-only target, do not depend on directly.
absl_cc_library(
NAME
random_internal_mock_overload_set random_internal_mock_overload_set
HDRS HDRS
"internal/mock_overload_set.h" "internal/mock_overload_set.h"
...@@ -93,6 +78,7 @@ absl_cc_library( ...@@ -93,6 +78,7 @@ absl_cc_library(
${ABSL_DEFAULT_LINKOPTS} ${ABSL_DEFAULT_LINKOPTS}
DEPS DEPS
absl::random_mocking_bit_gen absl::random_mocking_bit_gen
absl::fast_type_id
TESTONLY TESTONLY
) )
...@@ -111,8 +97,8 @@ absl_cc_library( ...@@ -111,8 +97,8 @@ absl_cc_library(
absl::raw_logging_internal absl::raw_logging_internal
absl::random_distributions absl::random_distributions
absl::random_internal_distribution_caller absl::random_internal_distribution_caller
absl::random_internal_mocking_bit_gen_base
absl::random_internal_mock_overload_set absl::random_internal_mock_overload_set
absl::random_random
absl::strings absl::strings
absl::span absl::span
absl::type_traits absl::type_traits
...@@ -533,6 +519,8 @@ absl_cc_library( ...@@ -533,6 +519,8 @@ absl_cc_library(
${ABSL_DEFAULT_LINKOPTS} ${ABSL_DEFAULT_LINKOPTS}
DEPS DEPS
absl::config absl::config
absl::utility
absl::fast_type_id
) )
# Internal-only target, do not depend on directly. # Internal-only target, do not depend on directly.
......
...@@ -24,11 +24,11 @@ ...@@ -24,11 +24,11 @@
#ifndef ABSL_RANDOM_BIT_GEN_REF_H_ #ifndef ABSL_RANDOM_BIT_GEN_REF_H_
#define ABSL_RANDOM_BIT_GEN_REF_H_ #define ABSL_RANDOM_BIT_GEN_REF_H_
#include "absl/base/internal/fast_type_id.h"
#include "absl/base/macros.h" #include "absl/base/macros.h"
#include "absl/meta/type_traits.h" #include "absl/meta/type_traits.h"
#include "absl/random/internal/distribution_caller.h" #include "absl/random/internal/distribution_caller.h"
#include "absl/random/internal/fast_uniform_bits.h" #include "absl/random/internal/fast_uniform_bits.h"
#include "absl/random/internal/mocking_bit_gen_base.h"
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
...@@ -51,6 +51,9 @@ struct is_urbg< ...@@ -51,6 +51,9 @@ struct is_urbg<
typename std::decay<decltype(std::declval<URBG>()())>::type>::value>> typename std::decay<decltype(std::declval<URBG>()())>::type>::value>>
: std::true_type {}; : std::true_type {};
template <typename>
struct DistributionCaller;
} // namespace random_internal } // namespace random_internal
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
...@@ -77,23 +80,50 @@ struct is_urbg< ...@@ -77,23 +80,50 @@ struct is_urbg<
// } // }
// //
class BitGenRef { class BitGenRef {
public: // SFINAE to detect whether the URBG type includes a member matching
using result_type = uint64_t; // bool InvokeMock(base_internal::FastTypeIdType, void*, void*).
//
// These live inside BitGenRef so that they have friend access
// to MockingBitGen. (see similar methods in DistributionCaller).
template <template <class...> class Trait, class AlwaysVoid, class... Args>
struct detector : std::false_type {};
template <template <class...> class Trait, class... Args>
struct detector<Trait, absl::void_t<Trait<Args...>>, Args...>
: std::true_type {};
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;
BitGenRef(const absl::BitGenRef&) = default; public:
BitGenRef(absl::BitGenRef&&) = default; BitGenRef(const BitGenRef&) = default;
BitGenRef& operator=(const absl::BitGenRef&) = default; BitGenRef(BitGenRef&&) = default;
BitGenRef& operator=(absl::BitGenRef&&) = default; BitGenRef& operator=(const BitGenRef&) = default;
BitGenRef& operator=(BitGenRef&&) = default;
template <typename URBG, typename absl::enable_if_t<
(!std::is_same<URBG, BitGenRef>::value &&
random_internal::is_urbg<URBG>::value &&
!HasInvokeMock<URBG>::value)>* = nullptr>
BitGenRef(URBG& gen) // NOLINT
: t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)),
mock_call_(NotAMock),
generate_impl_fn_(ImplFn<URBG>) {}
template <typename URBG, template <typename URBG,
typename absl::enable_if_t< typename absl::enable_if_t<(!std::is_same<URBG, BitGenRef>::value &&
(!std::is_same<URBG, BitGenRef>::value && random_internal::is_urbg<URBG>::value &&
random_internal::is_urbg<URBG>::value)>* = nullptr> HasInvokeMock<URBG>::value)>* = nullptr>
BitGenRef(URBG& gen) // NOLINT BitGenRef(URBG& gen) // NOLINT
: mocked_gen_ptr_(MakeMockPointer(&gen)), : t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)),
t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)), mock_call_(&MockCall<URBG>),
generate_impl_fn_(ImplFn<URBG>) { generate_impl_fn_(ImplFn<URBG>) {}
}
using result_type = uint64_t;
static constexpr result_type(min)() { static constexpr result_type(min)() {
return (std::numeric_limits<result_type>::min)(); return (std::numeric_limits<result_type>::min)();
...@@ -106,14 +136,9 @@ class BitGenRef { ...@@ -106,14 +136,9 @@ class BitGenRef {
result_type operator()() { return generate_impl_fn_(t_erased_gen_ptr_); } result_type operator()() { return generate_impl_fn_(t_erased_gen_ptr_); }
private: private:
friend struct absl::random_internal::DistributionCaller<absl::BitGenRef>;
using impl_fn = result_type (*)(uintptr_t); using impl_fn = result_type (*)(uintptr_t);
using mocker_base_t = absl::random_internal::MockingBitGenBase; using mock_call_fn = bool (*)(uintptr_t, base_internal::FastTypeIdType, void*,
void*);
// Convert an arbitrary URBG pointer into either a valid mocker_base_t
// pointer or a nullptr.
static inline mocker_base_t* MakeMockPointer(mocker_base_t* t) { return t; }
static inline mocker_base_t* MakeMockPointer(void*) { return nullptr; }
template <typename URBG> template <typename URBG>
static result_type ImplFn(uintptr_t ptr) { static result_type ImplFn(uintptr_t ptr) {
...@@ -123,29 +148,31 @@ class BitGenRef { ...@@ -123,29 +148,31 @@ class BitGenRef {
return fast_uniform_bits(*reinterpret_cast<URBG*>(ptr)); return fast_uniform_bits(*reinterpret_cast<URBG*>(ptr));
} }
mocker_base_t* mocked_gen_ptr_; // Get a type-erased InvokeMock pointer.
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_; uintptr_t t_erased_gen_ptr_;
mock_call_fn mock_call_;
impl_fn generate_impl_fn_; impl_fn generate_impl_fn_;
};
namespace random_internal {
template <> template <typename>
struct DistributionCaller<absl::BitGenRef> { friend struct ::absl::random_internal::DistributionCaller; // for InvokeMock
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)...);
}
}
}; };
} // namespace random_internal
ABSL_NAMESPACE_END ABSL_NAMESPACE_END
} // namespace absl } // namespace absl
......
...@@ -17,30 +17,31 @@ ...@@ -17,30 +17,31 @@
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "absl/base/internal/fast_type_id.h"
#include "absl/random/internal/sequence_urbg.h" #include "absl/random/internal/sequence_urbg.h"
#include "absl/random/random.h" #include "absl/random/random.h"
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
class ConstBitGen : public absl::random_internal::MockingBitGenBase { class ConstBitGen {
bool CallImpl(const std::type_info&, void*, void* result) override { public:
// URBG interface
using result_type = absl::BitGen::result_type;
static constexpr result_type(min)() { return (absl::BitGen::min)(); }
static constexpr result_type(max)() { return (absl::BitGen::max)(); }
result_type operator()() { return 1; }
// InvokeMock method
bool InvokeMock(base_internal::FastTypeIdType index, void*, void* result) {
*static_cast<int*>(result) = 42; *static_cast<int*>(result) = 42;
return true; return true;
} }
}; };
namespace random_internal {
template <>
struct DistributionCaller<ConstBitGen> {
template <typename DistrT, typename FormatT, typename... Args>
static typename DistrT::result_type Call(ConstBitGen* gen, Args&&... args) {
return gen->template Call<DistrT, FormatT>(std::forward<Args>(args)...);
}
};
} // namespace random_internal
namespace { namespace {
int FnTest(absl::BitGenRef gen_ref) { return absl::Uniform(gen_ref, 1, 7); } int FnTest(absl::BitGenRef gen_ref) { return absl::Uniform(gen_ref, 1, 7); }
template <typename T> template <typename T>
......
...@@ -45,7 +45,11 @@ cc_library( ...@@ -45,7 +45,11 @@ cc_library(
hdrs = ["distribution_caller.h"], hdrs = ["distribution_caller.h"],
copts = ABSL_DEFAULT_COPTS, copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS, linkopts = ABSL_DEFAULT_LINKOPTS,
deps = ["//absl/base:config"], deps = [
"//absl/base:config",
"//absl/base:fast_type_id",
"//absl/utility",
],
) )
cc_library( cc_library(
...@@ -481,20 +485,11 @@ cc_test( ...@@ -481,20 +485,11 @@ cc_test(
) )
cc_library( cc_library(
name = "mocking_bit_gen_base",
hdrs = ["mocking_bit_gen_base.h"],
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
"//absl/random",
"//absl/strings",
],
)
cc_library(
name = "mock_overload_set", name = "mock_overload_set",
testonly = 1, testonly = 1,
hdrs = ["mock_overload_set.h"], hdrs = ["mock_overload_set.h"],
deps = [ deps = [
"//absl/base:fast_type_id",
"//absl/random:mocking_bit_gen", "//absl/random:mocking_bit_gen",
"@com_google_googletest//:gtest", "@com_google_googletest//:gtest",
], ],
......
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
#include <utility> #include <utility>
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/base/internal/fast_type_id.h"
#include "absl/utility/utility.h"
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
...@@ -30,14 +32,55 @@ namespace random_internal { ...@@ -30,14 +32,55 @@ namespace random_internal {
// to intercept such calls. // to intercept such calls.
template <typename URBG> template <typename URBG>
struct DistributionCaller { struct DistributionCaller {
// Call the provided distribution type. The parameters are expected // SFINAE to detect whether the URBG type includes a member matching
// to be explicitly specified. // bool InvokeMock(base_internal::FastTypeIdType, void*, void*).
// DistrT is the distribution type. //
// These live inside BitGenRef so that they have friend access
// to MockingBitGen. (see similar methods in DistributionCaller).
template <template <class...> class Trait, class AlwaysVoid, class... Args>
struct detector : std::false_type {};
template <template <class...> class Trait, class... Args>
struct detector<Trait, absl::void_t<Trait<Args...>>, Args...>
: std::true_type {};
template <class T>
using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock(
std::declval<::absl::base_internal::FastTypeIdType>(),
std::declval<void*>(), std::declval<void*>()));
using HasInvokeMock = typename detector<invoke_mock_t, void, URBG>::type;
// Default implementation of distribution caller.
template <typename DistrT, typename... Args> template <typename DistrT, typename... Args>
static typename DistrT::result_type Call(URBG* urbg, Args&&... args) { static inline typename DistrT::result_type Impl(std::false_type, URBG* urbg,
Args&&... args) {
DistrT dist(std::forward<Args>(args)...); DistrT dist(std::forward<Args>(args)...);
return dist(*urbg); return dist(*urbg);
} }
// Mock implementation of distribution caller.
template <typename DistrT, typename... Args>
static inline 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>...>;
ArgTupleT arg_tuple(std::forward<Args>(args)...);
ResultT result;
if (!urbg->InvokeMock(
::absl::base_internal::FastTypeId<ResultT(DistrT, ArgTupleT)>(),
&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 inline typename DistrT::result_type Call(URBG* urbg, Args&&... args) {
return Impl<DistrT, Args...>(HasInvokeMock{}, urbg,
std::forward<Args>(args)...);
}
}; };
} // namespace random_internal } // namespace random_internal
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "absl/base/internal/fast_type_id.h"
#include "absl/random/mocking_bit_gen.h" #include "absl/random/mocking_bit_gen.h"
namespace absl { namespace absl {
...@@ -35,17 +36,23 @@ struct MockSingleOverload; ...@@ -35,17 +36,23 @@ struct MockSingleOverload;
// EXPECT_CALL(mock_single_overload, Call(...))` will expand to a call to // EXPECT_CALL(mock_single_overload, Call(...))` will expand to a call to
// `mock_single_overload.gmock_Call(...)`. Because expectations are stored on // `mock_single_overload.gmock_Call(...)`. Because expectations are stored on
// the MockingBitGen (an argument passed inside `Call(...)`), this forwards to // the MockingBitGen (an argument passed inside `Call(...)`), this forwards to
// arguments to Mocking::Register. // arguments to MockingBitGen::Register.
template <typename DistrT, typename Ret, typename... Args> template <typename DistrT, typename Ret, typename... Args>
struct MockSingleOverload<DistrT, Ret(MockingBitGen&, Args...)> { struct MockSingleOverload<DistrT, Ret(MockingBitGen&, Args...)> {
static_assert(std::is_same<typename DistrT::result_type, Ret>::value, static_assert(std::is_same<typename DistrT::result_type, Ret>::value,
"Overload signature must have return type matching the " "Overload signature must have return type matching the "
"distributions result type."); "distribution result_type.");
using ArgTupleT = std::tuple<Args...>;
auto gmock_Call( auto gmock_Call(
absl::MockingBitGen& gen, // NOLINT(google-runtime-references) absl::MockingBitGen& gen, // NOLINT(google-runtime-references)
const ::testing::Matcher<Args>&... args) const ::testing::Matcher<Args>&... matchers)
-> decltype(gen.Register<DistrT, Args...>(args...)) { -> decltype(gen.RegisterMock<Ret, ArgTupleT>(
return gen.Register<DistrT, Args...>(args...); std::declval<::absl::base_internal::FastTypeIdType>())
.gmock_Call(matchers...)) {
return gen
.RegisterMock<Ret, ArgTupleT>(
::absl::base_internal::FastTypeId<Ret(DistrT, ArgTupleT)>())
.gmock_Call(matchers...);
} }
}; };
...@@ -53,13 +60,19 @@ template <typename DistrT, typename Ret, typename Arg, typename... Args> ...@@ -53,13 +60,19 @@ template <typename DistrT, typename Ret, typename Arg, typename... Args>
struct MockSingleOverload<DistrT, Ret(Arg, MockingBitGen&, Args...)> { struct MockSingleOverload<DistrT, Ret(Arg, MockingBitGen&, Args...)> {
static_assert(std::is_same<typename DistrT::result_type, Ret>::value, static_assert(std::is_same<typename DistrT::result_type, Ret>::value,
"Overload signature must have return type matching the " "Overload signature must have return type matching the "
"distributions result type."); "distribution result_type.");
using ArgTupleT = std::tuple<Arg, Args...>;
auto gmock_Call( auto gmock_Call(
const ::testing::Matcher<Arg>& arg, const ::testing::Matcher<Arg>& matcher,
absl::MockingBitGen& gen, // NOLINT(google-runtime-references) absl::MockingBitGen& gen, // NOLINT(google-runtime-references)
const ::testing::Matcher<Args>&... args) const ::testing::Matcher<Args>&... matchers)
-> decltype(gen.Register<DistrT, Arg, Args...>(arg, args...)) { -> decltype(gen.RegisterMock<Ret, ArgTupleT>(
return gen.Register<DistrT, Arg, Args...>(arg, args...); std::declval<::absl::base_internal::FastTypeIdType>())
.gmock_Call(matcher, matchers...)) {
return gen
.RegisterMock<Ret, ArgTupleT>(
::absl::base_internal::FastTypeId<Ret(DistrT, ArgTupleT)>())
.gmock_Call(matcher, matchers...);
} }
}; };
......
//
// 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_
...@@ -27,6 +27,11 @@ ...@@ -27,6 +27,11 @@
// More information about the Googletest testing framework is available at // More information about the Googletest testing framework is available at
// https://github.com/google/googletest // https://github.com/google/googletest
// //
// EXPECT_CALL and ON_CALL need to be made within the same DLL component as
// the call to absl::Uniform and related methods, otherwise mocking will fail
// since the underlying implementation creates a type-specific pointer which
// will be distinct across different DLL boundaries.
//
// Example: // Example:
// //
// absl::MockingBitGen mock; // absl::MockingBitGen mock;
......
...@@ -33,17 +33,16 @@ ...@@ -33,17 +33,16 @@
#include <memory> #include <memory>
#include <tuple> #include <tuple>
#include <type_traits> #include <type_traits>
#include <typeindex>
#include <typeinfo>
#include <utility> #include <utility>
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "absl/base/internal/fast_type_id.h"
#include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_map.h"
#include "absl/meta/type_traits.h" #include "absl/meta/type_traits.h"
#include "absl/random/distributions.h" #include "absl/random/distributions.h"
#include "absl/random/internal/distribution_caller.h" #include "absl/random/internal/distribution_caller.h"
#include "absl/random/internal/mocking_bit_gen_base.h" #include "absl/random/random.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h" #include "absl/strings/str_join.h"
#include "absl/types/span.h" #include "absl/types/span.h"
...@@ -54,11 +53,13 @@ namespace absl { ...@@ -54,11 +53,13 @@ namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
namespace random_internal { namespace random_internal {
template <typename, typename> template <typename, typename>
struct MockSingleOverload; struct MockSingleOverload;
template <typename>
struct DistributionCaller;
} // namespace random_internal } // namespace random_internal
class BitGenRef;
// MockingBitGen // MockingBitGen
// //
...@@ -96,102 +97,132 @@ struct MockSingleOverload; ...@@ -96,102 +97,132 @@ struct MockSingleOverload;
// At this time, only mock distributions supplied within the Abseil random // At this time, only mock distributions supplied within the Abseil random
// library are officially supported. // library are officially supported.
// //
class MockingBitGen : public absl::random_internal::MockingBitGenBase { // EXPECT_CALL and ON_CALL need to be made within the same DLL component as
// the call to absl::Uniform and related methods, otherwise mocking will fail
// since the underlying implementation creates a type-specific pointer which
// will be distinct across different DLL boundaries.
//
class MockingBitGen {
public: public:
MockingBitGen() {} MockingBitGen() = default;
~MockingBitGen() override { ~MockingBitGen() {
for (const auto& del : deleters_) del(); for (const auto& del : deleters_) del();
} }
// URBG interface
using result_type = absl::BitGen::result_type;
static constexpr result_type(min)() { return (absl::BitGen::min)(); }
static constexpr result_type(max)() { return (absl::BitGen::max)(); }
result_type operator()() { return gen_(); }
private: private:
template <typename DistrT, typename... Args> using match_impl_fn = void (*)(void* mock_fn, void* t_erased_arg_tuple,
using MockFnType = void* t_erased_result);
::testing::MockFunction<typename DistrT::result_type(Args...)>;
// MockingBitGen::Register struct MockData {
void* mock_fn = nullptr;
match_impl_fn match_impl = nullptr;
};
// 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...)>;
// 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
// //
// Register<DistrT, FormatT, ArgTupleT> is the main extension point for // RegisterMock<ResultT, ArgTupleT>(FastTypeIdType) is the main extension
// extending the MockingBitGen framework. It provides a mechanism to install a // point for extending the MockingBitGen framework. It provides a mechanism to
// mock expectation for the distribution `distr_t` onto the MockingBitGen // install a mock expectation for a function like ResultT(Args...) keyed by
// context. // 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 // The returned MockFunction<...> type can be used to setup additional
// distribution parameters of the expectation. // distribution parameters of the expectation.
template <typename DistrT, typename... Args, typename... Ms> template <typename ResultT, typename ArgTupleT>
decltype(std::declval<MockFnType<DistrT, Args...>>().gmock_Call( auto RegisterMock(base_internal::FastTypeIdType type)
std::declval<Ms>()...)) -> decltype(GetMockFnType(std::declval<ResultT>(),
Register(Ms&&... matchers) { std::declval<ArgTupleT>()))& {
auto& mock = using MockFnType = decltype(
mocks_[std::type_index(GetTypeId<DistrT, std::tuple<Args...>>())]; GetMockFnType(std::declval<ResultT>(), std::declval<ArgTupleT>()));
auto& mock = mocks_[type];
if (!mock.mock_fn) { if (!mock.mock_fn) {
auto* mock_fn = new MockFnType<DistrT, Args...>; auto* mock_fn = new MockFnType;
mock.mock_fn = mock_fn; mock.mock_fn = mock_fn;
mock.match_impl = &MatchImpl<DistrT, Args...>; mock.match_impl = &MatchImpl<ResultT, ArgTupleT>;
deleters_.emplace_back([mock_fn] { delete mock_fn; }); deleters_.emplace_back([mock_fn] { delete mock_fn; });
} }
return *static_cast<MockFnType*>(mock.mock_fn);
return static_cast<MockFnType<DistrT, Args...>*>(mock.mock_fn)
->gmock_Call(std::forward<Ms>(matchers)...);
} }
mutable std::vector<std::function<void()>> deleters_; // MockingBitGen::MatchImpl<> is a dispatch function which converts the
// generic type-erased parameters into a specific mock invocation call.
using match_impl_fn = void (*)(void* mock_fn, void* t_erased_dist_args, // Requires tuple_args to point to a ArgTupleT, which is a std::tuple<Args...>
void* t_erased_result); // used to invoke the mock function.
struct MockData { // Requires result to point to a ResultT, which is the result of the call.
void* mock_fn = nullptr; template <typename ResultT, typename ArgTupleT>
match_impl_fn match_impl = nullptr; static void MatchImpl(/*MockFnType<ResultT, Args...>*/ void* mock_fn,
}; /*ArgTupleT*/ void* args_tuple,
/*ResultT*/ void* result) {
mutable absl::flat_hash_map<std::type_index, MockData> mocks_; using MockFnType = decltype(
GetMockFnType(std::declval<ResultT>(), std::declval<ArgTupleT>()));
template <typename DistrT, typename... Args> *static_cast<ResultT*>(result) = absl::apply(
static void MatchImpl(void* mock_fn, void* dist_args, void* result) { MockFnCaller<ResultT, MockFnType, ArgTupleT>{
using result_type = typename DistrT::result_type; static_cast<MockFnType*>(mock_fn)},
*static_cast<result_type*>(result) = absl::apply( *static_cast<ArgTupleT*>(args_tuple));
[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));
} }
// Looks for an appropriate mock - Returns the mocked result if one is found. // MockingBitGen::InvokeMock
// Otherwise, returns a random value generated by the underlying URBG. //
bool CallImpl(const std::type_info& key_type, void* dist_args, // InvokeMock(FastTypeIdType, args, result) is the entrypoint for invoking
void* result) override { // mocks registered on MockingBitGen.
//
// When no mocks are registered on the provided FastTypeIdType, returns false.
// Otherwise attempts to invoke the mock function ResultT(Args...) that
// was previously registered via the type_index.
// Requires tuple_args to point to a ArgTupleT, which is a std::tuple<Args...>
// used to invoke the mock function.
// Requires result to point to a ResultT, which is the result of the call.
inline bool InvokeMock(base_internal::FastTypeIdType type, void* args_tuple,
void* result) {
// Trigger a mock, if there exists one that matches `param`. // Trigger a mock, if there exists one that matches `param`.
auto it = mocks_.find(std::type_index(key_type)); auto it = mocks_.find(type);
if (it == mocks_.end()) return false; if (it == mocks_.end()) return false;
auto* mock_data = static_cast<MockData*>(&it->second); auto* mock_data = static_cast<MockData*>(&it->second);
mock_data->match_impl(mock_data->mock_fn, dist_args, result); mock_data->match_impl(mock_data->mock_fn, args_tuple, result);
return true; return true;
} }
template <typename, typename> absl::flat_hash_map<base_internal::FastTypeIdType, MockData> mocks_;
friend struct ::absl::random_internal::MockSingleOverload; std::vector<std::function<void()>> deleters_;
friend struct ::absl::random_internal::DistributionCaller< absl::BitGen gen_;
absl::MockingBitGen>;
};
// ----------------------------------------------------------------------------- template <typename, typename>
// Implementation Details Only Below friend struct ::absl::random_internal::MockSingleOverload; // for Register
// ----------------------------------------------------------------------------- template <typename>
friend struct ::absl::random_internal::DistributionCaller; // for InvokeMock
namespace random_internal { friend class ::absl::BitGenRef; // for InvokeMock
template <>
struct DistributionCaller<absl::MockingBitGen> {
template <typename DistrT, typename... Args>
static typename DistrT::result_type Call(absl::MockingBitGen* gen,
Args&&... args) {
return gen->template Call<DistrT>(std::forward<Args>(args)...);
}
}; };
} // namespace random_internal
ABSL_NAMESPACE_END ABSL_NAMESPACE_END
} // namespace absl } // namespace absl
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment