Export of internal Abseil changes.
-- ed3a3431eee9e48e6553b0320e0308d2dde6725c by Derek Mauro <dmauro@google.com>: Project import generated by Copybara. PiperOrigin-RevId: 258631680 GitOrigin-RevId: ed3a3431eee9e48e6553b0320e0308d2dde6725c Change-Id: I1d7ae86a79783842092d29504605ba039c369603
This commit is contained in:
parent
44efe96dfc
commit
c6c3c1b498
32 changed files with 1168 additions and 657 deletions
|
@ -62,7 +62,6 @@ static SpinLock static_cooperative_spinlock(
|
||||||
static SpinLock static_noncooperative_spinlock(
|
static SpinLock static_noncooperative_spinlock(
|
||||||
base_internal::kLinkerInitialized, base_internal::SCHEDULE_KERNEL_ONLY);
|
base_internal::kLinkerInitialized, base_internal::SCHEDULE_KERNEL_ONLY);
|
||||||
|
|
||||||
|
|
||||||
// Simple integer hash function based on the public domain lookup2 hash.
|
// Simple integer hash function based on the public domain lookup2 hash.
|
||||||
// http://burtleburtle.net/bob/c/lookup2.c
|
// http://burtleburtle.net/bob/c/lookup2.c
|
||||||
static uint32_t Hash32(uint32_t a, uint32_t c) {
|
static uint32_t Hash32(uint32_t a, uint32_t c) {
|
||||||
|
@ -194,6 +193,7 @@ TEST(SpinLock, WaitCyclesEncoding) {
|
||||||
TEST(SpinLockWithThreads, StaticSpinLock) {
|
TEST(SpinLockWithThreads, StaticSpinLock) {
|
||||||
ThreadedTest(&static_spinlock);
|
ThreadedTest(&static_spinlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(SpinLockWithThreads, StackSpinLock) {
|
TEST(SpinLockWithThreads, StackSpinLock) {
|
||||||
SpinLock spinlock;
|
SpinLock spinlock;
|
||||||
ThreadedTest(&spinlock);
|
ThreadedTest(&spinlock);
|
||||||
|
|
|
@ -44,7 +44,10 @@ cc_test(
|
||||||
linkopts = ABSL_DEFAULT_LINKOPTS,
|
linkopts = ABSL_DEFAULT_LINKOPTS,
|
||||||
deps = [
|
deps = [
|
||||||
":compressed_tuple",
|
":compressed_tuple",
|
||||||
|
":test_instance_tracker",
|
||||||
"//absl/memory",
|
"//absl/memory",
|
||||||
|
"//absl/types:any",
|
||||||
|
"//absl/types:optional",
|
||||||
"//absl/utility",
|
"//absl/utility",
|
||||||
"@com_google_googletest//:gtest_main",
|
"@com_google_googletest//:gtest_main",
|
||||||
],
|
],
|
||||||
|
|
|
@ -43,8 +43,11 @@ absl_cc_test(
|
||||||
COPTS
|
COPTS
|
||||||
${ABSL_TEST_COPTS}
|
${ABSL_TEST_COPTS}
|
||||||
DEPS
|
DEPS
|
||||||
|
absl::any
|
||||||
absl::compressed_tuple
|
absl::compressed_tuple
|
||||||
absl::memory
|
absl::memory
|
||||||
|
absl::optional
|
||||||
|
absl::test_instance_tracker
|
||||||
absl::utility
|
absl::utility
|
||||||
gmock_main
|
gmock_main
|
||||||
)
|
)
|
||||||
|
|
|
@ -77,10 +77,18 @@ class node_handle_base {
|
||||||
protected:
|
protected:
|
||||||
friend struct CommonAccess;
|
friend struct CommonAccess;
|
||||||
|
|
||||||
node_handle_base(const allocator_type& a, slot_type* s) : alloc_(a) {
|
struct transfer_tag_t {};
|
||||||
|
node_handle_base(transfer_tag_t, const allocator_type& a, slot_type* s)
|
||||||
|
: alloc_(a) {
|
||||||
PolicyTraits::transfer(alloc(), slot(), s);
|
PolicyTraits::transfer(alloc(), slot(), s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct move_tag_t {};
|
||||||
|
node_handle_base(move_tag_t, const allocator_type& a, slot_type* s)
|
||||||
|
: alloc_(a) {
|
||||||
|
PolicyTraits::construct(alloc(), slot(), s);
|
||||||
|
}
|
||||||
|
|
||||||
void destroy() {
|
void destroy() {
|
||||||
if (!empty()) {
|
if (!empty()) {
|
||||||
PolicyTraits::destroy(alloc(), slot());
|
PolicyTraits::destroy(alloc(), slot());
|
||||||
|
@ -121,7 +129,7 @@ class node_handle : public node_handle_base<PolicyTraits, Alloc> {
|
||||||
private:
|
private:
|
||||||
friend struct CommonAccess;
|
friend struct CommonAccess;
|
||||||
|
|
||||||
node_handle(const Alloc& a, typename Base::slot_type* s) : Base(a, s) {}
|
using Base::Base;
|
||||||
};
|
};
|
||||||
|
|
||||||
// For maps.
|
// For maps.
|
||||||
|
@ -148,7 +156,7 @@ class node_handle<Policy, PolicyTraits, Alloc,
|
||||||
private:
|
private:
|
||||||
friend struct CommonAccess;
|
friend struct CommonAccess;
|
||||||
|
|
||||||
node_handle(const Alloc& a, typename Base::slot_type* s) : Base(a, s) {}
|
using Base::Base;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Provide access to non-public node-handle functions.
|
// Provide access to non-public node-handle functions.
|
||||||
|
@ -164,8 +172,13 @@ struct CommonAccess {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename... Args>
|
template <typename T, typename... Args>
|
||||||
static T Make(Args&&... args) {
|
static T Transfer(Args&&... args) {
|
||||||
return T(std::forward<Args>(args)...);
|
return T(typename T::transfer_tag_t{}, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
static T Move(Args&&... args) {
|
||||||
|
return T(typename T::move_tag_t{}, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,9 @@ template <typename T, size_t I,
|
||||||
struct Storage {
|
struct Storage {
|
||||||
T value;
|
T value;
|
||||||
constexpr Storage() = default;
|
constexpr Storage() = default;
|
||||||
explicit constexpr Storage(T&& v) : value(absl::forward<T>(v)) {}
|
template <typename V>
|
||||||
|
explicit constexpr Storage(absl::in_place_t, V&& v)
|
||||||
|
: value(absl::forward<V>(v)) {}
|
||||||
constexpr const T& get() const& { return value; }
|
constexpr const T& get() const& { return value; }
|
||||||
T& get() & { return value; }
|
T& get() & { return value; }
|
||||||
constexpr const T&& get() const&& { return absl::move(*this).value; }
|
constexpr const T&& get() const&& { return absl::move(*this).value; }
|
||||||
|
@ -112,7 +114,11 @@ struct Storage {
|
||||||
template <typename T, size_t I>
|
template <typename T, size_t I>
|
||||||
struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC Storage<T, I, true> : T {
|
struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC Storage<T, I, true> : T {
|
||||||
constexpr Storage() = default;
|
constexpr Storage() = default;
|
||||||
explicit constexpr Storage(T&& v) : T(absl::forward<T>(v)) {}
|
|
||||||
|
template <typename V>
|
||||||
|
explicit constexpr Storage(absl::in_place_t, V&& v)
|
||||||
|
: T(absl::forward<V>(v)) {}
|
||||||
|
|
||||||
constexpr const T& get() const& { return *this; }
|
constexpr const T& get() const& { return *this; }
|
||||||
T& get() & { return *this; }
|
T& get() & { return *this; }
|
||||||
constexpr const T&& get() const&& { return absl::move(*this); }
|
constexpr const T&& get() const&& { return absl::move(*this); }
|
||||||
|
@ -132,8 +138,9 @@ struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl<
|
||||||
: uses_inheritance,
|
: uses_inheritance,
|
||||||
Storage<Ts, std::integral_constant<size_t, I>::value>... {
|
Storage<Ts, std::integral_constant<size_t, I>::value>... {
|
||||||
constexpr CompressedTupleImpl() = default;
|
constexpr CompressedTupleImpl() = default;
|
||||||
explicit constexpr CompressedTupleImpl(Ts&&... args)
|
template <typename... Vs>
|
||||||
: Storage<Ts, I>(absl::forward<Ts>(args))... {}
|
explicit constexpr CompressedTupleImpl(absl::in_place_t, Vs&&... args)
|
||||||
|
: Storage<Ts, I>(absl::in_place, absl::forward<Vs>(args))... {}
|
||||||
friend CompressedTuple<Ts...>;
|
friend CompressedTuple<Ts...>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -143,8 +150,9 @@ struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl<
|
||||||
// We use the dummy identity function as above...
|
// We use the dummy identity function as above...
|
||||||
: Storage<Ts, std::integral_constant<size_t, I>::value, false>... {
|
: Storage<Ts, std::integral_constant<size_t, I>::value, false>... {
|
||||||
constexpr CompressedTupleImpl() = default;
|
constexpr CompressedTupleImpl() = default;
|
||||||
explicit constexpr CompressedTupleImpl(Ts&&... args)
|
template <typename... Vs>
|
||||||
: Storage<Ts, I, false>(absl::forward<Ts>(args))... {}
|
explicit constexpr CompressedTupleImpl(absl::in_place_t, Vs&&... args)
|
||||||
|
: Storage<Ts, I, false>(absl::in_place, absl::forward<Vs>(args))... {}
|
||||||
friend CompressedTuple<Ts...>;
|
friend CompressedTuple<Ts...>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -159,6 +167,11 @@ constexpr bool ShouldAnyUseBase() {
|
||||||
Or({std::integral_constant<bool, ShouldUseBase<Ts>()>()...})){};
|
Or({std::integral_constant<bool, ShouldUseBase<Ts>()>()...})){};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T, typename V>
|
||||||
|
using TupleMoveConstructible = typename std::conditional<
|
||||||
|
std::is_reference<T>::value, std::is_convertible<V, T>,
|
||||||
|
std::is_constructible<T, V&&>>::type;
|
||||||
|
|
||||||
} // namespace internal_compressed_tuple
|
} // namespace internal_compressed_tuple
|
||||||
|
|
||||||
// Helper class to perform the Empty Base Class Optimization.
|
// Helper class to perform the Empty Base Class Optimization.
|
||||||
|
@ -192,9 +205,29 @@ class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple
|
||||||
using StorageT = internal_compressed_tuple::Storage<ElemT<I>, I>;
|
using StorageT = internal_compressed_tuple::Storage<ElemT<I>, I>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// There seems to be a bug in MSVC dealing in which using '=default' here will
|
||||||
|
// cause the compiler to ignore the body of other constructors. The work-
|
||||||
|
// around is to explicitly implement the default constructor.
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
constexpr CompressedTuple() : CompressedTuple::CompressedTupleImpl() {}
|
||||||
|
#else
|
||||||
constexpr CompressedTuple() = default;
|
constexpr CompressedTuple() = default;
|
||||||
explicit constexpr CompressedTuple(Ts... base)
|
#endif
|
||||||
: CompressedTuple::CompressedTupleImpl(absl::forward<Ts>(base)...) {}
|
explicit constexpr CompressedTuple(const Ts&... base)
|
||||||
|
: CompressedTuple::CompressedTupleImpl(absl::in_place, base...) {}
|
||||||
|
|
||||||
|
template <typename... Vs,
|
||||||
|
absl::enable_if_t<
|
||||||
|
absl::conjunction<
|
||||||
|
// Ensure we are not hiding default copy/move constructors.
|
||||||
|
absl::negation<std::is_same<void(CompressedTuple),
|
||||||
|
void(absl::decay_t<Vs>...)>>,
|
||||||
|
internal_compressed_tuple::TupleMoveConstructible<
|
||||||
|
Ts, Vs&&>...>::value,
|
||||||
|
bool> = true>
|
||||||
|
explicit constexpr CompressedTuple(Vs&&... base)
|
||||||
|
: CompressedTuple::CompressedTupleImpl(absl::in_place,
|
||||||
|
absl::forward<Vs>(base)...) {}
|
||||||
|
|
||||||
template <int I>
|
template <int I>
|
||||||
ElemT<I>& get() & {
|
ElemT<I>& get() & {
|
||||||
|
|
|
@ -19,7 +19,10 @@
|
||||||
|
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
#include "absl/container/internal/test_instance_tracker.h"
|
||||||
#include "absl/memory/memory.h"
|
#include "absl/memory/memory.h"
|
||||||
|
#include "absl/types/any.h"
|
||||||
|
#include "absl/types/optional.h"
|
||||||
#include "absl/utility/utility.h"
|
#include "absl/utility/utility.h"
|
||||||
|
|
||||||
// These are declared at global scope purely so that error messages
|
// These are declared at global scope purely so that error messages
|
||||||
|
@ -43,10 +46,14 @@ struct TwoValues {
|
||||||
U value2;
|
U value2;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
namespace absl {
|
namespace absl {
|
||||||
namespace container_internal {
|
namespace container_internal {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using absl::test_internal::CopyableMovableInstance;
|
||||||
|
using absl::test_internal::InstanceTracker;
|
||||||
|
|
||||||
TEST(CompressedTupleTest, Sizeof) {
|
TEST(CompressedTupleTest, Sizeof) {
|
||||||
EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int>));
|
EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int>));
|
||||||
EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int, Empty<0>>));
|
EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int, Empty<0>>));
|
||||||
|
@ -62,6 +69,141 @@ TEST(CompressedTupleTest, Sizeof) {
|
||||||
sizeof(CompressedTuple<int, Empty<0>, NotEmpty<double>, Empty<1>>));
|
sizeof(CompressedTuple<int, Empty<0>, NotEmpty<double>, Empty<1>>));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(CompressedTupleTest, OneMoveOnRValueConstructionTemp) {
|
||||||
|
InstanceTracker tracker;
|
||||||
|
CompressedTuple<CopyableMovableInstance> x1(CopyableMovableInstance(1));
|
||||||
|
EXPECT_EQ(tracker.instances(), 1);
|
||||||
|
EXPECT_EQ(tracker.copies(), 0);
|
||||||
|
EXPECT_LE(tracker.moves(), 1);
|
||||||
|
EXPECT_EQ(x1.get<0>().value(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CompressedTupleTest, OneMoveOnRValueConstructionMove) {
|
||||||
|
InstanceTracker tracker;
|
||||||
|
|
||||||
|
CopyableMovableInstance i1(1);
|
||||||
|
CompressedTuple<CopyableMovableInstance> x1(std::move(i1));
|
||||||
|
EXPECT_EQ(tracker.instances(), 2);
|
||||||
|
EXPECT_EQ(tracker.copies(), 0);
|
||||||
|
EXPECT_LE(tracker.moves(), 1);
|
||||||
|
EXPECT_EQ(x1.get<0>().value(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CompressedTupleTest, OneMoveOnRValueConstructionMixedTypes) {
|
||||||
|
InstanceTracker tracker;
|
||||||
|
CopyableMovableInstance i1(1);
|
||||||
|
CopyableMovableInstance i2(2);
|
||||||
|
Empty<0> empty;
|
||||||
|
CompressedTuple<CopyableMovableInstance, CopyableMovableInstance&, Empty<0>>
|
||||||
|
x1(std::move(i1), i2, empty);
|
||||||
|
EXPECT_EQ(x1.get<0>().value(), 1);
|
||||||
|
EXPECT_EQ(x1.get<1>().value(), 2);
|
||||||
|
EXPECT_EQ(tracker.copies(), 0);
|
||||||
|
EXPECT_EQ(tracker.moves(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IncompleteType;
|
||||||
|
CompressedTuple<CopyableMovableInstance, IncompleteType&, Empty<0>>
|
||||||
|
MakeWithIncomplete(CopyableMovableInstance i1,
|
||||||
|
IncompleteType& t, // NOLINT
|
||||||
|
Empty<0> empty) {
|
||||||
|
return CompressedTuple<CopyableMovableInstance, IncompleteType&, Empty<0>>{
|
||||||
|
std::move(i1), t, empty};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IncompleteType {};
|
||||||
|
TEST(CompressedTupleTest, OneMoveOnRValueConstructionWithIncompleteType) {
|
||||||
|
InstanceTracker tracker;
|
||||||
|
CopyableMovableInstance i1(1);
|
||||||
|
Empty<0> empty;
|
||||||
|
struct DerivedType : IncompleteType {int value = 0;};
|
||||||
|
DerivedType fd;
|
||||||
|
fd.value = 7;
|
||||||
|
|
||||||
|
CompressedTuple<CopyableMovableInstance, IncompleteType&, Empty<0>> x1 =
|
||||||
|
MakeWithIncomplete(std::move(i1), fd, empty);
|
||||||
|
|
||||||
|
EXPECT_EQ(x1.get<0>().value(), 1);
|
||||||
|
EXPECT_EQ(static_cast<DerivedType&>(x1.get<1>()).value, 7);
|
||||||
|
|
||||||
|
EXPECT_EQ(tracker.copies(), 0);
|
||||||
|
EXPECT_EQ(tracker.moves(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CompressedTupleTest,
|
||||||
|
OneMoveOnRValueConstructionMixedTypes_BraceInitPoisonPillExpected) {
|
||||||
|
InstanceTracker tracker;
|
||||||
|
CopyableMovableInstance i1(1);
|
||||||
|
CopyableMovableInstance i2(2);
|
||||||
|
CompressedTuple<CopyableMovableInstance, CopyableMovableInstance&, Empty<0>>
|
||||||
|
x1(std::move(i1), i2, {}); // NOLINT
|
||||||
|
EXPECT_EQ(x1.get<0>().value(), 1);
|
||||||
|
EXPECT_EQ(x1.get<1>().value(), 2);
|
||||||
|
EXPECT_EQ(tracker.instances(), 3);
|
||||||
|
// We are forced into the `const Ts&...` constructor (invoking copies)
|
||||||
|
// because we need it to deduce the type of `{}`.
|
||||||
|
// std::tuple also has this behavior.
|
||||||
|
// Note, this test is proof that this is expected behavior, but it is not
|
||||||
|
// _desired_ behavior.
|
||||||
|
EXPECT_EQ(tracker.copies(), 1);
|
||||||
|
EXPECT_EQ(tracker.moves(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CompressedTupleTest, OneCopyOnLValueConstruction) {
|
||||||
|
InstanceTracker tracker;
|
||||||
|
CopyableMovableInstance i1(1);
|
||||||
|
|
||||||
|
CompressedTuple<CopyableMovableInstance> x1(i1);
|
||||||
|
EXPECT_EQ(tracker.copies(), 1);
|
||||||
|
EXPECT_EQ(tracker.moves(), 0);
|
||||||
|
|
||||||
|
tracker.ResetCopiesMovesSwaps();
|
||||||
|
|
||||||
|
CopyableMovableInstance i2(2);
|
||||||
|
const CopyableMovableInstance& i2_ref = i2;
|
||||||
|
CompressedTuple<CopyableMovableInstance> x2(i2_ref);
|
||||||
|
EXPECT_EQ(tracker.copies(), 1);
|
||||||
|
EXPECT_EQ(tracker.moves(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CompressedTupleTest, OneMoveOnRValueAccess) {
|
||||||
|
InstanceTracker tracker;
|
||||||
|
CopyableMovableInstance i1(1);
|
||||||
|
CompressedTuple<CopyableMovableInstance> x(std::move(i1));
|
||||||
|
tracker.ResetCopiesMovesSwaps();
|
||||||
|
|
||||||
|
CopyableMovableInstance i2 = std::move(x).get<0>();
|
||||||
|
EXPECT_EQ(tracker.copies(), 0);
|
||||||
|
EXPECT_EQ(tracker.moves(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CompressedTupleTest, OneCopyOnLValueAccess) {
|
||||||
|
InstanceTracker tracker;
|
||||||
|
|
||||||
|
CompressedTuple<CopyableMovableInstance> x(CopyableMovableInstance(0));
|
||||||
|
EXPECT_EQ(tracker.copies(), 0);
|
||||||
|
EXPECT_EQ(tracker.moves(), 1);
|
||||||
|
|
||||||
|
CopyableMovableInstance t = x.get<0>();
|
||||||
|
EXPECT_EQ(tracker.copies(), 1);
|
||||||
|
EXPECT_EQ(tracker.moves(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CompressedTupleTest, ZeroCopyOnRefAccess) {
|
||||||
|
InstanceTracker tracker;
|
||||||
|
|
||||||
|
CompressedTuple<CopyableMovableInstance> x(CopyableMovableInstance(0));
|
||||||
|
EXPECT_EQ(tracker.copies(), 0);
|
||||||
|
EXPECT_EQ(tracker.moves(), 1);
|
||||||
|
|
||||||
|
CopyableMovableInstance& t1 = x.get<0>();
|
||||||
|
const CopyableMovableInstance& t2 = x.get<0>();
|
||||||
|
EXPECT_EQ(tracker.copies(), 0);
|
||||||
|
EXPECT_EQ(tracker.moves(), 1);
|
||||||
|
EXPECT_EQ(t1.value(), 0);
|
||||||
|
EXPECT_EQ(t2.value(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(CompressedTupleTest, Access) {
|
TEST(CompressedTupleTest, Access) {
|
||||||
struct S {
|
struct S {
|
||||||
std::string x;
|
std::string x;
|
||||||
|
@ -173,7 +315,40 @@ TEST(CompressedTupleTest, MoveOnlyElements) {
|
||||||
EXPECT_EQ(*x1, 5);
|
EXPECT_EQ(*x1, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(CompressedTupleTest, MoveConstructionMoveOnlyElements) {
|
||||||
|
CompressedTuple<std::unique_ptr<std::string>> base(
|
||||||
|
absl::make_unique<std::string>("str"));
|
||||||
|
EXPECT_EQ(*base.get<0>(), "str");
|
||||||
|
|
||||||
|
CompressedTuple<std::unique_ptr<std::string>> copy(std::move(base));
|
||||||
|
EXPECT_EQ(*copy.get<0>(), "str");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CompressedTupleTest, AnyElements) {
|
||||||
|
any a(std::string("str"));
|
||||||
|
CompressedTuple<any, any&> x(any(5), a);
|
||||||
|
EXPECT_EQ(absl::any_cast<int>(x.get<0>()), 5);
|
||||||
|
EXPECT_EQ(absl::any_cast<std::string>(x.get<1>()), "str");
|
||||||
|
|
||||||
|
a = 0.5f;
|
||||||
|
EXPECT_EQ(absl::any_cast<float>(x.get<1>()), 0.5);
|
||||||
|
|
||||||
|
// Ensure copy construction work in the face of a type with a universal
|
||||||
|
// implicit constructor;
|
||||||
|
CompressedTuple<absl::any> c{}, d(c); // NOLINT
|
||||||
|
}
|
||||||
|
|
||||||
TEST(CompressedTupleTest, Constexpr) {
|
TEST(CompressedTupleTest, Constexpr) {
|
||||||
|
struct NonTrivialStruct {
|
||||||
|
constexpr NonTrivialStruct() = default;
|
||||||
|
constexpr int value() const { return v; }
|
||||||
|
int v = 5;
|
||||||
|
};
|
||||||
|
struct TrivialStruct {
|
||||||
|
TrivialStruct() = default;
|
||||||
|
constexpr int value() const { return v; }
|
||||||
|
int v;
|
||||||
|
};
|
||||||
constexpr CompressedTuple<int, double, CompressedTuple<int>, Empty<0>> x(
|
constexpr CompressedTuple<int, double, CompressedTuple<int>, Empty<0>> x(
|
||||||
7, 1.25, CompressedTuple<int>(5), {});
|
7, 1.25, CompressedTuple<int>(5), {});
|
||||||
constexpr int x0 = x.get<0>();
|
constexpr int x0 = x.get<0>();
|
||||||
|
@ -186,6 +361,32 @@ TEST(CompressedTupleTest, Constexpr) {
|
||||||
EXPECT_EQ(x2, 5);
|
EXPECT_EQ(x2, 5);
|
||||||
EXPECT_EQ(x3, CallType::kConstRef);
|
EXPECT_EQ(x3, CallType::kConstRef);
|
||||||
|
|
||||||
|
#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4
|
||||||
|
constexpr CompressedTuple<Empty<0>, TrivialStruct, int> trivial = {};
|
||||||
|
constexpr CallType trivial0 = trivial.get<0>().value();
|
||||||
|
constexpr int trivial1 = trivial.get<1>().value();
|
||||||
|
constexpr int trivial2 = trivial.get<2>();
|
||||||
|
|
||||||
|
EXPECT_EQ(trivial0, CallType::kConstRef);
|
||||||
|
EXPECT_EQ(trivial1, 0);
|
||||||
|
EXPECT_EQ(trivial2, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
constexpr CompressedTuple<Empty<0>, NonTrivialStruct, absl::optional<int>>
|
||||||
|
non_trivial = {};
|
||||||
|
constexpr CallType non_trivial0 = non_trivial.get<0>().value();
|
||||||
|
constexpr int non_trivial1 = non_trivial.get<1>().value();
|
||||||
|
constexpr absl::optional<int> non_trivial2 = non_trivial.get<2>();
|
||||||
|
|
||||||
|
EXPECT_EQ(non_trivial0, CallType::kConstRef);
|
||||||
|
EXPECT_EQ(non_trivial1, 5);
|
||||||
|
EXPECT_EQ(non_trivial2, absl::nullopt);
|
||||||
|
|
||||||
|
static constexpr char data[] = "DEF";
|
||||||
|
constexpr CompressedTuple<const char*> z(data);
|
||||||
|
constexpr const char* z1 = z.get<0>();
|
||||||
|
EXPECT_EQ(std::string(z1), std::string(data));
|
||||||
|
|
||||||
#if defined(__clang__)
|
#if defined(__clang__)
|
||||||
// An apparent bug in earlier versions of gcc claims these are ambiguous.
|
// An apparent bug in earlier versions of gcc claims these are ambiguous.
|
||||||
constexpr int x2m = absl::move(x.get<2>()).get<0>();
|
constexpr int x2m = absl::move(x.get<2>()).get<0>();
|
||||||
|
|
|
@ -1182,7 +1182,7 @@ class raw_hash_set {
|
||||||
|
|
||||||
node_type extract(const_iterator position) {
|
node_type extract(const_iterator position) {
|
||||||
auto node =
|
auto node =
|
||||||
CommonAccess::Make<node_type>(alloc_ref(), position.inner_.slot_);
|
CommonAccess::Transfer<node_type>(alloc_ref(), position.inner_.slot_);
|
||||||
erase_meta_only(position);
|
erase_meta_only(position);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
namespace absl {
|
namespace absl {
|
||||||
namespace test_internal {
|
namespace test_internal {
|
||||||
|
|
||||||
// A type that counts number of occurences of the type, the live occurrences of
|
// A type that counts number of occurrences of the type, the live occurrences of
|
||||||
// the type, as well as the number of copies, moves, swaps, and comparisons that
|
// the type, as well as the number of copies, moves, swaps, and comparisons that
|
||||||
// have occurred on the type. This is used as a base class for the copyable,
|
// have occurred on the type. This is used as a base class for the copyable,
|
||||||
// copyable+movable, and movable types below that are used in actual tests. Use
|
// copyable+movable, and movable types below that are used in actual tests. Use
|
||||||
|
|
|
@ -155,14 +155,12 @@ cc_library(
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "usage",
|
name = "usage_internal",
|
||||||
srcs = [
|
srcs = [
|
||||||
"internal/usage.cc",
|
"internal/usage.cc",
|
||||||
"usage.cc",
|
|
||||||
],
|
],
|
||||||
hdrs = [
|
hdrs = [
|
||||||
"internal/usage.h",
|
"internal/usage.h",
|
||||||
"usage.h",
|
|
||||||
],
|
],
|
||||||
copts = ABSL_DEFAULT_COPTS,
|
copts = ABSL_DEFAULT_COPTS,
|
||||||
linkopts = ABSL_DEFAULT_LINKOPTS,
|
linkopts = ABSL_DEFAULT_LINKOPTS,
|
||||||
|
@ -179,6 +177,23 @@ cc_library(
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "usage",
|
||||||
|
srcs = [
|
||||||
|
"usage.cc",
|
||||||
|
],
|
||||||
|
hdrs = [
|
||||||
|
"usage.h",
|
||||||
|
],
|
||||||
|
copts = ABSL_DEFAULT_COPTS,
|
||||||
|
linkopts = ABSL_DEFAULT_LINKOPTS,
|
||||||
|
deps = [
|
||||||
|
":usage_internal",
|
||||||
|
"//absl/strings",
|
||||||
|
"//absl/synchronization",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "parse",
|
name = "parse",
|
||||||
srcs = ["parse.cc"],
|
srcs = ["parse.cc"],
|
||||||
|
@ -195,6 +210,7 @@ cc_library(
|
||||||
":internal",
|
":internal",
|
||||||
":registry",
|
":registry",
|
||||||
":usage",
|
":usage",
|
||||||
|
":usage_internal",
|
||||||
"//absl/strings",
|
"//absl/strings",
|
||||||
"//absl/synchronization",
|
"//absl/synchronization",
|
||||||
],
|
],
|
||||||
|
@ -360,6 +376,7 @@ cc_test(
|
||||||
":internal",
|
":internal",
|
||||||
":parse",
|
":parse",
|
||||||
":usage",
|
":usage",
|
||||||
|
":usage_internal",
|
||||||
"//absl/memory",
|
"//absl/memory",
|
||||||
"//absl/strings",
|
"//absl/strings",
|
||||||
"@com_google_googletest//:gtest",
|
"@com_google_googletest//:gtest",
|
||||||
|
|
|
@ -141,13 +141,11 @@ absl_cc_library(
|
||||||
# Internal-only target, do not depend on directly.
|
# Internal-only target, do not depend on directly.
|
||||||
absl_cc_library(
|
absl_cc_library(
|
||||||
NAME
|
NAME
|
||||||
flags_usage
|
flags_usage_internal
|
||||||
SRCS
|
SRCS
|
||||||
"internal/usage.cc"
|
"internal/usage.cc"
|
||||||
"usage.cc"
|
|
||||||
HDRS
|
HDRS
|
||||||
"internal/usage.h"
|
"internal/usage.h"
|
||||||
"usage.h"
|
|
||||||
COPTS
|
COPTS
|
||||||
${ABSL_DEFAULT_COPTS}
|
${ABSL_DEFAULT_COPTS}
|
||||||
LINKOPTS
|
LINKOPTS
|
||||||
|
@ -161,6 +159,23 @@ absl_cc_library(
|
||||||
absl::synchronization
|
absl::synchronization
|
||||||
)
|
)
|
||||||
|
|
||||||
|
absl_cc_library(
|
||||||
|
NAME
|
||||||
|
flags_usage
|
||||||
|
SRCS
|
||||||
|
"usage.cc"
|
||||||
|
HDRS
|
||||||
|
"usage.h"
|
||||||
|
COPTS
|
||||||
|
${ABSL_DEFAULT_COPTS}
|
||||||
|
LINKOPTS
|
||||||
|
${ABSL_DEFAULT_LINKOPTS}
|
||||||
|
DEPS
|
||||||
|
absl::flags_usage_internal
|
||||||
|
absl::strings
|
||||||
|
absl::synchronization
|
||||||
|
)
|
||||||
|
|
||||||
absl_cc_library(
|
absl_cc_library(
|
||||||
NAME
|
NAME
|
||||||
flags_parse
|
flags_parse
|
||||||
|
|
|
@ -25,23 +25,15 @@ namespace absl {
|
||||||
// validates the type.
|
// validates the type.
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
#define ABSL_FLAGS_ATOMIC_GET(T) \
|
#define ABSL_FLAGS_ATOMIC_GET(T) \
|
||||||
T GetFlag(const absl::Flag<T>& flag) { \
|
T GetFlag(const absl::Flag<T>& flag) { return flag.Get(); }
|
||||||
T result; \
|
|
||||||
flag.internal.Read(&result, &flags_internal::FlagOps<T>); \
|
|
||||||
return result; \
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
#define ABSL_FLAGS_ATOMIC_GET(T) \
|
#define ABSL_FLAGS_ATOMIC_GET(T) \
|
||||||
T GetFlag(const absl::Flag<T>& flag) { \
|
T GetFlag(const absl::Flag<T>& flag) { \
|
||||||
const int64_t r = flag.internal.atomic.load(std::memory_order_acquire); \
|
|
||||||
if (r != flags_internal::CommandLineFlag::kAtomicInit) { \
|
|
||||||
T t; \
|
|
||||||
memcpy(&t, &r, sizeof(T)); \
|
|
||||||
return t; \
|
|
||||||
} \
|
|
||||||
T result; \
|
T result; \
|
||||||
flag.internal.Read(&result, &flags_internal::FlagOps<T>); \
|
if (flag.AtomicGet(&result)) { \
|
||||||
return result; \
|
return result; \
|
||||||
|
} \
|
||||||
|
return flag.Get(); \
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -91,30 +91,7 @@ T GetFlag(const absl::Flag<T>& flag) {
|
||||||
ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_INTERNAL_LOCK_FREE_VALIDATE)
|
ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_INTERNAL_LOCK_FREE_VALIDATE)
|
||||||
#undef ABSL_FLAGS_INTERNAL_LOCK_FREE_VALIDATE
|
#undef ABSL_FLAGS_INTERNAL_LOCK_FREE_VALIDATE
|
||||||
|
|
||||||
// Implementation notes:
|
return flag.Get();
|
||||||
//
|
|
||||||
// We are wrapping a union around the value of `T` to serve three purposes:
|
|
||||||
//
|
|
||||||
// 1. `U.value` has correct size and alignment for a value of type `T`
|
|
||||||
// 2. The `U.value` constructor is not invoked since U's constructor does not
|
|
||||||
// do it explicitly.
|
|
||||||
// 3. The `U.value` destructor is invoked since U's destructor does it
|
|
||||||
// explicitly. This makes `U` a kind of RAII wrapper around non default
|
|
||||||
// constructible value of T, which is destructed when we leave the scope.
|
|
||||||
// We do need to destroy U.value, which is constructed by
|
|
||||||
// CommandLineFlag::Read even though we left it in a moved-from state
|
|
||||||
// after std::move.
|
|
||||||
//
|
|
||||||
// All of this serves to avoid requiring `T` being default constructible.
|
|
||||||
union U {
|
|
||||||
T value;
|
|
||||||
U() {}
|
|
||||||
~U() { value.~T(); }
|
|
||||||
};
|
|
||||||
U u;
|
|
||||||
|
|
||||||
flag.internal.Read(&u.value, &flags_internal::FlagOps<T>);
|
|
||||||
return std::move(u.value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overload for `GetFlag()` for types that support lock-free reads.
|
// Overload for `GetFlag()` for types that support lock-free reads.
|
||||||
|
@ -132,7 +109,7 @@ ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_INTERNAL_LOCK_FREE_EXPORT)
|
||||||
// but especially within performance-critical code.
|
// but especially within performance-critical code.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void SetFlag(absl::Flag<T>* flag, const T& v) {
|
void SetFlag(absl::Flag<T>* flag, const T& v) {
|
||||||
flag->internal.Write(&v, &flags_internal::FlagOps<T>);
|
flag->Set(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overload of `SetFlag()` to allow callers to pass in a value that is
|
// Overload of `SetFlag()` to allow callers to pass in a value that is
|
||||||
|
@ -141,7 +118,7 @@ void SetFlag(absl::Flag<T>* flag, const T& v) {
|
||||||
template <typename T, typename V>
|
template <typename T, typename V>
|
||||||
void SetFlag(absl::Flag<T>* flag, const V& v) {
|
void SetFlag(absl::Flag<T>* flag, const V& v) {
|
||||||
T value(v);
|
T value(v);
|
||||||
SetFlag<T>(flag, value);
|
flag->Set(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace absl
|
} // namespace absl
|
||||||
|
|
|
@ -37,44 +37,6 @@ const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void StoreAtomic(CommandLineFlag* flag, const void* data, size_t size) {
|
|
||||||
int64_t t = 0;
|
|
||||||
assert(size <= sizeof(int64_t));
|
|
||||||
memcpy(&t, data, size);
|
|
||||||
flag->atomic.store(t, std::memory_order_release);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the flag has a mutation callback this function invokes it. While the
|
|
||||||
// callback is being invoked the primary flag's mutex is unlocked and it is
|
|
||||||
// re-locked back after call to callback is completed. Callback invocation is
|
|
||||||
// guarded by flag's secondary mutex instead which prevents concurrent callback
|
|
||||||
// invocation. Note that it is possible for other thread to grab the primary
|
|
||||||
// lock and update flag's value at any time during the callback invocation.
|
|
||||||
// This is by design. Callback can get a value of the flag if necessary, but it
|
|
||||||
// might be different from the value initiated the callback and it also can be
|
|
||||||
// different by the time the callback invocation is completed.
|
|
||||||
// Requires that *primary_lock be held in exclusive mode; it may be released
|
|
||||||
// and reacquired by the implementation.
|
|
||||||
void InvokeCallback(CommandLineFlag* flag, absl::Mutex* primary_lock)
|
|
||||||
EXCLUSIVE_LOCKS_REQUIRED(primary_lock) {
|
|
||||||
if (!flag->callback) return;
|
|
||||||
|
|
||||||
// The callback lock is guaranteed initialized, because *primary_lock exists.
|
|
||||||
absl::Mutex* callback_mu = &flag->locks->callback_mu;
|
|
||||||
|
|
||||||
// When executing the callback we need the primary flag's mutex to be unlocked
|
|
||||||
// so that callback can retrieve the flag's value.
|
|
||||||
primary_lock->Unlock();
|
|
||||||
|
|
||||||
{
|
|
||||||
absl::MutexLock lock(callback_mu);
|
|
||||||
|
|
||||||
flag->callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
primary_lock->Lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Currently we only validate flag values for user-defined flag types.
|
// Currently we only validate flag values for user-defined flag types.
|
||||||
bool ShouldValidateFlagValue(const CommandLineFlag& flag) {
|
bool ShouldValidateFlagValue(const CommandLineFlag& flag) {
|
||||||
#define DONT_VALIDATE(T) \
|
#define DONT_VALIDATE(T) \
|
||||||
|
@ -89,145 +51,72 @@ bool ShouldValidateFlagValue(const CommandLineFlag& flag) {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// Update any copy of the flag value that is stored in an atomic word.
|
absl::Mutex* InitFlag(CommandLineFlag* flag) {
|
||||||
// In addition if flag has a mutation callback this function invokes it.
|
ABSL_CONST_INIT static absl::Mutex init_lock(absl::kConstInit);
|
||||||
void UpdateCopy(CommandLineFlag* flag, absl::Mutex* primary_lock)
|
absl::Mutex* mu;
|
||||||
EXCLUSIVE_LOCKS_REQUIRED(primary_lock) {
|
|
||||||
#define STORE_ATOMIC(T) \
|
{
|
||||||
else if (flag->IsOfType<T>()) { \
|
absl::MutexLock lock(&init_lock);
|
||||||
StoreAtomic(flag, flag->cur, sizeof(T)); \
|
|
||||||
|
if (flag->locks == nullptr) { // Must initialize Mutexes for this flag.
|
||||||
|
flag->locks = new flags_internal::CommandLineFlagLocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (false) {
|
mu = &flag->locks->primary_mu;
|
||||||
}
|
}
|
||||||
ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(STORE_ATOMIC)
|
|
||||||
#undef STORE_ATOMIC
|
|
||||||
|
|
||||||
InvokeCallback(flag, primary_lock);
|
{
|
||||||
|
absl::MutexLock lock(mu);
|
||||||
|
|
||||||
|
if (!flag->retired && flag->def == nullptr) {
|
||||||
|
// Need to initialize def and cur fields.
|
||||||
|
flag->def = (*flag->make_init_value)();
|
||||||
|
flag->cur = Clone(flag->op, flag->def);
|
||||||
|
UpdateCopy(flag);
|
||||||
|
flag->inited.store(true, std::memory_order_release);
|
||||||
|
flag->InvokeCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flag->inited.store(true, std::memory_order_release);
|
||||||
|
return mu;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that the lazily initialized fields of *flag have been initialized,
|
// Ensure that the lazily initialized fields of *flag have been initialized,
|
||||||
// and return &flag->locks->primary_mu.
|
// and return &flag->locks->primary_mu.
|
||||||
absl::Mutex* InitFlagIfNecessary(CommandLineFlag* flag)
|
absl::Mutex* CommandLineFlag::InitFlagIfNecessary() const
|
||||||
LOCK_RETURNED(flag->locks->primary_mu) {
|
LOCK_RETURNED(locks->primary_mu) {
|
||||||
absl::Mutex* mu;
|
if (!this->inited.load(std::memory_order_acquire)) {
|
||||||
if (!flag->inited.load(std::memory_order_acquire)) {
|
return InitFlag(const_cast<CommandLineFlag*>(this));
|
||||||
// Need to initialize lazily initialized fields.
|
|
||||||
ABSL_CONST_INIT static absl::Mutex init_lock(absl::kConstInit);
|
|
||||||
init_lock.Lock();
|
|
||||||
if (flag->locks == nullptr) { // Must initialize Mutexes for this flag.
|
|
||||||
flag->locks = new flags_internal::CommandLineFlagLocks;
|
|
||||||
}
|
}
|
||||||
mu = &flag->locks->primary_mu;
|
|
||||||
init_lock.Unlock();
|
// All fields initialized; this->locks is therefore safe to read.
|
||||||
mu->Lock();
|
return &this->locks->primary_mu;
|
||||||
if (!flag->retired &&
|
|
||||||
flag->def == nullptr) { // Need to initialize def and cur fields.
|
|
||||||
flag->def = (*flag->make_init_value)();
|
|
||||||
flag->cur = Clone(flag->op, flag->def);
|
|
||||||
UpdateCopy(flag, mu);
|
|
||||||
}
|
|
||||||
mu->Unlock();
|
|
||||||
flag->inited.store(true, std::memory_order_release);
|
|
||||||
} else { // All fields initialized; flag->locks is therefore safe to read.
|
|
||||||
mu = &flag->locks->primary_mu;
|
|
||||||
}
|
|
||||||
return mu;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return true iff flag value was changed via direct-access.
|
void CommandLineFlag::Destroy() const {
|
||||||
bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b) {
|
// Values are heap allocated for retired and Abseil Flags.
|
||||||
if (!flag->IsAbseilFlag()) {
|
if (IsRetired() || IsAbseilFlag()) {
|
||||||
// Need to compare values for direct-access flags.
|
if (this->cur) Delete(this->op, this->cur);
|
||||||
#define CHANGED_FOR_TYPE(T) \
|
if (this->def) Delete(this->op, this->def);
|
||||||
if (flag->IsOfType<T>()) { \
|
|
||||||
return *reinterpret_cast<const T*>(a) != *reinterpret_cast<const T*>(b); \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CHANGED_FOR_TYPE(bool);
|
delete this->locks;
|
||||||
CHANGED_FOR_TYPE(int32_t);
|
|
||||||
CHANGED_FOR_TYPE(int64_t);
|
|
||||||
CHANGED_FOR_TYPE(uint64_t);
|
|
||||||
CHANGED_FOR_TYPE(double);
|
|
||||||
CHANGED_FOR_TYPE(std::string);
|
|
||||||
#undef CHANGED_FOR_TYPE
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Direct-access flags can be modified without going through the
|
bool CommandLineFlag::IsModified() const {
|
||||||
// flag API. Detect such changes and updated the modified bit.
|
absl::MutexLock l(InitFlagIfNecessary());
|
||||||
void UpdateModifiedBit(CommandLineFlag* flag) {
|
return modified;
|
||||||
if (!flag->IsAbseilFlag()) {
|
|
||||||
absl::MutexLock l(InitFlagIfNecessary(flag));
|
|
||||||
if (!flag->modified && ChangedDirectly(flag, flag->cur, flag->def)) {
|
|
||||||
flag->modified = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Validate(CommandLineFlag*, const void*) {
|
void CommandLineFlag::SetModified(bool is_modified) {
|
||||||
return true;
|
absl::MutexLock l(InitFlagIfNecessary());
|
||||||
|
modified = is_modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string HelpText::GetHelpText() const {
|
bool CommandLineFlag::IsSpecifiedOnCommandLine() const {
|
||||||
if (help_function_) return help_function_();
|
absl::MutexLock l(InitFlagIfNecessary());
|
||||||
if (help_message_) return help_message_;
|
return on_command_line;
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const int64_t CommandLineFlag::kAtomicInit;
|
|
||||||
|
|
||||||
void CommandLineFlag::Read(void* dst,
|
|
||||||
const flags_internal::FlagOpFn dst_op) const {
|
|
||||||
absl::ReaderMutexLock l(
|
|
||||||
InitFlagIfNecessary(const_cast<CommandLineFlag*>(this)));
|
|
||||||
|
|
||||||
// `dst_op` is the unmarshaling operation corresponding to the declaration
|
|
||||||
// visibile at the call site. `op` is the Flag's defined unmarshalling
|
|
||||||
// operation. They must match for this operation to be well-defined.
|
|
||||||
if (ABSL_PREDICT_FALSE(dst_op != op)) {
|
|
||||||
ABSL_INTERNAL_LOG(
|
|
||||||
ERROR,
|
|
||||||
absl::StrCat("Flag '", name,
|
|
||||||
"' is defined as one type and declared as another"));
|
|
||||||
}
|
|
||||||
CopyConstruct(op, cur, dst);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CommandLineFlag::Write(const void* src,
|
|
||||||
const flags_internal::FlagOpFn src_op) {
|
|
||||||
absl::Mutex* mu = InitFlagIfNecessary(this);
|
|
||||||
absl::MutexLock l(mu);
|
|
||||||
|
|
||||||
// `src_op` is the marshalling operation corresponding to the declaration
|
|
||||||
// visible at the call site. `op` is the Flag's defined marshalling operation.
|
|
||||||
// They must match for this operation to be well-defined.
|
|
||||||
if (ABSL_PREDICT_FALSE(src_op != op)) {
|
|
||||||
ABSL_INTERNAL_LOG(
|
|
||||||
ERROR,
|
|
||||||
absl::StrCat("Flag '", name,
|
|
||||||
"' is defined as one type and declared as another"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ShouldValidateFlagValue(*this)) {
|
|
||||||
void* obj = Clone(op, src);
|
|
||||||
std::string ignored_error;
|
|
||||||
std::string src_as_str = Unparse(marshalling_op, src);
|
|
||||||
if (!Parse(marshalling_op, src_as_str, obj, &ignored_error) ||
|
|
||||||
!Validate(this, obj)) {
|
|
||||||
ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '", name,
|
|
||||||
"' to invalid value ", src_as_str));
|
|
||||||
}
|
|
||||||
Delete(op, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
modified = true;
|
|
||||||
counter++;
|
|
||||||
Copy(op, src, cur);
|
|
||||||
|
|
||||||
UpdateCopy(this, mu);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::string_view CommandLineFlag::Typename() const {
|
absl::string_view CommandLineFlag::Typename() const {
|
||||||
|
@ -259,21 +148,96 @@ std::string CommandLineFlag::Filename() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CommandLineFlag::DefaultValue() const {
|
std::string CommandLineFlag::DefaultValue() const {
|
||||||
|
absl::MutexLock l(InitFlagIfNecessary());
|
||||||
|
|
||||||
return Unparse(this->marshalling_op, this->def);
|
return Unparse(this->marshalling_op, this->def);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CommandLineFlag::CurrentValue() const {
|
std::string CommandLineFlag::CurrentValue() const {
|
||||||
|
absl::MutexLock l(InitFlagIfNecessary());
|
||||||
|
|
||||||
return Unparse(this->marshalling_op, this->cur);
|
return Unparse(this->marshalling_op, this->cur);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CommandLineFlag::HasValidatorFn() const {
|
||||||
|
absl::MutexLock l(InitFlagIfNecessary());
|
||||||
|
|
||||||
|
return this->validator != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CommandLineFlag::SetValidatorFn(FlagValidator fn) {
|
||||||
|
absl::MutexLock l(InitFlagIfNecessary());
|
||||||
|
|
||||||
|
// ok to register the same function over and over again
|
||||||
|
if (fn == this->validator) return true;
|
||||||
|
|
||||||
|
// Can't set validator to a different function, unless reset first.
|
||||||
|
if (fn != nullptr && this->validator != nullptr) {
|
||||||
|
ABSL_INTERNAL_LOG(
|
||||||
|
WARNING, absl::StrCat("Ignoring SetValidatorFn() for flag '", Name(),
|
||||||
|
"': validate-fn already registered"));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->validator = fn;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CommandLineFlag::InvokeValidator(const void* value) const
|
||||||
|
EXCLUSIVE_LOCKS_REQUIRED(this->locks->primary_mu) {
|
||||||
|
if (!this->validator) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)value;
|
||||||
|
|
||||||
|
ABSL_INTERNAL_LOG(
|
||||||
|
FATAL,
|
||||||
|
absl::StrCat("Flag '", Name(),
|
||||||
|
"' of encapsulated type should not have a validator"));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void CommandLineFlag::SetCallback(
|
void CommandLineFlag::SetCallback(
|
||||||
const flags_internal::FlagCallback mutation_callback) {
|
const flags_internal::FlagCallback mutation_callback) {
|
||||||
absl::Mutex* mu = InitFlagIfNecessary(this);
|
absl::MutexLock l(InitFlagIfNecessary());
|
||||||
absl::MutexLock l(mu);
|
|
||||||
|
|
||||||
callback = mutation_callback;
|
callback = mutation_callback;
|
||||||
|
|
||||||
InvokeCallback(this, mu);
|
InvokeCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the flag has a mutation callback this function invokes it. While the
|
||||||
|
// callback is being invoked the primary flag's mutex is unlocked and it is
|
||||||
|
// re-locked back after call to callback is completed. Callback invocation is
|
||||||
|
// guarded by flag's secondary mutex instead which prevents concurrent callback
|
||||||
|
// invocation. Note that it is possible for other thread to grab the primary
|
||||||
|
// lock and update flag's value at any time during the callback invocation.
|
||||||
|
// This is by design. Callback can get a value of the flag if necessary, but it
|
||||||
|
// might be different from the value initiated the callback and it also can be
|
||||||
|
// different by the time the callback invocation is completed.
|
||||||
|
// Requires that *primary_lock be held in exclusive mode; it may be released
|
||||||
|
// and reacquired by the implementation.
|
||||||
|
void CommandLineFlag::InvokeCallback()
|
||||||
|
EXCLUSIVE_LOCKS_REQUIRED(this->locks->primary_mu) {
|
||||||
|
if (!this->callback) return;
|
||||||
|
|
||||||
|
// The callback lock is guaranteed initialized, because *locks->primary_mu
|
||||||
|
// exists.
|
||||||
|
absl::Mutex* callback_mu = &this->locks->callback_mu;
|
||||||
|
|
||||||
|
// When executing the callback we need the primary flag's mutex to be unlocked
|
||||||
|
// so that callback can retrieve the flag's value.
|
||||||
|
this->locks->primary_mu.Unlock();
|
||||||
|
|
||||||
|
{
|
||||||
|
absl::MutexLock lock(callback_mu);
|
||||||
|
this->callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->locks->primary_mu.Lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempts to parse supplied `value` string using parsing routine in the `flag`
|
// Attempts to parse supplied `value` string using parsing routine in the `flag`
|
||||||
|
@ -282,8 +246,9 @@ void CommandLineFlag::SetCallback(
|
||||||
// parsed value in 'dst' assuming it is a pointer to the flag's value type. In
|
// parsed value in 'dst' assuming it is a pointer to the flag's value type. In
|
||||||
// case if any error is encountered in either step, the error message is stored
|
// case if any error is encountered in either step, the error message is stored
|
||||||
// in 'err'
|
// in 'err'
|
||||||
static bool TryParseLocked(CommandLineFlag* flag, void* dst,
|
bool TryParseLocked(CommandLineFlag* flag, void* dst, absl::string_view value,
|
||||||
absl::string_view value, std::string* err) {
|
std::string* err)
|
||||||
|
EXCLUSIVE_LOCKS_REQUIRED(flag->locks->primary_mu) {
|
||||||
void* tentative_value = Clone(flag->op, flag->def);
|
void* tentative_value = Clone(flag->op, flag->def);
|
||||||
std::string parse_err;
|
std::string parse_err;
|
||||||
if (!Parse(flag->marshalling_op, value, tentative_value, &parse_err)) {
|
if (!Parse(flag->marshalling_op, value, tentative_value, &parse_err)) {
|
||||||
|
@ -297,7 +262,7 @@ static bool TryParseLocked(CommandLineFlag* flag, void* dst,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Validate(flag, tentative_value)) {
|
if (!flag->InvokeValidator(tentative_value)) {
|
||||||
*err = absl::StrCat("Failed validation of new value '",
|
*err = absl::StrCat("Failed validation of new value '",
|
||||||
Unparse(flag->marshalling_op, tentative_value),
|
Unparse(flag->marshalling_op, tentative_value),
|
||||||
"' for flag '", flag->Name(), "'");
|
"' for flag '", flag->Name(), "'");
|
||||||
|
@ -324,17 +289,23 @@ bool CommandLineFlag::SetFromString(absl::string_view value,
|
||||||
ValueSource source, std::string* err) {
|
ValueSource source, std::string* err) {
|
||||||
if (IsRetired()) return false;
|
if (IsRetired()) return false;
|
||||||
|
|
||||||
UpdateModifiedBit(this);
|
absl::MutexLock l(InitFlagIfNecessary());
|
||||||
|
|
||||||
absl::Mutex* mu = InitFlagIfNecessary(this);
|
// Direct-access flags can be modified without going through the
|
||||||
absl::MutexLock l(mu);
|
// flag API. Detect such changes and update the flag->modified bit.
|
||||||
|
if (!IsAbseilFlag()) {
|
||||||
|
if (!this->modified && ChangedDirectly(this, this->cur, this->def)) {
|
||||||
|
this->modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (set_mode) {
|
switch (set_mode) {
|
||||||
case SET_FLAGS_VALUE: {
|
case SET_FLAGS_VALUE: {
|
||||||
// set or modify the flag's value
|
// set or modify the flag's value
|
||||||
if (!TryParseLocked(this, this->cur, value, err)) return false;
|
if (!TryParseLocked(this, this->cur, value, err)) return false;
|
||||||
this->modified = true;
|
this->modified = true;
|
||||||
UpdateCopy(this, mu);
|
UpdateCopy(this);
|
||||||
|
InvokeCallback();
|
||||||
|
|
||||||
if (source == kCommandLine) {
|
if (source == kCommandLine) {
|
||||||
this->on_command_line = true;
|
this->on_command_line = true;
|
||||||
|
@ -346,7 +317,8 @@ bool CommandLineFlag::SetFromString(absl::string_view value,
|
||||||
if (!this->modified) {
|
if (!this->modified) {
|
||||||
if (!TryParseLocked(this, this->cur, value, err)) return false;
|
if (!TryParseLocked(this, this->cur, value, err)) return false;
|
||||||
this->modified = true;
|
this->modified = true;
|
||||||
UpdateCopy(this, mu);
|
UpdateCopy(this);
|
||||||
|
InvokeCallback();
|
||||||
} else {
|
} else {
|
||||||
// TODO(rogeeff): review and fix this semantic. Currently we do not fail
|
// TODO(rogeeff): review and fix this semantic. Currently we do not fail
|
||||||
// in this case if flag is modified. This is misleading since the flag's
|
// in this case if flag is modified. This is misleading since the flag's
|
||||||
|
@ -365,7 +337,8 @@ bool CommandLineFlag::SetFromString(absl::string_view value,
|
||||||
if (!this->modified) {
|
if (!this->modified) {
|
||||||
// Need to set both defvalue *and* current, in this case
|
// Need to set both defvalue *and* current, in this case
|
||||||
Copy(this->op, this->def, this->cur);
|
Copy(this->op, this->def, this->cur);
|
||||||
UpdateCopy(this, mu);
|
UpdateCopy(this);
|
||||||
|
InvokeCallback();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -379,5 +352,143 @@ bool CommandLineFlag::SetFromString(absl::string_view value,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CommandLineFlag::StoreAtomic(size_t size) {
|
||||||
|
int64_t t = 0;
|
||||||
|
assert(size <= sizeof(int64_t));
|
||||||
|
memcpy(&t, this->cur, size);
|
||||||
|
this->atomic.store(t, std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandLineFlag::CheckDefaultValueParsingRoundtrip() const {
|
||||||
|
std::string v = DefaultValue();
|
||||||
|
|
||||||
|
absl::MutexLock lock(InitFlagIfNecessary());
|
||||||
|
|
||||||
|
void* dst = Clone(this->op, this->def);
|
||||||
|
std::string error;
|
||||||
|
if (!flags_internal::Parse(this->marshalling_op, v, dst, &error)) {
|
||||||
|
ABSL_INTERNAL_LOG(
|
||||||
|
FATAL,
|
||||||
|
absl::StrCat("Flag ", Name(), " (from ", Filename(),
|
||||||
|
"): std::string form of default value '", v,
|
||||||
|
"' could not be parsed; error=", error));
|
||||||
|
}
|
||||||
|
|
||||||
|
// We do not compare dst to def since parsing/unparsing may make
|
||||||
|
// small changes, e.g., precision loss for floating point types.
|
||||||
|
Delete(this->op, dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CommandLineFlag::ValidateDefaultValue() const {
|
||||||
|
absl::MutexLock lock(InitFlagIfNecessary());
|
||||||
|
return InvokeValidator(this->def);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CommandLineFlag::ValidateInputValue(absl::string_view value) const {
|
||||||
|
absl::MutexLock l(InitFlagIfNecessary()); // protect default value access
|
||||||
|
|
||||||
|
void* obj = Clone(this->op, this->def);
|
||||||
|
std::string ignored_error;
|
||||||
|
const bool result =
|
||||||
|
flags_internal::Parse(this->marshalling_op, value, obj, &ignored_error) &&
|
||||||
|
InvokeValidator(obj);
|
||||||
|
Delete(this->op, obj);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int64_t CommandLineFlag::kAtomicInit;
|
||||||
|
|
||||||
|
void CommandLineFlag::Read(void* dst,
|
||||||
|
const flags_internal::FlagOpFn dst_op) const {
|
||||||
|
absl::ReaderMutexLock l(InitFlagIfNecessary());
|
||||||
|
|
||||||
|
// `dst_op` is the unmarshaling operation corresponding to the declaration
|
||||||
|
// visibile at the call site. `op` is the Flag's defined unmarshalling
|
||||||
|
// operation. They must match for this operation to be well-defined.
|
||||||
|
if (ABSL_PREDICT_FALSE(dst_op != op)) {
|
||||||
|
ABSL_INTERNAL_LOG(
|
||||||
|
ERROR,
|
||||||
|
absl::StrCat("Flag '", name,
|
||||||
|
"' is defined as one type and declared as another"));
|
||||||
|
}
|
||||||
|
CopyConstruct(op, cur, dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandLineFlag::Write(const void* src,
|
||||||
|
const flags_internal::FlagOpFn src_op) {
|
||||||
|
absl::MutexLock l(InitFlagIfNecessary());
|
||||||
|
|
||||||
|
// `src_op` is the marshalling operation corresponding to the declaration
|
||||||
|
// visible at the call site. `op` is the Flag's defined marshalling operation.
|
||||||
|
// They must match for this operation to be well-defined.
|
||||||
|
if (ABSL_PREDICT_FALSE(src_op != op)) {
|
||||||
|
ABSL_INTERNAL_LOG(
|
||||||
|
ERROR,
|
||||||
|
absl::StrCat("Flag '", name,
|
||||||
|
"' is defined as one type and declared as another"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ShouldValidateFlagValue(*this)) {
|
||||||
|
void* obj = Clone(op, src);
|
||||||
|
std::string ignored_error;
|
||||||
|
std::string src_as_str = Unparse(marshalling_op, src);
|
||||||
|
if (!Parse(marshalling_op, src_as_str, obj, &ignored_error) ||
|
||||||
|
!InvokeValidator(obj)) {
|
||||||
|
ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '", name,
|
||||||
|
"' to invalid value ", src_as_str));
|
||||||
|
}
|
||||||
|
Delete(op, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
modified = true;
|
||||||
|
counter++;
|
||||||
|
Copy(op, src, cur);
|
||||||
|
|
||||||
|
UpdateCopy(this);
|
||||||
|
InvokeCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string HelpText::GetHelpText() const {
|
||||||
|
if (help_function_) return help_function_();
|
||||||
|
if (help_message_) return help_message_;
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update any copy of the flag value that is stored in an atomic word.
|
||||||
|
// In addition if flag has a mutation callback this function invokes it.
|
||||||
|
void UpdateCopy(CommandLineFlag* flag) {
|
||||||
|
#define STORE_ATOMIC(T) \
|
||||||
|
else if (flag->IsOfType<T>()) { \
|
||||||
|
flag->StoreAtomic(sizeof(T)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false) {
|
||||||
|
}
|
||||||
|
ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(STORE_ATOMIC)
|
||||||
|
#undef STORE_ATOMIC
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true iff flag value was changed via direct-access.
|
||||||
|
bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b) {
|
||||||
|
if (!flag->IsAbseilFlag()) {
|
||||||
|
// Need to compare values for direct-access flags.
|
||||||
|
#define CHANGED_FOR_TYPE(T) \
|
||||||
|
if (flag->IsOfType<T>()) { \
|
||||||
|
return *reinterpret_cast<const T*>(a) != *reinterpret_cast<const T*>(b); \
|
||||||
|
}
|
||||||
|
|
||||||
|
CHANGED_FOR_TYPE(bool);
|
||||||
|
CHANGED_FOR_TYPE(int32_t);
|
||||||
|
CHANGED_FOR_TYPE(int64_t);
|
||||||
|
CHANGED_FOR_TYPE(uint64_t);
|
||||||
|
CHANGED_FOR_TYPE(double);
|
||||||
|
CHANGED_FOR_TYPE(std::string);
|
||||||
|
#undef CHANGED_FOR_TYPE
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace flags_internal
|
} // namespace flags_internal
|
||||||
} // namespace absl
|
} // namespace absl
|
||||||
|
|
|
@ -69,11 +69,15 @@ using HelpGenFunc = std::string (*)();
|
||||||
// based on default value supplied in flag's definition)
|
// based on default value supplied in flag's definition)
|
||||||
using InitialValGenFunc = void* (*)();
|
using InitialValGenFunc = void* (*)();
|
||||||
|
|
||||||
|
struct CommandLineFlagInfo;
|
||||||
|
|
||||||
// Signature for the mutation callback used by watched Flags
|
// Signature for the mutation callback used by watched Flags
|
||||||
// The callback is noexcept.
|
// The callback is noexcept.
|
||||||
// TODO(rogeeff): add noexcept after C++17 support is added.
|
// TODO(rogeeff): add noexcept after C++17 support is added.
|
||||||
using FlagCallback = void (*)();
|
using FlagCallback = void (*)();
|
||||||
|
|
||||||
|
using FlagValidator = bool (*)();
|
||||||
|
|
||||||
extern const char kStrippedFlagHelp[];
|
extern const char kStrippedFlagHelp[];
|
||||||
|
|
||||||
// The per-type function
|
// The per-type function
|
||||||
|
@ -217,6 +221,9 @@ struct CommandLineFlag {
|
||||||
atomic(kAtomicInit),
|
atomic(kAtomicInit),
|
||||||
locks(nullptr) {}
|
locks(nullptr) {}
|
||||||
|
|
||||||
|
// Revert the init routine.
|
||||||
|
void Destroy() const;
|
||||||
|
|
||||||
// Not copyable/assignable.
|
// Not copyable/assignable.
|
||||||
CommandLineFlag(const CommandLineFlag&) = delete;
|
CommandLineFlag(const CommandLineFlag&) = delete;
|
||||||
CommandLineFlag& operator=(const CommandLineFlag&) = delete;
|
CommandLineFlag& operator=(const CommandLineFlag&) = delete;
|
||||||
|
@ -224,7 +231,9 @@ struct CommandLineFlag {
|
||||||
absl::string_view Name() const { return name; }
|
absl::string_view Name() const { return name; }
|
||||||
std::string Help() const { return help.GetHelpText(); }
|
std::string Help() const { return help.GetHelpText(); }
|
||||||
bool IsRetired() const { return this->retired; }
|
bool IsRetired() const { return this->retired; }
|
||||||
bool IsSpecifiedOnCommandLine() const { return on_command_line; }
|
bool IsModified() const;
|
||||||
|
void SetModified(bool is_modified);
|
||||||
|
bool IsSpecifiedOnCommandLine() const;
|
||||||
// Returns true iff this is a handle to an Abseil Flag.
|
// Returns true iff this is a handle to an Abseil Flag.
|
||||||
bool IsAbseilFlag() const {
|
bool IsAbseilFlag() const {
|
||||||
// Set to null for V1 flags
|
// Set to null for V1 flags
|
||||||
|
@ -236,6 +245,10 @@ struct CommandLineFlag {
|
||||||
std::string DefaultValue() const;
|
std::string DefaultValue() const;
|
||||||
std::string CurrentValue() const;
|
std::string CurrentValue() const;
|
||||||
|
|
||||||
|
bool HasValidatorFn() const;
|
||||||
|
bool SetValidatorFn(FlagValidator fn);
|
||||||
|
bool InvokeValidator(const void* value) const;
|
||||||
|
|
||||||
// Return true iff flag has type T.
|
// Return true iff flag has type T.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline bool IsOfType() const {
|
inline bool IsOfType() const {
|
||||||
|
@ -245,7 +258,7 @@ struct CommandLineFlag {
|
||||||
// Attempts to retrieve the flag value. Returns value on success,
|
// Attempts to retrieve the flag value. Returns value on success,
|
||||||
// absl::nullopt otherwise.
|
// absl::nullopt otherwise.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
absl::optional<T> Get() {
|
absl::optional<T> Get() const {
|
||||||
if (IsRetired() || flags_internal::FlagOps<T> != this->op)
|
if (IsRetired() || flags_internal::FlagOps<T> != this->op)
|
||||||
return absl::nullopt;
|
return absl::nullopt;
|
||||||
|
|
||||||
|
@ -256,6 +269,7 @@ struct CommandLineFlag {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetCallback(const flags_internal::FlagCallback mutation_callback);
|
void SetCallback(const flags_internal::FlagCallback mutation_callback);
|
||||||
|
void InvokeCallback();
|
||||||
|
|
||||||
// Sets the value of the flag based on specified std::string `value`. If the flag
|
// Sets the value of the flag based on specified std::string `value`. If the flag
|
||||||
// was successfully set to new value, it returns true. Otherwise, sets `error`
|
// was successfully set to new value, it returns true. Otherwise, sets `error`
|
||||||
|
@ -269,13 +283,21 @@ struct CommandLineFlag {
|
||||||
flags_internal::FlagSettingMode set_mode,
|
flags_internal::FlagSettingMode set_mode,
|
||||||
flags_internal::ValueSource source, std::string* error);
|
flags_internal::ValueSource source, std::string* error);
|
||||||
|
|
||||||
|
void StoreAtomic(size_t size);
|
||||||
|
|
||||||
|
void CheckDefaultValueParsingRoundtrip() const;
|
||||||
|
// Invoke the flag validators for old flags.
|
||||||
|
// TODO(rogeeff): implement proper validators for Abseil Flags
|
||||||
|
bool ValidateDefaultValue() const;
|
||||||
|
bool ValidateInputValue(absl::string_view value) const;
|
||||||
|
|
||||||
// Constant configuration for a particular flag.
|
// Constant configuration for a particular flag.
|
||||||
private:
|
private:
|
||||||
const char* const name;
|
const char* const name;
|
||||||
const HelpText help;
|
const HelpText help;
|
||||||
const char* const filename;
|
const char* const filename;
|
||||||
|
|
||||||
public:
|
protected:
|
||||||
const FlagOpFn op; // Type-specific handler
|
const FlagOpFn op; // Type-specific handler
|
||||||
const FlagMarshallingOpFn marshalling_op; // Marshalling ops handler
|
const FlagMarshallingOpFn marshalling_op; // Marshalling ops handler
|
||||||
const InitialValGenFunc make_init_value; // Makes initial value for the flag
|
const InitialValGenFunc make_init_value; // Makes initial value for the flag
|
||||||
|
@ -285,7 +307,7 @@ struct CommandLineFlag {
|
||||||
// Mutable state (guarded by locks->primary_mu).
|
// Mutable state (guarded by locks->primary_mu).
|
||||||
bool modified; // Has flag value been modified?
|
bool modified; // Has flag value been modified?
|
||||||
bool on_command_line; // Specified on command line.
|
bool on_command_line; // Specified on command line.
|
||||||
bool (*validator)(); // Validator function, or nullptr
|
FlagValidator validator; // Validator function, or nullptr
|
||||||
FlagCallback callback; // Mutation callback, or nullptr
|
FlagCallback callback; // Mutation callback, or nullptr
|
||||||
void* def; // Lazily initialized pointer to default value
|
void* def; // Lazily initialized pointer to default value
|
||||||
void* cur; // Lazily initialized pointer to current value
|
void* cur; // Lazily initialized pointer to current value
|
||||||
|
@ -302,24 +324,26 @@ struct CommandLineFlag {
|
||||||
// TODO(rogeeff): fix it once Mutex has constexpr constructor
|
// TODO(rogeeff): fix it once Mutex has constexpr constructor
|
||||||
struct CommandLineFlagLocks* locks; // locks, laziliy allocated.
|
struct CommandLineFlagLocks* locks; // locks, laziliy allocated.
|
||||||
|
|
||||||
|
// Ensure that the lazily initialized fields of *flag have been initialized,
|
||||||
|
// and return the lock which should be locked when flag's state is mutated.
|
||||||
|
absl::Mutex* InitFlagIfNecessary() const;
|
||||||
|
|
||||||
// copy construct new value of flag's type in a memory referenced by dst
|
// copy construct new value of flag's type in a memory referenced by dst
|
||||||
// based on current flag's value
|
// based on current flag's value
|
||||||
void Read(void* dst, const flags_internal::FlagOpFn dst_op) const;
|
void Read(void* dst, const flags_internal::FlagOpFn dst_op) const;
|
||||||
// updates flag's value to *src (locked)
|
// updates flag's value to *src (locked)
|
||||||
void Write(const void* src, const flags_internal::FlagOpFn src_op);
|
void Write(const void* src, const flags_internal::FlagOpFn src_op);
|
||||||
|
|
||||||
ABSL_DEPRECATED(
|
|
||||||
"temporary until FlagName call sites are migrated and validator API is "
|
|
||||||
"changed")
|
|
||||||
const char* NameAsCString() const { return name; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class FlagRegistry;
|
friend class FlagRegistry;
|
||||||
|
friend class FlagPtrMap;
|
||||||
|
friend class FlagSaverImpl;
|
||||||
|
friend void FillCommandLineFlagInfo(CommandLineFlag* flag,
|
||||||
|
CommandLineFlagInfo* result);
|
||||||
|
friend bool TryParseLocked(CommandLineFlag* flag, void* dst,
|
||||||
|
absl::string_view value, std::string* err);
|
||||||
|
friend absl::Mutex* InitFlag(CommandLineFlag* flag);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Ensure that the lazily initialized fields of *flag have been initialized,
|
|
||||||
// and return &flag->locks->primary_mu.
|
|
||||||
absl::Mutex* InitFlagIfNecessary(CommandLineFlag* flag);
|
|
||||||
// Update any copy of the flag value that is stored in an atomic word.
|
// Update any copy of the flag value that is stored in an atomic word.
|
||||||
// In addition if flag has a mutation callback this function invokes it. While
|
// In addition if flag has a mutation callback this function invokes it. While
|
||||||
// callback is being invoked the primary flag's mutex is unlocked and it is
|
// callback is being invoked the primary flag's mutex is unlocked and it is
|
||||||
|
@ -332,15 +356,9 @@ absl::Mutex* InitFlagIfNecessary(CommandLineFlag* flag);
|
||||||
// different by the time the callback invocation is completed.
|
// different by the time the callback invocation is completed.
|
||||||
// Requires that *primary_lock be held in exclusive mode; it may be released
|
// Requires that *primary_lock be held in exclusive mode; it may be released
|
||||||
// and reacquired by the implementation.
|
// and reacquired by the implementation.
|
||||||
void UpdateCopy(CommandLineFlag* flag, absl::Mutex* primary_lock);
|
void UpdateCopy(CommandLineFlag* flag);
|
||||||
// Return true iff flag value was changed via direct-access.
|
// Return true iff flag value was changed via direct-access.
|
||||||
bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b);
|
bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b);
|
||||||
// Direct-access flags can be modified without going through the
|
|
||||||
// flag API. Detect such changes and updated the modified bit.
|
|
||||||
void UpdateModifiedBit(CommandLineFlag* flag);
|
|
||||||
// Invoke the flag validators for old flags.
|
|
||||||
// TODO(rogeeff): implement proper validators for Abseil Flags
|
|
||||||
bool Validate(CommandLineFlag* flag, const void* value);
|
|
||||||
|
|
||||||
// This macro is the "source of truth" for the list of supported flag types we
|
// This macro is the "source of truth" for the list of supported flag types we
|
||||||
// expect to perform lock free operations on. Specifically it generates code,
|
// expect to perform lock free operations on. Specifically it generates code,
|
||||||
|
|
|
@ -100,39 +100,39 @@ TEST_F(CommandLineFlagTest, TestSetFromStringCurrentValue) {
|
||||||
std::string err;
|
std::string err;
|
||||||
|
|
||||||
auto* flag_01 = flags::FindCommandLineFlag("int_flag");
|
auto* flag_01 = flags::FindCommandLineFlag("int_flag");
|
||||||
EXPECT_FALSE(flag_01->on_command_line);
|
EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine());
|
||||||
|
|
||||||
EXPECT_TRUE(flag_01->SetFromString("11", flags::SET_FLAGS_VALUE,
|
EXPECT_TRUE(flag_01->SetFromString("11", flags::SET_FLAGS_VALUE,
|
||||||
flags::kProgrammaticChange, &err));
|
flags::kProgrammaticChange, &err));
|
||||||
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11);
|
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11);
|
||||||
EXPECT_FALSE(flag_01->on_command_line);
|
EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine());
|
||||||
|
|
||||||
EXPECT_TRUE(flag_01->SetFromString("-123", flags::SET_FLAGS_VALUE,
|
EXPECT_TRUE(flag_01->SetFromString("-123", flags::SET_FLAGS_VALUE,
|
||||||
flags::kProgrammaticChange, &err));
|
flags::kProgrammaticChange, &err));
|
||||||
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123);
|
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123);
|
||||||
EXPECT_FALSE(flag_01->on_command_line);
|
EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine());
|
||||||
|
|
||||||
EXPECT_TRUE(!flag_01->SetFromString("xyz", flags::SET_FLAGS_VALUE,
|
EXPECT_TRUE(!flag_01->SetFromString("xyz", flags::SET_FLAGS_VALUE,
|
||||||
flags::kProgrammaticChange, &err));
|
flags::kProgrammaticChange, &err));
|
||||||
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123);
|
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123);
|
||||||
EXPECT_EQ(err, "Illegal value 'xyz' specified for flag 'int_flag'");
|
EXPECT_EQ(err, "Illegal value 'xyz' specified for flag 'int_flag'");
|
||||||
EXPECT_FALSE(flag_01->on_command_line);
|
EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine());
|
||||||
|
|
||||||
EXPECT_TRUE(!flag_01->SetFromString("A1", flags::SET_FLAGS_VALUE,
|
EXPECT_TRUE(!flag_01->SetFromString("A1", flags::SET_FLAGS_VALUE,
|
||||||
flags::kProgrammaticChange, &err));
|
flags::kProgrammaticChange, &err));
|
||||||
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123);
|
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123);
|
||||||
EXPECT_EQ(err, "Illegal value 'A1' specified for flag 'int_flag'");
|
EXPECT_EQ(err, "Illegal value 'A1' specified for flag 'int_flag'");
|
||||||
EXPECT_FALSE(flag_01->on_command_line);
|
EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine());
|
||||||
|
|
||||||
EXPECT_TRUE(flag_01->SetFromString("0x10", flags::SET_FLAGS_VALUE,
|
EXPECT_TRUE(flag_01->SetFromString("0x10", flags::SET_FLAGS_VALUE,
|
||||||
flags::kProgrammaticChange, &err));
|
flags::kProgrammaticChange, &err));
|
||||||
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 16);
|
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 16);
|
||||||
EXPECT_FALSE(flag_01->on_command_line);
|
EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine());
|
||||||
|
|
||||||
EXPECT_TRUE(flag_01->SetFromString("011", flags::SET_FLAGS_VALUE,
|
EXPECT_TRUE(flag_01->SetFromString("011", flags::SET_FLAGS_VALUE,
|
||||||
flags::kCommandLine, &err));
|
flags::kCommandLine, &err));
|
||||||
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11);
|
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11);
|
||||||
EXPECT_TRUE(flag_01->on_command_line);
|
EXPECT_TRUE(flag_01->IsSpecifiedOnCommandLine());
|
||||||
|
|
||||||
EXPECT_TRUE(!flag_01->SetFromString("", flags::SET_FLAGS_VALUE,
|
EXPECT_TRUE(!flag_01->SetFromString("", flags::SET_FLAGS_VALUE,
|
||||||
flags::kProgrammaticChange, &err));
|
flags::kProgrammaticChange, &err));
|
||||||
|
|
|
@ -24,40 +24,58 @@ namespace flags_internal {
|
||||||
|
|
||||||
// This is "unspecified" implementation of absl::Flag<T> type.
|
// This is "unspecified" implementation of absl::Flag<T> type.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class Flag {
|
class Flag : public flags_internal::CommandLineFlag {
|
||||||
public:
|
public:
|
||||||
constexpr Flag(const char* name, const flags_internal::HelpGenFunc help_gen,
|
constexpr Flag(const char* name, const flags_internal::HelpGenFunc help_gen,
|
||||||
const char* filename,
|
const char* filename,
|
||||||
const flags_internal::FlagMarshallingOpFn marshalling_op,
|
const flags_internal::FlagMarshallingOpFn marshalling_op_arg,
|
||||||
const flags_internal::InitialValGenFunc initial_value_gen)
|
const flags_internal::InitialValGenFunc initial_value_gen)
|
||||||
: internal(name, flags_internal::HelpText::FromFunctionPointer(help_gen),
|
: flags_internal::CommandLineFlag(
|
||||||
filename, &flags_internal::FlagOps<T>, marshalling_op,
|
name, flags_internal::HelpText::FromFunctionPointer(help_gen),
|
||||||
|
filename, &flags_internal::FlagOps<T>, marshalling_op_arg,
|
||||||
initial_value_gen,
|
initial_value_gen,
|
||||||
/*retired_arg=*/false, /*def_arg=*/nullptr,
|
/*retired_arg=*/false, /*def_arg=*/nullptr,
|
||||||
/*cur_arg=*/nullptr) {}
|
/*cur_arg=*/nullptr) {}
|
||||||
|
|
||||||
// Not copyable/assignable.
|
T Get() const {
|
||||||
Flag(const Flag<T>&) = delete;
|
// Implementation notes:
|
||||||
Flag<T>& operator=(const Flag<T>&) = delete;
|
//
|
||||||
|
// We are wrapping a union around the value of `T` to serve three purposes:
|
||||||
|
//
|
||||||
|
// 1. `U.value` has correct size and alignment for a value of type `T`
|
||||||
|
// 2. The `U.value` constructor is not invoked since U's constructor does
|
||||||
|
// not
|
||||||
|
// do it explicitly.
|
||||||
|
// 3. The `U.value` destructor is invoked since U's destructor does it
|
||||||
|
// explicitly. This makes `U` a kind of RAII wrapper around non default
|
||||||
|
// constructible value of T, which is destructed when we leave the
|
||||||
|
// scope. We do need to destroy U.value, which is constructed by
|
||||||
|
// CommandLineFlag::Read even though we left it in a moved-from state
|
||||||
|
// after std::move.
|
||||||
|
//
|
||||||
|
// All of this serves to avoid requiring `T` being default constructible.
|
||||||
|
union U {
|
||||||
|
T value;
|
||||||
|
U() {}
|
||||||
|
~U() { value.~T(); }
|
||||||
|
};
|
||||||
|
U u;
|
||||||
|
|
||||||
absl::string_view Name() const { return internal.Name(); }
|
this->Read(&u.value, &flags_internal::FlagOps<T>);
|
||||||
std::string Help() const { return internal.Help(); }
|
return std::move(u.value);
|
||||||
std::string Filename() const { return internal.Filename(); }
|
|
||||||
|
|
||||||
absl::flags_internal::CommandLineFlag internal;
|
|
||||||
|
|
||||||
void SetCallback(const flags_internal::FlagCallback mutation_callback) {
|
|
||||||
internal.SetCallback(mutation_callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
bool AtomicGet(T* v) const {
|
||||||
// TODO(rogeeff): add these validations once UnparseFlag invocation is fixed
|
const int64_t r = this->atomic.load(std::memory_order_acquire);
|
||||||
// for built-in types and when we cleanup existing code from operating on
|
if (r != flags_internal::CommandLineFlag::kAtomicInit) {
|
||||||
// forward declared types.
|
memcpy(v, &r, sizeof(T));
|
||||||
// auto IsCopyConstructible(const T& v) -> decltype(T(v));
|
return true;
|
||||||
// auto HasAbslParseFlag(absl::string_view in, T* dst, std::string* err)
|
}
|
||||||
// -> decltype(AbslParseFlag(in, dst, err));
|
|
||||||
// auto HasAbslUnparseFlag(const T& v) -> decltype(AbslUnparseFlag(v));
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Set(const T& v) { this->Write(&v, &flags_internal::FlagOps<T>); }
|
||||||
};
|
};
|
||||||
|
|
||||||
// This class facilitates Flag object registration and tail expression-based
|
// This class facilitates Flag object registration and tail expression-based
|
||||||
|
@ -67,7 +85,7 @@ template <typename T, bool do_register>
|
||||||
class FlagRegistrar {
|
class FlagRegistrar {
|
||||||
public:
|
public:
|
||||||
explicit FlagRegistrar(Flag<T>* flag) : flag_(flag) {
|
explicit FlagRegistrar(Flag<T>* flag) : flag_(flag) {
|
||||||
if (do_register) flags_internal::RegisterCommandLineFlag(&flag_->internal);
|
if (do_register) flags_internal::RegisterCommandLineFlag(flag_);
|
||||||
}
|
}
|
||||||
|
|
||||||
FlagRegistrar& OnUpdate(flags_internal::FlagCallback cb) && {
|
FlagRegistrar& OnUpdate(flags_internal::FlagCallback cb) && {
|
||||||
|
|
|
@ -34,13 +34,7 @@ namespace flags_internal {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void DestroyFlag(CommandLineFlag* flag) NO_THREAD_SAFETY_ANALYSIS {
|
void DestroyFlag(CommandLineFlag* flag) NO_THREAD_SAFETY_ANALYSIS {
|
||||||
// Values are heap allocated for retired and Abseil Flags.
|
flag->Destroy();
|
||||||
if (flag->IsRetired() || flag->IsAbseilFlag()) {
|
|
||||||
if (flag->cur) Delete(flag->op, flag->cur);
|
|
||||||
if (flag->def) Delete(flag->op, flag->def);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete flag->locks;
|
|
||||||
|
|
||||||
// CommandLineFlag handle object is heap allocated for non Abseil Flags.
|
// CommandLineFlag handle object is heap allocated for non Abseil Flags.
|
||||||
if (!flag->IsAbseilFlag()) {
|
if (!flag->IsAbseilFlag()) {
|
||||||
|
@ -48,6 +42,8 @@ void DestroyFlag(CommandLineFlag* flag) NO_THREAD_SAFETY_ANALYSIS {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
// FlagRegistry
|
// FlagRegistry
|
||||||
// A FlagRegistry singleton object holds all flag objects indexed
|
// A FlagRegistry singleton object holds all flag objects indexed
|
||||||
|
@ -105,8 +101,6 @@ class FlagPtrMap {
|
||||||
};
|
};
|
||||||
constexpr size_t FlagPtrMap::kNumBuckets;
|
constexpr size_t FlagPtrMap::kNumBuckets;
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
class FlagRegistry {
|
class FlagRegistry {
|
||||||
public:
|
public:
|
||||||
FlagRegistry() = default;
|
FlagRegistry() = default;
|
||||||
|
@ -292,10 +286,10 @@ class FlagSaverImpl {
|
||||||
saved.op = flag->op;
|
saved.op = flag->op;
|
||||||
saved.marshalling_op = flag->marshalling_op;
|
saved.marshalling_op = flag->marshalling_op;
|
||||||
{
|
{
|
||||||
absl::MutexLock l(InitFlagIfNecessary(flag));
|
absl::MutexLock l(flag->InitFlagIfNecessary());
|
||||||
saved.validator = flag->validator;
|
saved.validator = flag->validator;
|
||||||
saved.modified = flag->modified;
|
saved.modified = flag->modified;
|
||||||
saved.on_command_line = flag->IsSpecifiedOnCommandLine();
|
saved.on_command_line = flag->on_command_line;
|
||||||
saved.current = Clone(saved.op, flag->cur);
|
saved.current = Clone(saved.op, flag->cur);
|
||||||
saved.default_value = Clone(saved.op, flag->def);
|
saved.default_value = Clone(saved.op, flag->def);
|
||||||
saved.counter = flag->counter;
|
saved.counter = flag->counter;
|
||||||
|
@ -318,34 +312,34 @@ class FlagSaverImpl {
|
||||||
|
|
||||||
bool restored = false;
|
bool restored = false;
|
||||||
{
|
{
|
||||||
absl::Mutex* mu = InitFlagIfNecessary(flag);
|
absl::MutexLock l(flag->InitFlagIfNecessary());
|
||||||
absl::MutexLock l(mu);
|
|
||||||
flag->validator = src.validator;
|
flag->validator = src.validator;
|
||||||
flag->modified = src.modified;
|
flag->modified = src.modified;
|
||||||
flag->on_command_line = src.on_command_line;
|
flag->on_command_line = src.on_command_line;
|
||||||
if (flag->counter != src.counter ||
|
if (flag->counter != src.counter ||
|
||||||
ChangedDirectly(flag, src.default_value, flag->def)) {
|
ChangedDirectly(flag, src.default_value, flag->def)) {
|
||||||
flag->counter++;
|
restored = true;
|
||||||
Copy(src.op, src.default_value, flag->def);
|
Copy(src.op, src.default_value, flag->def);
|
||||||
}
|
}
|
||||||
if (flag->counter != src.counter ||
|
if (flag->counter != src.counter ||
|
||||||
ChangedDirectly(flag, src.current, flag->cur)) {
|
ChangedDirectly(flag, src.current, flag->cur)) {
|
||||||
restored = true;
|
restored = true;
|
||||||
flag->counter++;
|
|
||||||
Copy(src.op, src.current, flag->cur);
|
Copy(src.op, src.current, flag->cur);
|
||||||
UpdateCopy(flag, mu);
|
UpdateCopy(flag);
|
||||||
|
flag->InvokeCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (restored) {
|
||||||
|
flag->counter++;
|
||||||
|
|
||||||
// Revalidate the flag because the validator might store state based
|
// Revalidate the flag because the validator might store state based
|
||||||
// on the flag's value, which just changed due to the restore.
|
// on the flag's value, which just changed due to the restore.
|
||||||
// Failing validation is ignored because it's assumed that the flag
|
// Failing validation is ignored because it's assumed that the flag
|
||||||
// was valid previously and there's little that can be done about it
|
// was valid previously and there's little that can be done about it
|
||||||
// here, anyway.
|
// here, anyway.
|
||||||
Validate(flag, flag->cur);
|
flag->ValidateInputValue(flag->CurrentValue());
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log statements must be done when no flag lock is held.
|
|
||||||
if (restored) {
|
|
||||||
ABSL_INTERNAL_LOG(
|
ABSL_INTERNAL_LOG(
|
||||||
INFO, absl::StrCat("Restore saved value of ", flag->Name(), ": ",
|
INFO, absl::StrCat("Restore saved value of ", flag->Name(), ": ",
|
||||||
Unparse(src.marshalling_op, src.current)));
|
Unparse(src.marshalling_op, src.current)));
|
||||||
|
@ -412,13 +406,17 @@ void FillCommandLineFlagInfo(CommandLineFlag* flag,
|
||||||
result->description = flag->Help();
|
result->description = flag->Help();
|
||||||
result->filename = flag->Filename();
|
result->filename = flag->Filename();
|
||||||
|
|
||||||
UpdateModifiedBit(flag);
|
if (!flag->IsAbseilFlag()) {
|
||||||
|
if (!flag->IsModified() && ChangedDirectly(flag, flag->cur, flag->def)) {
|
||||||
|
flag->modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
absl::MutexLock l(InitFlagIfNecessary(flag));
|
|
||||||
result->current_value = flag->CurrentValue();
|
result->current_value = flag->CurrentValue();
|
||||||
result->default_value = flag->DefaultValue();
|
result->default_value = flag->DefaultValue();
|
||||||
result->is_default = !flag->modified;
|
result->is_default = !flag->IsModified();
|
||||||
result->has_validator_fn = (flag->validator != nullptr);
|
result->has_validator_fn = flag->HasValidatorFn();
|
||||||
|
absl::MutexLock l(flag->InitFlagIfNecessary());
|
||||||
result->flag_ptr = flag->IsAbseilFlag() ? nullptr : flag->cur;
|
result->flag_ptr = flag->IsAbseilFlag() ? nullptr : flag->cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,6 @@ bool GetCommandLineOption(absl::string_view name, std::string* value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::MutexLock l(InitFlagIfNecessary(flag));
|
|
||||||
*value = flag->CurrentValue();
|
*value = flag->CurrentValue();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -88,22 +87,9 @@ bool SetCommandLineOptionWithMode(absl::string_view name,
|
||||||
|
|
||||||
bool IsValidFlagValue(absl::string_view name, absl::string_view value) {
|
bool IsValidFlagValue(absl::string_view name, absl::string_view value) {
|
||||||
CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name);
|
CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name);
|
||||||
if (flag == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flag->IsRetired()) {
|
return flag != nullptr &&
|
||||||
return true;
|
(flag->IsRetired() || flag->ValidateInputValue(value));
|
||||||
}
|
|
||||||
|
|
||||||
// No need to lock the flag since we are not mutating it.
|
|
||||||
void* obj = Clone(flag->op, flag->def);
|
|
||||||
std::string ignored_error;
|
|
||||||
const bool result =
|
|
||||||
flags_internal::Parse(flag->marshalling_op, value, obj, &ignored_error) &&
|
|
||||||
Validate(flag, obj);
|
|
||||||
Delete(flag->op, obj);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
|
@ -111,7 +97,6 @@ bool IsValidFlagValue(absl::string_view name, absl::string_view value) {
|
||||||
bool SpecifiedOnCommandLine(absl::string_view name) {
|
bool SpecifiedOnCommandLine(absl::string_view name) {
|
||||||
CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name);
|
CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name);
|
||||||
if (flag != nullptr && !flag->IsRetired()) {
|
if (flag != nullptr && !flag->IsRetired()) {
|
||||||
absl::MutexLock l(InitFlagIfNecessary(flag));
|
|
||||||
return flag->IsSpecifiedOnCommandLine();
|
return flag->IsSpecifiedOnCommandLine();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -21,11 +21,11 @@
|
||||||
#include "absl/flags/flag.h"
|
#include "absl/flags/flag.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/usage.h"
|
|
||||||
#include "absl/flags/usage_config.h"
|
#include "absl/flags/usage_config.h"
|
||||||
#include "absl/strings/ascii.h"
|
#include "absl/strings/ascii.h"
|
||||||
#include "absl/strings/str_cat.h"
|
#include "absl/strings/str_cat.h"
|
||||||
#include "absl/strings/str_split.h"
|
#include "absl/strings/str_split.h"
|
||||||
|
#include "absl/strings/string_view.h"
|
||||||
#include "absl/synchronization/mutex.h"
|
#include "absl/synchronization/mutex.h"
|
||||||
|
|
||||||
ABSL_FLAG(bool, help, false,
|
ABSL_FLAG(bool, help, false,
|
||||||
|
@ -185,7 +185,7 @@ void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag,
|
||||||
}
|
}
|
||||||
printer.Write(absl::StrCat("default: ", dflt_val, ";"));
|
printer.Write(absl::StrCat("default: ", dflt_val, ";"));
|
||||||
|
|
||||||
if (flag.modified) {
|
if (flag.IsModified()) {
|
||||||
std::string curr_val = flag.CurrentValue();
|
std::string curr_val = flag.CurrentValue();
|
||||||
if (flag.IsOfType<std::string>()) {
|
if (flag.IsOfType<std::string>()) {
|
||||||
curr_val = absl::StrCat("\"", curr_val, "\"");
|
curr_val = absl::StrCat("\"", curr_val, "\"");
|
||||||
|
@ -202,10 +202,10 @@ void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag,
|
||||||
// STRIP_FLAG_HELP 1' then this flag will not be displayed by '--help'
|
// STRIP_FLAG_HELP 1' then this flag will not be displayed by '--help'
|
||||||
// and its variants.
|
// and its variants.
|
||||||
void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
|
void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
|
||||||
HelpFormat format = HelpFormat::kHumanReadable) {
|
HelpFormat format, absl::string_view program_usage_message) {
|
||||||
if (format == HelpFormat::kHumanReadable) {
|
if (format == HelpFormat::kHumanReadable) {
|
||||||
out << flags_internal::ShortProgramInvocationName() << ": "
|
out << flags_internal::ShortProgramInvocationName() << ": "
|
||||||
<< absl::ProgramUsageMessage() << "\n\n";
|
<< program_usage_message << "\n\n";
|
||||||
} else {
|
} else {
|
||||||
// XML schema is not a part of our public API for now.
|
// XML schema is not a part of our public API for now.
|
||||||
out << "<?xml version=\"1.0\"?>\n"
|
out << "<?xml version=\"1.0\"?>\n"
|
||||||
|
@ -214,7 +214,7 @@ void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
|
||||||
// The program name and usage.
|
// The program name and usage.
|
||||||
<< XMLElement("program", flags_internal::ShortProgramInvocationName())
|
<< XMLElement("program", flags_internal::ShortProgramInvocationName())
|
||||||
<< '\n'
|
<< '\n'
|
||||||
<< XMLElement("usage", absl::ProgramUsageMessage()) << '\n';
|
<< XMLElement("usage", program_usage_message) << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map of package name to
|
// Map of package name to
|
||||||
|
@ -228,8 +228,6 @@ void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
|
||||||
matching_flags;
|
matching_flags;
|
||||||
|
|
||||||
flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) {
|
flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) {
|
||||||
absl::MutexLock l(InitFlagIfNecessary(flag));
|
|
||||||
|
|
||||||
std::string flag_filename = flag->Filename();
|
std::string flag_filename = flag->Filename();
|
||||||
|
|
||||||
// Ignore retired flags.
|
// Ignore retired flags.
|
||||||
|
@ -292,44 +290,51 @@ void FlagHelp(std::ostream& out, const flags_internal::CommandLineFlag& flag,
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
// Produces the help messages for all flags matching the filter.
|
// Produces the help messages for all flags matching the filter.
|
||||||
// If filter is empty produces help messages for all flags.
|
// If filter is empty produces help messages for all flags.
|
||||||
void FlagsHelp(std::ostream& out, absl::string_view filter, HelpFormat format) {
|
void FlagsHelp(std::ostream& out, absl::string_view filter, HelpFormat format,
|
||||||
|
absl::string_view program_usage_message) {
|
||||||
flags_internal::FlagKindFilter filter_cb = [&](absl::string_view filename) {
|
flags_internal::FlagKindFilter filter_cb = [&](absl::string_view filename) {
|
||||||
return filter.empty() || filename.find(filter) != absl::string_view::npos;
|
return filter.empty() || filename.find(filter) != absl::string_view::npos;
|
||||||
};
|
};
|
||||||
flags_internal::FlagsHelpImpl(out, filter_cb, format);
|
flags_internal::FlagsHelpImpl(out, filter_cb, format, program_usage_message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
// Checks all the 'usage' command line flags to see if any have been set.
|
// Checks all the 'usage' command line flags to see if any have been set.
|
||||||
// If so, handles them appropriately.
|
// If so, handles them appropriately.
|
||||||
int HandleUsageFlags(std::ostream& out) {
|
int HandleUsageFlags(std::ostream& out,
|
||||||
|
absl::string_view program_usage_message) {
|
||||||
if (absl::GetFlag(FLAGS_helpshort)) {
|
if (absl::GetFlag(FLAGS_helpshort)) {
|
||||||
flags_internal::FlagsHelpImpl(
|
flags_internal::FlagsHelpImpl(
|
||||||
out, flags_internal::GetUsageConfig().contains_helpshort_flags,
|
out, flags_internal::GetUsageConfig().contains_helpshort_flags,
|
||||||
HelpFormat::kHumanReadable);
|
HelpFormat::kHumanReadable, program_usage_message);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (absl::GetFlag(FLAGS_helpfull)) {
|
if (absl::GetFlag(FLAGS_helpfull)) {
|
||||||
// show all options
|
// show all options
|
||||||
flags_internal::FlagsHelp(out);
|
flags_internal::FlagsHelp(out, "", HelpFormat::kHumanReadable,
|
||||||
|
program_usage_message);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!absl::GetFlag(FLAGS_helpon).empty()) {
|
if (!absl::GetFlag(FLAGS_helpon).empty()) {
|
||||||
flags_internal::FlagsHelp(
|
flags_internal::FlagsHelp(
|
||||||
out, absl::StrCat("/", absl::GetFlag(FLAGS_helpon), "."));
|
out, absl::StrCat("/", absl::GetFlag(FLAGS_helpon), "."),
|
||||||
|
HelpFormat::kHumanReadable, program_usage_message);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!absl::GetFlag(FLAGS_helpmatch).empty()) {
|
if (!absl::GetFlag(FLAGS_helpmatch).empty()) {
|
||||||
flags_internal::FlagsHelp(out, absl::GetFlag(FLAGS_helpmatch));
|
flags_internal::FlagsHelp(out, absl::GetFlag(FLAGS_helpmatch),
|
||||||
|
HelpFormat::kHumanReadable,
|
||||||
|
program_usage_message);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (absl::GetFlag(FLAGS_help)) {
|
if (absl::GetFlag(FLAGS_help)) {
|
||||||
flags_internal::FlagsHelpImpl(
|
flags_internal::FlagsHelpImpl(
|
||||||
out, flags_internal::GetUsageConfig().contains_help_flags);
|
out, flags_internal::GetUsageConfig().contains_help_flags,
|
||||||
|
HelpFormat::kHumanReadable, program_usage_message);
|
||||||
|
|
||||||
out << "\nTry --helpfull to get a list of all flags.\n";
|
out << "\nTry --helpfull to get a list of all flags.\n";
|
||||||
|
|
||||||
|
@ -338,7 +343,8 @@ int HandleUsageFlags(std::ostream& out) {
|
||||||
|
|
||||||
if (absl::GetFlag(FLAGS_helppackage)) {
|
if (absl::GetFlag(FLAGS_helppackage)) {
|
||||||
flags_internal::FlagsHelpImpl(
|
flags_internal::FlagsHelpImpl(
|
||||||
out, flags_internal::GetUsageConfig().contains_helppackage_flags);
|
out, flags_internal::GetUsageConfig().contains_helppackage_flags,
|
||||||
|
HelpFormat::kHumanReadable, program_usage_message);
|
||||||
|
|
||||||
out << "\nTry --helpfull to get a list of all flags.\n";
|
out << "\nTry --helpfull to get a list of all flags.\n";
|
||||||
|
|
||||||
|
|
|
@ -47,8 +47,8 @@ void FlagHelp(std::ostream& out, const flags_internal::CommandLineFlag& flag,
|
||||||
// .../path/to/file.<ext>
|
// .../path/to/file.<ext>
|
||||||
// for any extension 'ext'. If the filter is empty this function produces help
|
// for any extension 'ext'. If the filter is empty this function produces help
|
||||||
// messages for all flags.
|
// messages for all flags.
|
||||||
void FlagsHelp(std::ostream& out, absl::string_view filter = {},
|
void FlagsHelp(std::ostream& out, absl::string_view filter,
|
||||||
HelpFormat format = HelpFormat::kHumanReadable);
|
HelpFormat format, absl::string_view program_usage_message);
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -60,7 +60,8 @@ void FlagsHelp(std::ostream& out, absl::string_view filter = {},
|
||||||
// -1 - if no usage flags were set on a commmand line.
|
// -1 - if no usage flags were set on a commmand line.
|
||||||
// Non negative return values are expected to be used as an exit code for a
|
// Non negative return values are expected to be used as an exit code for a
|
||||||
// binary.
|
// binary.
|
||||||
int HandleUsageFlags(std::ostream& out);
|
int HandleUsageFlags(std::ostream& out,
|
||||||
|
absl::string_view program_usage_message);
|
||||||
|
|
||||||
} // namespace flags_internal
|
} // namespace flags_internal
|
||||||
} // namespace absl
|
} // namespace absl
|
||||||
|
|
|
@ -36,6 +36,8 @@ ABSL_FLAG(double, usage_reporting_test_flag_03, 1.03,
|
||||||
ABSL_FLAG(int64_t, usage_reporting_test_flag_04, 1000000000000004L,
|
ABSL_FLAG(int64_t, usage_reporting_test_flag_04, 1000000000000004L,
|
||||||
"usage_reporting_test_flag_04 help message");
|
"usage_reporting_test_flag_04 help message");
|
||||||
|
|
||||||
|
static const char kTestUsageMessage[] = "Custom usage message";
|
||||||
|
|
||||||
struct UDT {
|
struct UDT {
|
||||||
UDT() = default;
|
UDT() = default;
|
||||||
UDT(const UDT&) = default;
|
UDT(const UDT&) = default;
|
||||||
|
@ -83,7 +85,7 @@ class UsageReportingTest : public testing::Test {
|
||||||
using UsageReportingDeathTest = UsageReportingTest;
|
using UsageReportingDeathTest = UsageReportingTest;
|
||||||
|
|
||||||
TEST_F(UsageReportingDeathTest, TestSetProgramUsageMessage) {
|
TEST_F(UsageReportingDeathTest, TestSetProgramUsageMessage) {
|
||||||
EXPECT_EQ(absl::ProgramUsageMessage(), "Custom usage message");
|
EXPECT_EQ(absl::ProgramUsageMessage(), kTestUsageMessage);
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
// TODO(rogeeff): figure out why this does not work on Windows.
|
// TODO(rogeeff): figure out why this does not work on Windows.
|
||||||
|
@ -175,22 +177,22 @@ TEST_F(UsageReportingTest, TestFlagsHelpHRF) {
|
||||||
|
|
||||||
std::stringstream test_buf_01;
|
std::stringstream test_buf_01;
|
||||||
flags::FlagsHelp(test_buf_01, "usage_test.cc",
|
flags::FlagsHelp(test_buf_01, "usage_test.cc",
|
||||||
flags::HelpFormat::kHumanReadable);
|
flags::HelpFormat::kHumanReadable, kTestUsageMessage);
|
||||||
EXPECT_EQ(test_buf_01.str(), usage_test_flags_out);
|
EXPECT_EQ(test_buf_01.str(), usage_test_flags_out);
|
||||||
|
|
||||||
std::stringstream test_buf_02;
|
std::stringstream test_buf_02;
|
||||||
flags::FlagsHelp(test_buf_02, "flags/internal/usage_test.cc",
|
flags::FlagsHelp(test_buf_02, "flags/internal/usage_test.cc",
|
||||||
flags::HelpFormat::kHumanReadable);
|
flags::HelpFormat::kHumanReadable, kTestUsageMessage);
|
||||||
EXPECT_EQ(test_buf_02.str(), usage_test_flags_out);
|
EXPECT_EQ(test_buf_02.str(), usage_test_flags_out);
|
||||||
|
|
||||||
std::stringstream test_buf_03;
|
std::stringstream test_buf_03;
|
||||||
flags::FlagsHelp(test_buf_03, "usage_test",
|
flags::FlagsHelp(test_buf_03, "usage_test", flags::HelpFormat::kHumanReadable,
|
||||||
flags::HelpFormat::kHumanReadable);
|
kTestUsageMessage);
|
||||||
EXPECT_EQ(test_buf_03.str(), usage_test_flags_out);
|
EXPECT_EQ(test_buf_03.str(), usage_test_flags_out);
|
||||||
|
|
||||||
std::stringstream test_buf_04;
|
std::stringstream test_buf_04;
|
||||||
flags::FlagsHelp(test_buf_04, "flags/invalid_file_name.cc",
|
flags::FlagsHelp(test_buf_04, "flags/invalid_file_name.cc",
|
||||||
flags::HelpFormat::kHumanReadable);
|
flags::HelpFormat::kHumanReadable, kTestUsageMessage);
|
||||||
EXPECT_EQ(test_buf_04.str(),
|
EXPECT_EQ(test_buf_04.str(),
|
||||||
R"(usage_test: Custom usage message
|
R"(usage_test: Custom usage message
|
||||||
|
|
||||||
|
@ -198,7 +200,8 @@ TEST_F(UsageReportingTest, TestFlagsHelpHRF) {
|
||||||
)");
|
)");
|
||||||
|
|
||||||
std::stringstream test_buf_05;
|
std::stringstream test_buf_05;
|
||||||
flags::FlagsHelp(test_buf_05, "", flags::HelpFormat::kHumanReadable);
|
flags::FlagsHelp(test_buf_05, "", flags::HelpFormat::kHumanReadable,
|
||||||
|
kTestUsageMessage);
|
||||||
std::string test_out = test_buf_05.str();
|
std::string test_out = test_buf_05.str();
|
||||||
absl::string_view test_out_str(test_out);
|
absl::string_view test_out_str(test_out);
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
|
@ -217,7 +220,7 @@ TEST_F(UsageReportingTest, TestFlagsHelpHRF) {
|
||||||
|
|
||||||
TEST_F(UsageReportingTest, TestNoUsageFlags) {
|
TEST_F(UsageReportingTest, TestNoUsageFlags) {
|
||||||
std::stringstream test_buf;
|
std::stringstream test_buf;
|
||||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf), -1);
|
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
|
@ -226,7 +229,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpshort) {
|
||||||
absl::SetFlag(&FLAGS_helpshort, true);
|
absl::SetFlag(&FLAGS_helpshort, true);
|
||||||
|
|
||||||
std::stringstream test_buf;
|
std::stringstream test_buf;
|
||||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf), 1);
|
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
|
||||||
EXPECT_EQ(test_buf.str(),
|
EXPECT_EQ(test_buf.str(),
|
||||||
R"(usage_test: Custom usage message
|
R"(usage_test: Custom usage message
|
||||||
|
|
||||||
|
@ -250,7 +253,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_help) {
|
||||||
absl::SetFlag(&FLAGS_help, true);
|
absl::SetFlag(&FLAGS_help, true);
|
||||||
|
|
||||||
std::stringstream test_buf;
|
std::stringstream test_buf;
|
||||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf), 1);
|
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
|
||||||
EXPECT_EQ(test_buf.str(),
|
EXPECT_EQ(test_buf.str(),
|
||||||
R"(usage_test: Custom usage message
|
R"(usage_test: Custom usage message
|
||||||
|
|
||||||
|
@ -276,7 +279,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_helppackage) {
|
||||||
absl::SetFlag(&FLAGS_helppackage, true);
|
absl::SetFlag(&FLAGS_helppackage, true);
|
||||||
|
|
||||||
std::stringstream test_buf;
|
std::stringstream test_buf;
|
||||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf), 1);
|
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
|
||||||
EXPECT_EQ(test_buf.str(),
|
EXPECT_EQ(test_buf.str(),
|
||||||
R"(usage_test: Custom usage message
|
R"(usage_test: Custom usage message
|
||||||
|
|
||||||
|
@ -302,10 +305,9 @@ TEST_F(UsageReportingTest, TestUsageFlag_version) {
|
||||||
absl::SetFlag(&FLAGS_version, true);
|
absl::SetFlag(&FLAGS_version, true);
|
||||||
|
|
||||||
std::stringstream test_buf;
|
std::stringstream test_buf;
|
||||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf), 0);
|
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 0);
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
EXPECT_EQ(test_buf.str(),
|
EXPECT_EQ(test_buf.str(), "usage_test\nDebug build (NDEBUG not #defined)\n");
|
||||||
"usage_test\nDebug build (NDEBUG not #defined)\n");
|
|
||||||
#else
|
#else
|
||||||
EXPECT_EQ(test_buf.str(), "usage_test\n");
|
EXPECT_EQ(test_buf.str(), "usage_test\n");
|
||||||
#endif
|
#endif
|
||||||
|
@ -317,7 +319,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_only_check_args) {
|
||||||
absl::SetFlag(&FLAGS_only_check_args, true);
|
absl::SetFlag(&FLAGS_only_check_args, true);
|
||||||
|
|
||||||
std::stringstream test_buf;
|
std::stringstream test_buf;
|
||||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf), 0);
|
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 0);
|
||||||
EXPECT_EQ(test_buf.str(), "");
|
EXPECT_EQ(test_buf.str(), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +329,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
|
||||||
absl::SetFlag(&FLAGS_helpon, "bla-bla");
|
absl::SetFlag(&FLAGS_helpon, "bla-bla");
|
||||||
|
|
||||||
std::stringstream test_buf_01;
|
std::stringstream test_buf_01;
|
||||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf_01), 1);
|
EXPECT_EQ(flags::HandleUsageFlags(test_buf_01, kTestUsageMessage), 1);
|
||||||
EXPECT_EQ(test_buf_01.str(),
|
EXPECT_EQ(test_buf_01.str(),
|
||||||
R"(usage_test: Custom usage message
|
R"(usage_test: Custom usage message
|
||||||
|
|
||||||
|
@ -337,7 +339,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
|
||||||
absl::SetFlag(&FLAGS_helpon, "usage_test");
|
absl::SetFlag(&FLAGS_helpon, "usage_test");
|
||||||
|
|
||||||
std::stringstream test_buf_02;
|
std::stringstream test_buf_02;
|
||||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf_02), 1);
|
EXPECT_EQ(flags::HandleUsageFlags(test_buf_02, kTestUsageMessage), 1);
|
||||||
EXPECT_EQ(test_buf_02.str(),
|
EXPECT_EQ(test_buf_02.str(),
|
||||||
R"(usage_test: Custom usage message
|
R"(usage_test: Custom usage message
|
||||||
|
|
||||||
|
@ -362,7 +364,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
absl::GetFlag(FLAGS_undefok); // Force linking of parse.cc
|
absl::GetFlag(FLAGS_undefok); // Force linking of parse.cc
|
||||||
flags::SetProgramInvocationName("usage_test");
|
flags::SetProgramInvocationName("usage_test");
|
||||||
absl::SetProgramUsageMessage("Custom usage message");
|
absl::SetProgramUsageMessage(kTestUsageMessage);
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
|
||||||
return RUN_ALL_TESTS();
|
return RUN_ALL_TESTS();
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "absl/flags/parse.h"
|
#include "absl/flags/parse.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
@ -28,6 +29,7 @@
|
||||||
#include "absl/flags/internal/program_name.h"
|
#include "absl/flags/internal/program_name.h"
|
||||||
#include "absl/flags/internal/registry.h"
|
#include "absl/flags/internal/registry.h"
|
||||||
#include "absl/flags/internal/usage.h"
|
#include "absl/flags/internal/usage.h"
|
||||||
|
#include "absl/flags/usage.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/strip.h"
|
#include "absl/strings/strip.h"
|
||||||
|
@ -280,22 +282,7 @@ void CheckDefaultValuesParsingRoundtrip() {
|
||||||
IGNORE_TYPE(std::vector<std::string>)
|
IGNORE_TYPE(std::vector<std::string>)
|
||||||
#undef IGNORE_TYPE
|
#undef IGNORE_TYPE
|
||||||
|
|
||||||
absl::MutexLock lock(InitFlagIfNecessary(flag));
|
flag->CheckDefaultValueParsingRoundtrip();
|
||||||
|
|
||||||
std::string v = flag->DefaultValue();
|
|
||||||
void* dst = Clone(flag->op, flag->def);
|
|
||||||
std::string error;
|
|
||||||
if (!flags_internal::Parse(flag->marshalling_op, v, dst, &error)) {
|
|
||||||
ABSL_INTERNAL_LOG(
|
|
||||||
FATAL,
|
|
||||||
absl::StrCat("Flag ", flag->Name(), " (from ", flag->Filename(),
|
|
||||||
"): std::string form of default value '", v,
|
|
||||||
"' could not be parsed; error=", error));
|
|
||||||
}
|
|
||||||
|
|
||||||
// We do not compare dst to def since parsing/unparsing may make
|
|
||||||
// small changes, e.g., precision loss for floating point types.
|
|
||||||
Delete(flag->op, dst);
|
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -717,12 +704,14 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
flags_internal::HandleUsageFlags(std::cout);
|
flags_internal::HandleUsageFlags(std::cout,
|
||||||
|
ProgramUsageMessage());
|
||||||
std::exit(1);
|
std::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usage_flag_act == UsageFlagsAction::kHandleUsage) {
|
if (usage_flag_act == UsageFlagsAction::kHandleUsage) {
|
||||||
int exit_code = flags_internal::HandleUsageFlags(std::cout);
|
int exit_code = flags_internal::HandleUsageFlags(
|
||||||
|
std::cout, ProgramUsageMessage());
|
||||||
|
|
||||||
if (exit_code != -1) {
|
if (exit_code != -1) {
|
||||||
std::exit(exit_code);
|
std::exit(exit_code);
|
||||||
|
|
|
@ -288,7 +288,6 @@ TEST(HashValueTest, Strings) {
|
||||||
// Also check that nested types maintain the same hash.
|
// Also check that nested types maintain the same hash.
|
||||||
const WrapInTuple t{};
|
const WrapInTuple t{};
|
||||||
EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
|
EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
|
||||||
//
|
|
||||||
t(std::string()), t(absl::string_view()),
|
t(std::string()), t(absl::string_view()),
|
||||||
t(std::string("")), t(absl::string_view("")),
|
t(std::string("")), t(absl::string_view("")),
|
||||||
t(std::string(small)), t(absl::string_view(small)),
|
t(std::string(small)), t(absl::string_view(small)),
|
||||||
|
|
|
@ -640,7 +640,8 @@ class CityHashState : public HashStateBase<CityHashState> {
|
||||||
#endif // ABSL_HAVE_INTRINSIC_INT128
|
#endif // ABSL_HAVE_INTRINSIC_INT128
|
||||||
|
|
||||||
static constexpr uint64_t kMul =
|
static constexpr uint64_t kMul =
|
||||||
sizeof(size_t) == 4 ? uint64_t{0xcc9e2d51} : uint64_t{0x9ddfea08eb382d69};
|
sizeof(size_t) == 4 ? uint64_t{0xcc9e2d51}
|
||||||
|
: uint64_t{0x9ddfea08eb382d69};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using IntegralFastPath =
|
using IntegralFastPath =
|
||||||
|
|
|
@ -19,8 +19,8 @@
|
||||||
//
|
//
|
||||||
// This header file defines 128-bit integer types.
|
// This header file defines 128-bit integer types.
|
||||||
//
|
//
|
||||||
// Currently, this file defines `uint128`, an unsigned 128-bit integer; a signed
|
// Currently, this file defines `uint128`, an unsigned 128-bit integer;
|
||||||
// 128-bit integer is forthcoming.
|
// a signed 128-bit integer is forthcoming.
|
||||||
|
|
||||||
#ifndef ABSL_NUMERIC_INT128_H_
|
#ifndef ABSL_NUMERIC_INT128_H_
|
||||||
#define ABSL_NUMERIC_INT128_H_
|
#define ABSL_NUMERIC_INT128_H_
|
||||||
|
|
|
@ -22,11 +22,18 @@
|
||||||
|
|
||||||
namespace absl {
|
namespace absl {
|
||||||
namespace random_internal {
|
namespace random_internal {
|
||||||
|
// Returns true if the input value is zero or a power of two. Useful for
|
||||||
|
// determining if the range of output values in a URBG
|
||||||
|
template <typename UIntType>
|
||||||
|
constexpr bool IsPowerOfTwoOrZero(UIntType n) {
|
||||||
|
return (n == 0) || ((n & (n - 1)) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
// Computes the length of the range of values producible by the URBG, or returns
|
// Computes the length of the range of values producible by the URBG, or returns
|
||||||
// zero if that would encompass the entire range of representable values in
|
// zero if that would encompass the entire range of representable values in
|
||||||
// URBG::result_type.
|
// URBG::result_type.
|
||||||
template <typename URBG>
|
template <typename URBG>
|
||||||
constexpr typename URBG::result_type constexpr_range() {
|
constexpr typename URBG::result_type RangeSize() {
|
||||||
using result_type = typename URBG::result_type;
|
using result_type = typename URBG::result_type;
|
||||||
return ((URBG::max)() == (std::numeric_limits<result_type>::max)() &&
|
return ((URBG::max)() == (std::numeric_limits<result_type>::max)() &&
|
||||||
(URBG::min)() == std::numeric_limits<result_type>::lowest())
|
(URBG::min)() == std::numeric_limits<result_type>::lowest())
|
||||||
|
@ -34,6 +41,42 @@ constexpr typename URBG::result_type constexpr_range() {
|
||||||
: (URBG::max)() - (URBG::min)() + result_type{1};
|
: (URBG::max)() - (URBG::min)() + result_type{1};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename UIntType>
|
||||||
|
constexpr UIntType LargestPowerOfTwoLessThanOrEqualTo(UIntType n) {
|
||||||
|
return n < 2 ? n : 2 * LargestPowerOfTwoLessThanOrEqualTo(n / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a URBG generating values in the closed interval [Lo, Hi], returns the
|
||||||
|
// largest power of two less than or equal to `Hi - Lo + 1`.
|
||||||
|
template <typename URBG>
|
||||||
|
constexpr typename URBG::result_type PowerOfTwoSubRangeSize() {
|
||||||
|
return LargestPowerOfTwoLessThanOrEqualTo(RangeSize<URBG>());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes the floor of the log. (i.e., std::floor(std::log2(N));
|
||||||
|
template <typename UIntType>
|
||||||
|
constexpr UIntType IntegerLog2(UIntType n) {
|
||||||
|
return (n <= 1) ? 0 : 1 + IntegerLog2(n / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the number of bits of randomness returned through
|
||||||
|
// `PowerOfTwoVariate(urbg)`.
|
||||||
|
template <typename URBG>
|
||||||
|
constexpr size_t NumBits() {
|
||||||
|
return RangeSize<URBG>() == 0
|
||||||
|
? std::numeric_limits<typename URBG::result_type>::digits
|
||||||
|
: IntegerLog2(PowerOfTwoSubRangeSize<URBG>());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a shift value `n`, constructs a mask with exactly the low `n` bits set.
|
||||||
|
// If `n == 0`, all bits are set.
|
||||||
|
template <typename UIntType>
|
||||||
|
constexpr UIntType MaskFromShift(UIntType n) {
|
||||||
|
return ((n % std::numeric_limits<UIntType>::digits) == 0)
|
||||||
|
? ~UIntType{0}
|
||||||
|
: (UIntType{1} << n) - UIntType{1};
|
||||||
|
}
|
||||||
|
|
||||||
// FastUniformBits implements a fast path to acquire uniform independent bits
|
// FastUniformBits implements a fast path to acquire uniform independent bits
|
||||||
// from a type which conforms to the [rand.req.urbg] concept.
|
// from a type which conforms to the [rand.req.urbg] concept.
|
||||||
// Parameterized by:
|
// Parameterized by:
|
||||||
|
@ -45,14 +88,6 @@ constexpr typename URBG::result_type constexpr_range() {
|
||||||
// generator that will outlive the std::independent_bits_engine instance.
|
// generator that will outlive the std::independent_bits_engine instance.
|
||||||
template <typename UIntType = uint64_t>
|
template <typename UIntType = uint64_t>
|
||||||
class FastUniformBits {
|
class FastUniformBits {
|
||||||
static_assert(std::is_unsigned<UIntType>::value,
|
|
||||||
"Class-template FastUniformBits<> must be parameterized using "
|
|
||||||
"an unsigned type.");
|
|
||||||
|
|
||||||
// `kWidth` is the width, in binary digits, of the output. By default it is
|
|
||||||
// the number of binary digits in the `result_type`.
|
|
||||||
static constexpr size_t kWidth = std::numeric_limits<UIntType>::digits;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using result_type = UIntType;
|
using result_type = UIntType;
|
||||||
|
|
||||||
|
@ -65,14 +100,47 @@ class FastUniformBits {
|
||||||
result_type operator()(URBG& g); // NOLINT(runtime/references)
|
result_type operator()(URBG& g); // NOLINT(runtime/references)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Variate() generates a single random variate, always returning a value
|
static_assert(std::is_unsigned<UIntType>::value,
|
||||||
// in the closed interval [0 ... FastUniformBitsURBGConstants::kRangeMask]
|
"Class-template FastUniformBits<> must be parameterized using "
|
||||||
// (kRangeMask+1 is a power of 2).
|
"an unsigned type.");
|
||||||
template <typename URBG>
|
|
||||||
typename URBG::result_type Variate(URBG& g); // NOLINT(runtime/references)
|
|
||||||
|
|
||||||
// generate() generates a random value, dispatched on whether
|
// PowerOfTwoVariate() generates a single random variate, always returning a
|
||||||
// the underlying URNG must loop over multiple calls or not.
|
// value in the half-open interval `[0, PowerOfTwoSubRangeSize<URBG>())`. If
|
||||||
|
// the URBG already generates values in a power-of-two range, the generator
|
||||||
|
// itself is used. Otherwise, we use rejection sampling on the largest
|
||||||
|
// possible power-of-two-sized subrange.
|
||||||
|
struct PowerOfTwoTag {};
|
||||||
|
struct RejectionSamplingTag {};
|
||||||
|
template <typename URBG>
|
||||||
|
static typename URBG::result_type PowerOfTwoVariate(
|
||||||
|
URBG& g) { // NOLINT(runtime/references)
|
||||||
|
using tag =
|
||||||
|
typename std::conditional<IsPowerOfTwoOrZero(RangeSize<URBG>()),
|
||||||
|
PowerOfTwoTag, RejectionSamplingTag>::type;
|
||||||
|
return PowerOfTwoVariate(g, tag{});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename URBG>
|
||||||
|
static typename URBG::result_type PowerOfTwoVariate(
|
||||||
|
URBG& g, // NOLINT(runtime/references)
|
||||||
|
PowerOfTwoTag) {
|
||||||
|
return g() - (URBG::min)();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename URBG>
|
||||||
|
static typename URBG::result_type PowerOfTwoVariate(
|
||||||
|
URBG& g, // NOLINT(runtime/references)
|
||||||
|
RejectionSamplingTag) {
|
||||||
|
// Use rejection sampling to ensure uniformity across the range.
|
||||||
|
typename URBG::result_type u;
|
||||||
|
do {
|
||||||
|
u = g() - (URBG::min)();
|
||||||
|
} while (u >= PowerOfTwoSubRangeSize<URBG>());
|
||||||
|
return u;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate() generates a random value, dispatched on whether
|
||||||
|
// the underlying URBG must loop over multiple calls or not.
|
||||||
template <typename URBG>
|
template <typename URBG>
|
||||||
result_type Generate(URBG& g, // NOLINT(runtime/references)
|
result_type Generate(URBG& g, // NOLINT(runtime/references)
|
||||||
std::true_type /* avoid_looping */);
|
std::true_type /* avoid_looping */);
|
||||||
|
@ -82,196 +150,107 @@ class FastUniformBits {
|
||||||
std::false_type /* avoid_looping */);
|
std::false_type /* avoid_looping */);
|
||||||
};
|
};
|
||||||
|
|
||||||
// FastUniformBitsURBGConstants computes the URBG-derived constants used
|
template <typename UIntType>
|
||||||
// by FastUniformBits::Generate and FastUniformBits::Variate.
|
|
||||||
// Parameterized by the FastUniformBits parameter:
|
|
||||||
// `URBG`: The underlying UniformRandomNumberGenerator.
|
|
||||||
//
|
|
||||||
// The values here indicate the URBG range as well as providing an indicator
|
|
||||||
// whether the URBG output is a power of 2, and kRangeMask, which allows masking
|
|
||||||
// the generated output to kRangeBits.
|
|
||||||
template <typename URBG>
|
template <typename URBG>
|
||||||
class FastUniformBitsURBGConstants {
|
typename FastUniformBits<UIntType>::result_type
|
||||||
// Computes the floor of the log. (i.e., std::floor(std::log2(N));
|
FastUniformBits<UIntType>::operator()(URBG& g) { // NOLINT(runtime/references)
|
||||||
static constexpr size_t constexpr_log2(size_t n) {
|
// kRangeMask is the mask used when sampling variates from the URBG when the
|
||||||
return (n <= 1) ? 0 : 1 + constexpr_log2(n / 2);
|
// width of the URBG range is not a power of 2.
|
||||||
}
|
|
||||||
|
|
||||||
// Computes a mask of n bits for the URBG::result_type.
|
|
||||||
static constexpr typename URBG::result_type constexpr_mask(size_t n) {
|
|
||||||
return (typename URBG::result_type(1) << n) - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
using result_type = typename URBG::result_type;
|
|
||||||
|
|
||||||
// The range of the URNG, max - min + 1, or zero if that result would cause
|
|
||||||
// overflow.
|
|
||||||
static constexpr result_type kRange = constexpr_range<URBG>();
|
|
||||||
|
|
||||||
static constexpr bool kPowerOfTwo =
|
|
||||||
(kRange == 0) || ((kRange & (kRange - 1)) == 0);
|
|
||||||
|
|
||||||
// kRangeBits describes the number number of bits suitable to mask off of URNG
|
|
||||||
// variate, which is:
|
|
||||||
// kRangeBits = floor(log2(kRange))
|
|
||||||
static constexpr size_t kRangeBits =
|
|
||||||
kRange == 0 ? std::numeric_limits<result_type>::digits
|
|
||||||
: constexpr_log2(kRange);
|
|
||||||
|
|
||||||
// kRangeMask is the mask used when sampling variates from the URNG when the
|
|
||||||
// width of the URNG range is not a power of 2.
|
|
||||||
// Y = (2 ^ kRange) - 1
|
// Y = (2 ^ kRange) - 1
|
||||||
static constexpr result_type kRangeMask =
|
static_assert((URBG::max)() > (URBG::min)(),
|
||||||
kRange == 0 ? (std::numeric_limits<result_type>::max)()
|
|
||||||
: constexpr_mask(kRangeBits);
|
|
||||||
|
|
||||||
static_assert((URBG::max)() != (URBG::min)(),
|
|
||||||
"Class-template FastUniformBitsURBGConstants<> "
|
|
||||||
"URBG::max and URBG::min may not be equal.");
|
"URBG::max and URBG::min may not be equal.");
|
||||||
|
|
||||||
static_assert(std::is_unsigned<result_type>::value,
|
|
||||||
"Class-template FastUniformBitsURBGConstants<> "
|
|
||||||
"URBG::result_type must be unsigned.");
|
|
||||||
|
|
||||||
static_assert(kRangeMask > 0,
|
|
||||||
"Class-template FastUniformBitsURBGConstants<> "
|
|
||||||
"URBG does not generate sufficient random bits.");
|
|
||||||
|
|
||||||
static_assert(kRange == 0 ||
|
|
||||||
kRangeBits < std::numeric_limits<result_type>::digits,
|
|
||||||
"Class-template FastUniformBitsURBGConstants<> "
|
|
||||||
"URBG range computation error.");
|
|
||||||
};
|
|
||||||
|
|
||||||
// FastUniformBitsLoopingConstants computes the looping constants used
|
|
||||||
// by FastUniformBits::Generate. These constants indicate how multiple
|
|
||||||
// URBG::result_type values are combined into an output_value.
|
|
||||||
// Parameterized by the FastUniformBits parameters:
|
|
||||||
// `UIntType`: output type.
|
|
||||||
// `URNG`: The underlying UniformRandomNumberGenerator.
|
|
||||||
//
|
|
||||||
// The looping constants describe the sets of loop counters and mask values
|
|
||||||
// which control how individual variates are combined the final output. The
|
|
||||||
// algorithm ensures that the number of bits used by any individual call differs
|
|
||||||
// by at-most one bit from any other call. This is simplified into constants
|
|
||||||
// which describe two loops, with the second loop parameters providing one extra
|
|
||||||
// bit per variate.
|
|
||||||
//
|
|
||||||
// See [rand.adapt.ibits] for more details on the use of these constants.
|
|
||||||
template <typename UIntType, typename URBG>
|
|
||||||
class FastUniformBitsLoopingConstants {
|
|
||||||
private:
|
|
||||||
static constexpr size_t kWidth = std::numeric_limits<UIntType>::digits;
|
|
||||||
using urbg_result_type = typename URBG::result_type;
|
using urbg_result_type = typename URBG::result_type;
|
||||||
using uint_result_type = UIntType;
|
constexpr urbg_result_type kRangeMask =
|
||||||
|
RangeSize<URBG>() == 0
|
||||||
public:
|
? (std::numeric_limits<urbg_result_type>::max)()
|
||||||
using result_type =
|
: static_cast<urbg_result_type>(PowerOfTwoSubRangeSize<URBG>() - 1);
|
||||||
typename std::conditional<(sizeof(urbg_result_type) <=
|
return Generate(g, std::integral_constant<bool, (kRangeMask >= (max)())>{});
|
||||||
sizeof(uint_result_type)),
|
|
||||||
uint_result_type, urbg_result_type>::type;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Estimate N as ceil(width / urng width), and W0 as (width / N).
|
|
||||||
static constexpr size_t kRangeBits =
|
|
||||||
FastUniformBitsURBGConstants<URBG>::kRangeBits;
|
|
||||||
|
|
||||||
// The range of the URNG, max - min + 1, or zero if that result would cause
|
|
||||||
// overflow.
|
|
||||||
static constexpr result_type kRange = constexpr_range<URBG>();
|
|
||||||
static constexpr size_t kEstimateN =
|
|
||||||
kWidth / kRangeBits + (kWidth % kRangeBits != 0);
|
|
||||||
static constexpr size_t kEstimateW0 = kWidth / kEstimateN;
|
|
||||||
static constexpr result_type kEstimateY0 = (kRange >> kEstimateW0)
|
|
||||||
<< kEstimateW0;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Parameters for the two loops:
|
|
||||||
// kN0, kN1 are the number of underlying calls required for each loop.
|
|
||||||
// KW0, kW1 are shift widths for each loop.
|
|
||||||
//
|
|
||||||
static constexpr size_t kN1 = (kRange - kEstimateY0) >
|
|
||||||
(kEstimateY0 / kEstimateN)
|
|
||||||
? kEstimateN + 1
|
|
||||||
: kEstimateN;
|
|
||||||
static constexpr size_t kN0 = kN1 - (kWidth % kN1);
|
|
||||||
static constexpr size_t kW0 = kWidth / kN1;
|
|
||||||
static constexpr size_t kW1 = kW0 + 1;
|
|
||||||
|
|
||||||
static constexpr result_type kM0 = (result_type(1) << kW0) - 1;
|
|
||||||
static constexpr result_type kM1 = (result_type(1) << kW1) - 1;
|
|
||||||
|
|
||||||
static_assert(
|
|
||||||
kW0 <= kRangeBits,
|
|
||||||
"Class-template FastUniformBitsLoopingConstants::kW0 too large.");
|
|
||||||
|
|
||||||
static_assert(
|
|
||||||
kW0 > 0,
|
|
||||||
"Class-template FastUniformBitsLoopingConstants::kW0 too small.");
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename UIntType>
|
|
||||||
template <typename URBG>
|
|
||||||
typename FastUniformBits<UIntType>::result_type
|
|
||||||
FastUniformBits<UIntType>::operator()(
|
|
||||||
URBG& g) { // NOLINT(runtime/references)
|
|
||||||
using constants = FastUniformBitsURBGConstants<URBG>;
|
|
||||||
return Generate(
|
|
||||||
g, std::integral_constant<bool, constants::kRangeMask >= (max)()>{});
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename UIntType>
|
|
||||||
template <typename URBG>
|
|
||||||
typename URBG::result_type FastUniformBits<UIntType>::Variate(
|
|
||||||
URBG& g) { // NOLINT(runtime/references)
|
|
||||||
using constants = FastUniformBitsURBGConstants<URBG>;
|
|
||||||
if (constants::kPowerOfTwo) {
|
|
||||||
return g() - (URBG::min)();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use rejection sampling to ensure uniformity across the range.
|
|
||||||
typename URBG::result_type u;
|
|
||||||
do {
|
|
||||||
u = g() - (URBG::min)();
|
|
||||||
} while (u > constants::kRangeMask);
|
|
||||||
return u;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename UIntType>
|
template <typename UIntType>
|
||||||
template <typename URBG>
|
template <typename URBG>
|
||||||
typename FastUniformBits<UIntType>::result_type
|
typename FastUniformBits<UIntType>::result_type
|
||||||
FastUniformBits<UIntType>::Generate(
|
FastUniformBits<UIntType>::Generate(URBG& g, // NOLINT(runtime/references)
|
||||||
URBG& g, // NOLINT(runtime/references)
|
|
||||||
std::true_type /* avoid_looping */) {
|
std::true_type /* avoid_looping */) {
|
||||||
// The width of the result_type is less than than the width of the random bits
|
// The width of the result_type is less than than the width of the random bits
|
||||||
// provided by URNG. Thus, generate a single value and then simply mask off
|
// provided by URBG. Thus, generate a single value and then simply mask off
|
||||||
// the required bits.
|
// the required bits.
|
||||||
return Variate(g) & (max)();
|
|
||||||
|
return PowerOfTwoVariate(g) & (max)();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename UIntType>
|
template <typename UIntType>
|
||||||
template <typename URBG>
|
template <typename URBG>
|
||||||
typename FastUniformBits<UIntType>::result_type
|
typename FastUniformBits<UIntType>::result_type
|
||||||
FastUniformBits<UIntType>::Generate(
|
FastUniformBits<UIntType>::Generate(URBG& g, // NOLINT(runtime/references)
|
||||||
URBG& g, // NOLINT(runtime/references)
|
|
||||||
std::false_type /* avoid_looping */) {
|
std::false_type /* avoid_looping */) {
|
||||||
// The width of the result_type is wider than the number of random bits
|
// See [rand.adapt.ibits] for more details on the constants calculated below.
|
||||||
// provided by URNG. Thus we merge several variates of URNG into the result
|
//
|
||||||
// using a shift and mask. The constants type generates the parameters used
|
// It is preferable to use roughly the same number of bits from each generator
|
||||||
// ensure that the bits are distributed across all the invocations of the
|
// call, however this is only possible when the number of bits provided by the
|
||||||
// underlying URNG.
|
// URBG is a divisor of the number of bits in `result_type`. In all other
|
||||||
using constants = FastUniformBitsLoopingConstants<UIntType, URBG>;
|
// cases, the number of bits used cannot always be the same, but it can be
|
||||||
|
// guaranteed to be off by at most 1. Thus we run two loops, one with a
|
||||||
|
// smaller bit-width size (`kSmallWidth`) and one with a larger width size
|
||||||
|
// (satisfying `kLargeWidth == kSmallWidth + 1`). The loops are run
|
||||||
|
// `kSmallIters` and `kLargeIters` times respectively such
|
||||||
|
// that
|
||||||
|
//
|
||||||
|
// `kTotalWidth == kSmallIters * kSmallWidth
|
||||||
|
// + kLargeIters * kLargeWidth`
|
||||||
|
//
|
||||||
|
// where `kTotalWidth` is the total number of bits in `result_type`.
|
||||||
|
//
|
||||||
|
constexpr size_t kTotalWidth = std::numeric_limits<result_type>::digits;
|
||||||
|
constexpr size_t kUrbgWidth = NumBits<URBG>();
|
||||||
|
constexpr size_t kTotalIters =
|
||||||
|
kTotalWidth / kUrbgWidth + (kTotalWidth % kUrbgWidth != 0);
|
||||||
|
constexpr size_t kSmallWidth = kTotalWidth / kTotalIters;
|
||||||
|
constexpr size_t kLargeWidth = kSmallWidth + 1;
|
||||||
|
//
|
||||||
|
// Because `kLargeWidth == kSmallWidth + 1`, it follows that
|
||||||
|
//
|
||||||
|
// `kTotalWidth == kTotalIters * kSmallWidth + kLargeIters`
|
||||||
|
//
|
||||||
|
// and therefore
|
||||||
|
//
|
||||||
|
// `kLargeIters == kTotalWidth % kSmallWidth`
|
||||||
|
//
|
||||||
|
// Intuitively, each iteration with the large width accounts for one unit
|
||||||
|
// of the remainder when `kTotalWidth` is divided by `kSmallWidth`. As
|
||||||
|
// mentioned above, if the URBG width is a divisor of `kTotalWidth`, then
|
||||||
|
// there would be no need for any large iterations (i.e., one loop would
|
||||||
|
// suffice), and indeed, in this case, `kLargeIters` would be zero.
|
||||||
|
constexpr size_t kLargeIters = kTotalWidth % kSmallWidth;
|
||||||
|
constexpr size_t kSmallIters =
|
||||||
|
(kTotalWidth - (kLargeWidth * kLargeIters)) / kSmallWidth;
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
kTotalWidth == kSmallIters * kSmallWidth + kLargeIters * kLargeWidth,
|
||||||
|
"Error in looping constant calculations.");
|
||||||
|
|
||||||
result_type s = 0;
|
result_type s = 0;
|
||||||
for (size_t n = 0; n < constants::kN0; ++n) {
|
|
||||||
auto u = Variate(g);
|
constexpr size_t kSmallShift = kSmallWidth % kTotalWidth;
|
||||||
s = (s << constants::kW0) + (u & constants::kM0);
|
constexpr result_type kSmallMask = MaskFromShift(result_type{kSmallShift});
|
||||||
|
for (size_t n = 0; n < kSmallIters; ++n) {
|
||||||
|
s = (s << kSmallShift) +
|
||||||
|
(static_cast<result_type>(PowerOfTwoVariate(g)) & kSmallMask);
|
||||||
}
|
}
|
||||||
for (size_t n = constants::kN0; n < constants::kN1; ++n) {
|
|
||||||
auto u = Variate(g);
|
constexpr size_t kLargeShift = kLargeWidth % kTotalWidth;
|
||||||
s = (s << constants::kW1) + (u & constants::kM1);
|
constexpr result_type kLargeMask = MaskFromShift(result_type{kLargeShift});
|
||||||
|
for (size_t n = 0; n < kLargeIters; ++n) {
|
||||||
|
s = (s << kLargeShift) +
|
||||||
|
(static_cast<result_type>(PowerOfTwoVariate(g)) & kLargeMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
kLargeShift == kSmallShift + 1 ||
|
||||||
|
(kLargeShift == 0 &&
|
||||||
|
kSmallShift == std::numeric_limits<result_type>::digits - 1),
|
||||||
|
"Error in looping constant calculations");
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
namespace absl {
|
||||||
|
namespace random_internal {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
template <typename IntType>
|
template <typename IntType>
|
||||||
|
@ -29,7 +31,7 @@ TYPED_TEST_SUITE(FastUniformBitsTypedTest, IntTypes);
|
||||||
|
|
||||||
TYPED_TEST(FastUniformBitsTypedTest, BasicTest) {
|
TYPED_TEST(FastUniformBitsTypedTest, BasicTest) {
|
||||||
using Limits = std::numeric_limits<TypeParam>;
|
using Limits = std::numeric_limits<TypeParam>;
|
||||||
using FastBits = absl::random_internal::FastUniformBits<TypeParam>;
|
using FastBits = FastUniformBits<TypeParam>;
|
||||||
|
|
||||||
EXPECT_EQ(0, FastBits::min());
|
EXPECT_EQ(0, FastBits::min());
|
||||||
EXPECT_EQ(Limits::max(), FastBits::max());
|
EXPECT_EQ(Limits::max(), FastBits::max());
|
||||||
|
@ -45,91 +47,226 @@ TYPED_TEST(FastUniformBitsTypedTest, BasicTest) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UrngOddbits {
|
template <typename UIntType, UIntType Lo, UIntType Hi, UIntType Val = Lo>
|
||||||
public:
|
struct FakeUrbg {
|
||||||
using result_type = uint8_t;
|
using result_type = UIntType;
|
||||||
static constexpr result_type min() { return 1; }
|
|
||||||
static constexpr result_type max() { return 0xfe; }
|
static constexpr result_type(max)() { return Hi; }
|
||||||
result_type operator()() { return 2; }
|
static constexpr result_type(min)() { return Lo; }
|
||||||
|
result_type operator()() { return Val; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class Urng4bits {
|
using UrngOddbits = FakeUrbg<uint8_t, 1, 0xfe, 0x73>;
|
||||||
public:
|
using Urng4bits = FakeUrbg<uint8_t, 1, 0x10, 2>;
|
||||||
using result_type = uint8_t;
|
using Urng31bits = FakeUrbg<uint32_t, 1, 0xfffffffe, 0x60070f03>;
|
||||||
static constexpr result_type min() { return 1; }
|
using Urng32bits = FakeUrbg<uint32_t, 0, 0xffffffff, 0x74010f01>;
|
||||||
static constexpr result_type max() { return 0xf + 1; }
|
|
||||||
result_type operator()() { return 2; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class Urng32bits {
|
TEST(FastUniformBitsTest, IsPowerOfTwoOrZero) {
|
||||||
public:
|
EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{0}));
|
||||||
using result_type = uint32_t;
|
EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{1}));
|
||||||
static constexpr result_type min() { return 0; }
|
EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{2}));
|
||||||
static constexpr result_type max() { return 0xffffffff; }
|
EXPECT_FALSE(IsPowerOfTwoOrZero(uint8_t{3}));
|
||||||
result_type operator()() { return 1; }
|
EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{16}));
|
||||||
};
|
EXPECT_FALSE(IsPowerOfTwoOrZero(uint8_t{17}));
|
||||||
|
EXPECT_FALSE(IsPowerOfTwoOrZero((std::numeric_limits<uint8_t>::max)()));
|
||||||
|
|
||||||
// Compile-time test to validate the helper classes used by FastUniformBits
|
EXPECT_TRUE(IsPowerOfTwoOrZero(uint16_t{0}));
|
||||||
TEST(FastUniformBitsTest, FastUniformBitsDetails) {
|
EXPECT_TRUE(IsPowerOfTwoOrZero(uint16_t{1}));
|
||||||
using absl::random_internal::FastUniformBitsLoopingConstants;
|
EXPECT_TRUE(IsPowerOfTwoOrZero(uint16_t{2}));
|
||||||
using absl::random_internal::FastUniformBitsURBGConstants;
|
EXPECT_FALSE(IsPowerOfTwoOrZero(uint16_t{3}));
|
||||||
|
EXPECT_TRUE(IsPowerOfTwoOrZero(uint16_t{16}));
|
||||||
|
EXPECT_FALSE(IsPowerOfTwoOrZero(uint16_t{17}));
|
||||||
|
EXPECT_FALSE(IsPowerOfTwoOrZero((std::numeric_limits<uint16_t>::max)()));
|
||||||
|
|
||||||
// 4-bit URBG
|
EXPECT_TRUE(IsPowerOfTwoOrZero(uint32_t{0}));
|
||||||
{
|
EXPECT_TRUE(IsPowerOfTwoOrZero(uint32_t{1}));
|
||||||
using constants = FastUniformBitsURBGConstants<Urng4bits>;
|
EXPECT_TRUE(IsPowerOfTwoOrZero(uint32_t{2}));
|
||||||
static_assert(constants::kPowerOfTwo == true,
|
EXPECT_FALSE(IsPowerOfTwoOrZero(uint32_t{3}));
|
||||||
"constants::kPowerOfTwo == false");
|
EXPECT_TRUE(IsPowerOfTwoOrZero(uint32_t{32}));
|
||||||
static_assert(constants::kRange == 16, "constants::kRange == false");
|
EXPECT_FALSE(IsPowerOfTwoOrZero(uint32_t{17}));
|
||||||
static_assert(constants::kRangeBits == 4, "constants::kRangeBits == false");
|
EXPECT_FALSE(IsPowerOfTwoOrZero((std::numeric_limits<uint32_t>::max)()));
|
||||||
static_assert(constants::kRangeMask == 0x0f,
|
|
||||||
"constants::kRangeMask == false");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ~7-bit URBG
|
EXPECT_TRUE(IsPowerOfTwoOrZero(uint64_t{0}));
|
||||||
{
|
EXPECT_TRUE(IsPowerOfTwoOrZero(uint64_t{1}));
|
||||||
using constants = FastUniformBitsURBGConstants<UrngOddbits>;
|
EXPECT_TRUE(IsPowerOfTwoOrZero(uint64_t{2}));
|
||||||
static_assert(constants::kPowerOfTwo == false,
|
EXPECT_FALSE(IsPowerOfTwoOrZero(uint64_t{3}));
|
||||||
"constants::kPowerOfTwo == false");
|
EXPECT_TRUE(IsPowerOfTwoOrZero(uint64_t{64}));
|
||||||
static_assert(constants::kRange == 0xfe, "constants::kRange == 0xfe");
|
EXPECT_FALSE(IsPowerOfTwoOrZero(uint64_t{17}));
|
||||||
static_assert(constants::kRangeBits == 7, "constants::kRangeBits == 7");
|
EXPECT_FALSE(IsPowerOfTwoOrZero((std::numeric_limits<uint64_t>::max)()));
|
||||||
static_assert(constants::kRangeMask == 0x7f,
|
}
|
||||||
"constants::kRangeMask == 0x7f");
|
|
||||||
}
|
TEST(FastUniformBitsTest, IntegerLog2) {
|
||||||
|
EXPECT_EQ(IntegerLog2(uint16_t{0}), 0);
|
||||||
|
EXPECT_EQ(IntegerLog2(uint16_t{1}), 0);
|
||||||
|
EXPECT_EQ(IntegerLog2(uint16_t{2}), 1);
|
||||||
|
EXPECT_EQ(IntegerLog2(uint16_t{3}), 1);
|
||||||
|
EXPECT_EQ(IntegerLog2(uint16_t{4}), 2);
|
||||||
|
EXPECT_EQ(IntegerLog2(uint16_t{5}), 2);
|
||||||
|
EXPECT_EQ(IntegerLog2(std::numeric_limits<uint64_t>::max()), 63);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FastUniformBitsTest, RangeSize) {
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint8_t, 0, 3>>()), 4);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint8_t, 2, 2>>()), 1);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint8_t, 2, 5>>()), 4);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint8_t, 2, 6>>()), 5);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint8_t, 2, 10>>()), 9);
|
||||||
|
EXPECT_EQ(
|
||||||
|
(RangeSize<FakeUrbg<uint8_t, 0, std::numeric_limits<uint8_t>::max()>>()),
|
||||||
|
0);
|
||||||
|
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint16_t, 0, 3>>()), 4);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint16_t, 2, 2>>()), 1);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint16_t, 2, 5>>()), 4);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint16_t, 2, 6>>()), 5);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint16_t, 1000, 1017>>()), 18);
|
||||||
|
EXPECT_EQ((RangeSize<
|
||||||
|
FakeUrbg<uint16_t, 0, std::numeric_limits<uint16_t>::max()>>()),
|
||||||
|
0);
|
||||||
|
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 0, 3>>()), 4);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 2, 2>>()), 1);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 2, 5>>()), 4);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 2, 6>>()), 5);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 1000, 1017>>()), 18);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 0, 0xffffffff>>()), 0);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 1, 0xffffffff>>()), 0xffffffff);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 1, 0xfffffffe>>()), 0xfffffffe);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 2, 0xfffffffe>>()), 0xfffffffd);
|
||||||
|
EXPECT_EQ((RangeSize<
|
||||||
|
FakeUrbg<uint32_t, 0, std::numeric_limits<uint32_t>::max()>>()),
|
||||||
|
0);
|
||||||
|
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 0, 3>>()), 4);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 2, 2>>()), 1);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 2, 5>>()), 4);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 2, 6>>()), 5);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 1000, 1017>>()), 18);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 0, 0xffffffff>>()), 0x100000000ull);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 1, 0xffffffff>>()), 0xffffffffull);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 1, 0xfffffffe>>()), 0xfffffffeull);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 2, 0xfffffffe>>()), 0xfffffffdull);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 0, 0xffffffffffffffffull>>()), 0ull);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 1, 0xffffffffffffffffull>>()),
|
||||||
|
0xffffffffffffffffull);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 1, 0xfffffffffffffffeull>>()),
|
||||||
|
0xfffffffffffffffeull);
|
||||||
|
EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 2, 0xfffffffffffffffeull>>()),
|
||||||
|
0xfffffffffffffffdull);
|
||||||
|
EXPECT_EQ((RangeSize<
|
||||||
|
FakeUrbg<uint64_t, 0, std::numeric_limits<uint64_t>::max()>>()),
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FastUniformBitsTest, PowerOfTwoSubRangeSize) {
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint8_t, 0, 3>>()), 4);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint8_t, 2, 2>>()), 1);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint8_t, 2, 5>>()), 4);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint8_t, 2, 6>>()), 4);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint8_t, 2, 10>>()), 8);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<
|
||||||
|
FakeUrbg<uint8_t, 0, std::numeric_limits<uint8_t>::max()>>()),
|
||||||
|
0);
|
||||||
|
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint16_t, 0, 3>>()), 4);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint16_t, 2, 2>>()), 1);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint16_t, 2, 5>>()), 4);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint16_t, 2, 6>>()), 4);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint16_t, 1000, 1017>>()), 16);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<
|
||||||
|
FakeUrbg<uint16_t, 0, std::numeric_limits<uint16_t>::max()>>()),
|
||||||
|
0);
|
||||||
|
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 0, 3>>()), 4);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 2, 2>>()), 1);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 2, 5>>()), 4);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 2, 6>>()), 4);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 1000, 1017>>()), 16);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 0, 0xffffffff>>()), 0);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 1, 0xffffffff>>()),
|
||||||
|
0x80000000);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 1, 0xfffffffe>>()),
|
||||||
|
0x80000000);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<
|
||||||
|
FakeUrbg<uint32_t, 0, std::numeric_limits<uint32_t>::max()>>()),
|
||||||
|
0);
|
||||||
|
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 0, 3>>()), 4);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 2, 2>>()), 1);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 2, 5>>()), 4);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 2, 6>>()), 4);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 1000, 1017>>()), 16);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 0, 0xffffffff>>()),
|
||||||
|
0x100000000ull);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 1, 0xffffffff>>()),
|
||||||
|
0x80000000ull);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 1, 0xfffffffe>>()),
|
||||||
|
0x80000000ull);
|
||||||
|
EXPECT_EQ(
|
||||||
|
(PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 0, 0xffffffffffffffffull>>()),
|
||||||
|
0);
|
||||||
|
EXPECT_EQ(
|
||||||
|
(PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 1, 0xffffffffffffffffull>>()),
|
||||||
|
0x8000000000000000ull);
|
||||||
|
EXPECT_EQ(
|
||||||
|
(PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 1, 0xfffffffffffffffeull>>()),
|
||||||
|
0x8000000000000000ull);
|
||||||
|
EXPECT_EQ((PowerOfTwoSubRangeSize<
|
||||||
|
FakeUrbg<uint64_t, 0, std::numeric_limits<uint64_t>::max()>>()),
|
||||||
|
0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FastUniformBitsTest, Urng4_VariousOutputs) {
|
TEST(FastUniformBitsTest, Urng4_VariousOutputs) {
|
||||||
// Tests that how values are composed; the single-bit deltas should be spread
|
// Tests that how values are composed; the single-bit deltas should be spread
|
||||||
// across each invocation.
|
// across each invocation.
|
||||||
Urng4bits urng4;
|
Urng4bits urng4;
|
||||||
|
Urng31bits urng31;
|
||||||
Urng32bits urng32;
|
Urng32bits urng32;
|
||||||
|
|
||||||
// 8-bit types
|
// 8-bit types
|
||||||
{
|
{
|
||||||
absl::random_internal::FastUniformBits<uint8_t> fast8;
|
FastUniformBits<uint8_t> fast8;
|
||||||
EXPECT_EQ(0x11, fast8(urng4));
|
EXPECT_EQ(0x11, fast8(urng4));
|
||||||
|
EXPECT_EQ(0x2, fast8(urng31));
|
||||||
EXPECT_EQ(0x1, fast8(urng32));
|
EXPECT_EQ(0x1, fast8(urng32));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 16-bit types
|
// 16-bit types
|
||||||
{
|
{
|
||||||
absl::random_internal::FastUniformBits<uint16_t> fast16;
|
FastUniformBits<uint16_t> fast16;
|
||||||
EXPECT_EQ(0x1111, fast16(urng4));
|
EXPECT_EQ(0x1111, fast16(urng4));
|
||||||
EXPECT_EQ(0x1, fast16(urng32));
|
EXPECT_EQ(0xf02, fast16(urng31));
|
||||||
|
EXPECT_EQ(0xf01, fast16(urng32));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 32-bit types
|
// 32-bit types
|
||||||
{
|
{
|
||||||
absl::random_internal::FastUniformBits<uint32_t> fast32;
|
FastUniformBits<uint32_t> fast32;
|
||||||
EXPECT_EQ(0x11111111, fast32(urng4));
|
EXPECT_EQ(0x11111111, fast32(urng4));
|
||||||
EXPECT_EQ(0x1, fast32(urng32));
|
EXPECT_EQ(0x0f020f02, fast32(urng31));
|
||||||
|
EXPECT_EQ(0x74010f01, fast32(urng32));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 64-bit types
|
// 64-bit types
|
||||||
{
|
{
|
||||||
absl::random_internal::FastUniformBits<uint64_t> fast64;
|
FastUniformBits<uint64_t> fast64;
|
||||||
EXPECT_EQ(0x1111111111111111, fast64(urng4));
|
EXPECT_EQ(0x1111111111111111, fast64(urng4));
|
||||||
EXPECT_EQ(0x0000000100000001, fast64(urng32));
|
EXPECT_EQ(0x387811c3c0870f02, fast64(urng31));
|
||||||
|
EXPECT_EQ(0x74010f0174010f01, fast64(urng32));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(FastUniformBitsTest, URBG32bitRegression) {
|
||||||
|
// Validate with deterministic 32-bit std::minstd_rand
|
||||||
|
// to ensure that operator() performs as expected.
|
||||||
|
std::minstd_rand gen(1);
|
||||||
|
FastUniformBits<uint64_t> fast64;
|
||||||
|
|
||||||
|
EXPECT_EQ(0x05e47095f847c122ull, fast64(gen));
|
||||||
|
EXPECT_EQ(0x8f82c1ba30b64d22ull, fast64(gen));
|
||||||
|
EXPECT_EQ(0x3b971a3558155039ull, fast64(gen));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
} // namespace random_internal
|
||||||
|
} // namespace absl
|
||||||
|
|
|
@ -275,7 +275,8 @@ std::string StrJoin(const Range& range, absl::string_view separator) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::string StrJoin(std::initializer_list<T> il, absl::string_view separator) {
|
std::string StrJoin(std::initializer_list<T> il,
|
||||||
|
absl::string_view separator) {
|
||||||
return strings_internal::JoinRange(il, separator);
|
return strings_internal::JoinRange(il, separator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,8 @@ namespace synchronization_internal {
|
||||||
|
|
||||||
// ThreadIdentity storage is persistent, we maintain a free-list of previously
|
// ThreadIdentity storage is persistent, we maintain a free-list of previously
|
||||||
// released ThreadIdentity objects.
|
// released ThreadIdentity objects.
|
||||||
static base_internal::SpinLock freelist_lock(base_internal::kLinkerInitialized);
|
static base_internal::SpinLock freelist_lock(
|
||||||
|
base_internal::kLinkerInitialized);
|
||||||
static base_internal::ThreadIdentity* thread_identity_freelist;
|
static base_internal::ThreadIdentity* thread_identity_freelist;
|
||||||
|
|
||||||
// A per-thread destructor for reclaiming associated ThreadIdentity objects.
|
// A per-thread destructor for reclaiming associated ThreadIdentity objects.
|
||||||
|
|
|
@ -179,6 +179,7 @@ class Duration {
|
||||||
Duration& operator%=(Duration rhs);
|
Duration& operator%=(Duration rhs);
|
||||||
|
|
||||||
// Overloads that forward to either the int64_t or double overloads above.
|
// Overloads that forward to either the int64_t or double overloads above.
|
||||||
|
// Integer operands must be representable as int64_t.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
Duration& operator*=(T r) {
|
Duration& operator*=(T r) {
|
||||||
int64_t x = r;
|
int64_t x = r;
|
||||||
|
@ -221,6 +222,7 @@ inline Duration operator+(Duration lhs, Duration rhs) { return lhs += rhs; }
|
||||||
inline Duration operator-(Duration lhs, Duration rhs) { return lhs -= rhs; }
|
inline Duration operator-(Duration lhs, Duration rhs) { return lhs -= rhs; }
|
||||||
|
|
||||||
// Multiplicative Operators
|
// Multiplicative Operators
|
||||||
|
// Integer operands must be representable as int64_t.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
Duration operator*(Duration lhs, T rhs) {
|
Duration operator*(Duration lhs, T rhs) {
|
||||||
return lhs *= rhs;
|
return lhs *= rhs;
|
||||||
|
@ -375,7 +377,8 @@ constexpr Duration InfiniteDuration();
|
||||||
// Hours()
|
// Hours()
|
||||||
//
|
//
|
||||||
// Factory functions for constructing `Duration` values from an integral number
|
// Factory functions for constructing `Duration` values from an integral number
|
||||||
// of the unit indicated by the factory function's name.
|
// of the unit indicated by the factory function's name. The number must be
|
||||||
|
// representable as int64_t.
|
||||||
//
|
//
|
||||||
// Note: no "Days()" factory function exists because "a day" is ambiguous.
|
// Note: no "Days()" factory function exists because "a day" is ambiguous.
|
||||||
// Civil days are not always 24 hours long, and a 24-hour duration often does
|
// Civil days are not always 24 hours long, and a 24-hour duration often does
|
||||||
|
|
|
@ -67,13 +67,21 @@ for std in ${STD}; do
|
||||||
--compilation_mode=${compilation_mode} \
|
--compilation_mode=${compilation_mode} \
|
||||||
--copt="-DDYNAMIC_ANNOTATIONS_ENABLED=1" \
|
--copt="-DDYNAMIC_ANNOTATIONS_ENABLED=1" \
|
||||||
--copt="-DADDRESS_SANITIZER" \
|
--copt="-DADDRESS_SANITIZER" \
|
||||||
|
--copt="-DUNDEFINED_BEHAVIOR_SANITIZER" \
|
||||||
--copt="-fsanitize=address" \
|
--copt="-fsanitize=address" \
|
||||||
|
--copt="-fsanitize=float-divide-by-zero" \
|
||||||
|
--copt="-fsanitize=nullability" \
|
||||||
|
--copt="-fsanitize=undefined" \
|
||||||
|
--copt="-fno-sanitize=vptr" \
|
||||||
--copt=-Werror \
|
--copt=-Werror \
|
||||||
--keep_going \
|
--keep_going \
|
||||||
--linkopt="-fsanitize=address" \
|
--linkopt="-fsanitize=address" \
|
||||||
|
--linkopt="-fsanitize-link-c++-runtime" \
|
||||||
--show_timestamps \
|
--show_timestamps \
|
||||||
--test_env="ASAN_SYMBOLIZER_PATH=/opt/llvm/clang/bin/llvm-symbolizer" \
|
--test_env="ASAN_SYMBOLIZER_PATH=/opt/llvm/clang/bin/llvm-symbolizer" \
|
||||||
--test_env="TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo" \
|
--test_env="TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo" \
|
||||||
|
--test_env="UBSAN_OPTIONS=print_stacktrace=1" \
|
||||||
|
--test_env="UBSAN_SYMBOLIZER_PATH=/opt/llvm/clang/bin/llvm-symbolizer" \
|
||||||
--test_output=errors \
|
--test_output=errors \
|
||||||
--test_tag_filters="-benchmark,-noasan" \
|
--test_tag_filters="-benchmark,-noasan" \
|
||||||
${BAZEL_EXTRA_ARGS:-}
|
${BAZEL_EXTRA_ARGS:-}
|
||||||
|
|
Loading…
Reference in a new issue