Commit c6c3c1b4 by Abseil Team Committed by Derek Mauro

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
parent 44efe96d
......@@ -62,7 +62,6 @@ static SpinLock static_cooperative_spinlock(
static SpinLock static_noncooperative_spinlock(
base_internal::kLinkerInitialized, base_internal::SCHEDULE_KERNEL_ONLY);
// Simple integer hash function based on the public domain lookup2 hash.
// http://burtleburtle.net/bob/c/lookup2.c
static uint32_t Hash32(uint32_t a, uint32_t c) {
......@@ -194,6 +193,7 @@ TEST(SpinLock, WaitCyclesEncoding) {
TEST(SpinLockWithThreads, StaticSpinLock) {
ThreadedTest(&static_spinlock);
}
TEST(SpinLockWithThreads, StackSpinLock) {
SpinLock spinlock;
ThreadedTest(&spinlock);
......
......@@ -44,7 +44,10 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":compressed_tuple",
":test_instance_tracker",
"//absl/memory",
"//absl/types:any",
"//absl/types:optional",
"//absl/utility",
"@com_google_googletest//:gtest_main",
],
......
......@@ -43,8 +43,11 @@ absl_cc_test(
COPTS
${ABSL_TEST_COPTS}
DEPS
absl::any
absl::compressed_tuple
absl::memory
absl::optional
absl::test_instance_tracker
absl::utility
gmock_main
)
......
......@@ -77,10 +77,18 @@ class node_handle_base {
protected:
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);
}
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() {
if (!empty()) {
PolicyTraits::destroy(alloc(), slot());
......@@ -121,7 +129,7 @@ class node_handle : public node_handle_base<PolicyTraits, Alloc> {
private:
friend struct CommonAccess;
node_handle(const Alloc& a, typename Base::slot_type* s) : Base(a, s) {}
using Base::Base;
};
// For maps.
......@@ -148,7 +156,7 @@ class node_handle<Policy, PolicyTraits, Alloc,
private:
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.
......@@ -164,8 +172,13 @@ struct CommonAccess {
}
template <typename T, typename... Args>
static T Make(Args&&... args) {
return T(std::forward<Args>(args)...);
static T Transfer(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 {
T value;
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; }
T& get() & { return value; }
constexpr const T&& get() const&& { return absl::move(*this).value; }
......@@ -112,7 +114,11 @@ struct Storage {
template <typename T, size_t I>
struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC Storage<T, I, true> : T {
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; }
T& get() & { return *this; }
constexpr const T&& get() const&& { return absl::move(*this); }
......@@ -132,8 +138,9 @@ struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl<
: uses_inheritance,
Storage<Ts, std::integral_constant<size_t, I>::value>... {
constexpr CompressedTupleImpl() = default;
explicit constexpr CompressedTupleImpl(Ts&&... args)
: Storage<Ts, I>(absl::forward<Ts>(args))... {}
template <typename... Vs>
explicit constexpr CompressedTupleImpl(absl::in_place_t, Vs&&... args)
: Storage<Ts, I>(absl::in_place, absl::forward<Vs>(args))... {}
friend CompressedTuple<Ts...>;
};
......@@ -143,8 +150,9 @@ struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl<
// We use the dummy identity function as above...
: Storage<Ts, std::integral_constant<size_t, I>::value, false>... {
constexpr CompressedTupleImpl() = default;
explicit constexpr CompressedTupleImpl(Ts&&... args)
: Storage<Ts, I, false>(absl::forward<Ts>(args))... {}
template <typename... Vs>
explicit constexpr CompressedTupleImpl(absl::in_place_t, Vs&&... args)
: Storage<Ts, I, false>(absl::in_place, absl::forward<Vs>(args))... {}
friend CompressedTuple<Ts...>;
};
......@@ -159,6 +167,11 @@ constexpr bool ShouldAnyUseBase() {
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
// 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>;
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;
explicit constexpr CompressedTuple(Ts... base)
: CompressedTuple::CompressedTupleImpl(absl::forward<Ts>(base)...) {}
#endif
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>
ElemT<I>& get() & {
......
......@@ -19,7 +19,10 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/container/internal/test_instance_tracker.h"
#include "absl/memory/memory.h"
#include "absl/types/any.h"
#include "absl/types/optional.h"
#include "absl/utility/utility.h"
// These are declared at global scope purely so that error messages
......@@ -43,10 +46,14 @@ struct TwoValues {
U value2;
};
namespace absl {
namespace container_internal {
namespace {
using absl::test_internal::CopyableMovableInstance;
using absl::test_internal::InstanceTracker;
TEST(CompressedTupleTest, Sizeof) {
EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int>));
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>>));
}
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) {
struct S {
std::string x;
......@@ -173,7 +315,40 @@ TEST(CompressedTupleTest, MoveOnlyElements) {
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) {
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(
7, 1.25, CompressedTuple<int>(5), {});
constexpr int x0 = x.get<0>();
......@@ -186,6 +361,32 @@ TEST(CompressedTupleTest, Constexpr) {
EXPECT_EQ(x2, 5);
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__)
// An apparent bug in earlier versions of gcc claims these are ambiguous.
constexpr int x2m = absl::move(x.get<2>()).get<0>();
......
......@@ -32,7 +32,7 @@ constexpr int HashtablezInfo::kMaxStackDepth;
namespace {
ABSL_CONST_INIT std::atomic<bool> g_hashtablez_enabled{
false
false
};
ABSL_CONST_INIT std::atomic<int32_t> g_hashtablez_sample_parameter{1 << 10};
ABSL_CONST_INIT std::atomic<int32_t> g_hashtablez_max_samples{1 << 20};
......
......@@ -1182,7 +1182,7 @@ class raw_hash_set {
node_type extract(const_iterator position) {
auto node =
CommonAccess::Make<node_type>(alloc_ref(), position.inner_.slot_);
CommonAccess::Transfer<node_type>(alloc_ref(), position.inner_.slot_);
erase_meta_only(position);
return node;
}
......
......@@ -23,7 +23,7 @@
namespace absl {
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
// 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
......
......@@ -155,14 +155,12 @@ cc_library(
)
cc_library(
name = "usage",
name = "usage_internal",
srcs = [
"internal/usage.cc",
"usage.cc",
],
hdrs = [
"internal/usage.h",
"usage.h",
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
......@@ -180,6 +178,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(
name = "parse",
srcs = ["parse.cc"],
hdrs = [
......@@ -195,6 +210,7 @@ cc_library(
":internal",
":registry",
":usage",
":usage_internal",
"//absl/strings",
"//absl/synchronization",
],
......@@ -360,6 +376,7 @@ cc_test(
":internal",
":parse",
":usage",
":usage_internal",
"//absl/memory",
"//absl/strings",
"@com_google_googletest//:gtest",
......
......@@ -141,13 +141,11 @@ absl_cc_library(
# Internal-only target, do not depend on directly.
absl_cc_library(
NAME
flags_usage
flags_usage_internal
SRCS
"internal/usage.cc"
"usage.cc"
HDRS
"internal/usage.h"
"usage.h"
COPTS
${ABSL_DEFAULT_COPTS}
LINKOPTS
......@@ -163,6 +161,23 @@ absl_cc_library(
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(
NAME
flags_parse
SRCS
"parse.cc"
......
......@@ -24,24 +24,16 @@ namespace absl {
// so in debug builds we always use the slower implementation, which always
// validates the type.
#ifndef NDEBUG
#define ABSL_FLAGS_ATOMIC_GET(T) \
T GetFlag(const absl::Flag<T>& flag) { \
T result; \
flag.internal.Read(&result, &flags_internal::FlagOps<T>); \
return result; \
}
#define ABSL_FLAGS_ATOMIC_GET(T) \
T GetFlag(const absl::Flag<T>& flag) { return flag.Get(); }
#else
#define ABSL_FLAGS_ATOMIC_GET(T) \
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; \
flag.internal.Read(&result, &flags_internal::FlagOps<T>); \
return result; \
#define ABSL_FLAGS_ATOMIC_GET(T) \
T GetFlag(const absl::Flag<T>& flag) { \
T result; \
if (flag.AtomicGet(&result)) { \
return result; \
} \
return flag.Get(); \
}
#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)
#undef ABSL_FLAGS_INTERNAL_LOCK_FREE_VALIDATE
// Implementation notes:
//
// We are wrapping a union around the value of `T` to serve three purposes:
//
// 1. `U.value` has correct size and alignment for a value of type `T`
// 2. The `U.value` constructor is not invoked since U's constructor does not
// do it explicitly.
// 3. The `U.value` destructor is invoked since U's destructor does it
// explicitly. This makes `U` a kind of RAII wrapper around non default
// constructible value of T, which is destructed when we leave the scope.
// We do need to destroy U.value, which is constructed by
// CommandLineFlag::Read even though we left it in a moved-from state
// after std::move.
//
// All of this serves to avoid requiring `T` being default constructible.
union U {
T value;
U() {}
~U() { value.~T(); }
};
U u;
flag.internal.Read(&u.value, &flags_internal::FlagOps<T>);
return std::move(u.value);
return flag.Get();
}
// 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.
template <typename T>
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
......@@ -141,7 +118,7 @@ void SetFlag(absl::Flag<T>* flag, const T& v) {
template <typename T, typename V>
void SetFlag(absl::Flag<T>* flag, const V& v) {
T value(v);
SetFlag<T>(flag, value);
flag->Set(value);
}
} // namespace absl
......@@ -239,17 +216,17 @@ void SetFlag(absl::Flag<T>* flag, const V& v) {
// Note: Name of registrar object is not arbitrary. It is used to "grab"
// global name for FLAGS_no<flag_name> symbol, thus preventing the possibility
// of defining two flags with names foo and nofoo.
#define ABSL_FLAG_IMPL(Type, name, default_value, help) \
namespace absl {} \
ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \
ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help) \
absl::Flag<Type> FLAGS_##name( \
ABSL_FLAG_IMPL_FLAGNAME(#name), \
&AbslFlagsWrapHelp##name, \
ABSL_FLAG_IMPL_FILENAME(), \
&absl::flags_internal::FlagMarshallingOps<Type>, \
&AbslFlagsInitFlag##name); \
extern bool FLAGS_no##name; \
#define ABSL_FLAG_IMPL(Type, name, default_value, help) \
namespace absl {} \
ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \
ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help) \
absl::Flag<Type> FLAGS_##name( \
ABSL_FLAG_IMPL_FLAGNAME(#name), \
&AbslFlagsWrapHelp##name, \
ABSL_FLAG_IMPL_FILENAME(), \
&absl::flags_internal::FlagMarshallingOps<Type>, \
&AbslFlagsInitFlag##name); \
extern bool FLAGS_no##name; \
bool FLAGS_no##name = ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name)
// ABSL_RETIRED_FLAG
......
......@@ -69,11 +69,15 @@ using HelpGenFunc = std::string (*)();
// based on default value supplied in flag's definition)
using InitialValGenFunc = void* (*)();
struct CommandLineFlagInfo;
// Signature for the mutation callback used by watched Flags
// The callback is noexcept.
// TODO(rogeeff): add noexcept after C++17 support is added.
using FlagCallback = void (*)();
using FlagValidator = bool (*)();
extern const char kStrippedFlagHelp[];
// The per-type function
......@@ -217,6 +221,9 @@ struct CommandLineFlag {
atomic(kAtomicInit),
locks(nullptr) {}
// Revert the init routine.
void Destroy() const;
// Not copyable/assignable.
CommandLineFlag(const CommandLineFlag&) = delete;
CommandLineFlag& operator=(const CommandLineFlag&) = delete;
......@@ -224,7 +231,9 @@ struct CommandLineFlag {
absl::string_view Name() const { return name; }
std::string Help() const { return help.GetHelpText(); }
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.
bool IsAbseilFlag() const {
// Set to null for V1 flags
......@@ -236,6 +245,10 @@ struct CommandLineFlag {
std::string DefaultValue() 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.
template <typename T>
inline bool IsOfType() const {
......@@ -245,7 +258,7 @@ struct CommandLineFlag {
// Attempts to retrieve the flag value. Returns value on success,
// absl::nullopt otherwise.
template <typename T>
absl::optional<T> Get() {
absl::optional<T> Get() const {
if (IsRetired() || flags_internal::FlagOps<T> != this->op)
return absl::nullopt;
......@@ -256,6 +269,7 @@ struct CommandLineFlag {
}
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
// was successfully set to new value, it returns true. Otherwise, sets `error`
......@@ -269,27 +283,35 @@ struct CommandLineFlag {
flags_internal::FlagSettingMode set_mode,
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.
private:
const char* const name;
const HelpText help;
const char* const filename;
public:
const FlagOpFn op; // Type-specific handler
protected:
const FlagOpFn op; // Type-specific handler
const FlagMarshallingOpFn marshalling_op; // Marshalling ops handler
const InitialValGenFunc make_init_value; // Makes initial value for the flag
const bool retired; // Is the flag retired?
std::atomic<bool> inited; // fields have been lazily initialized
const bool retired; // Is the flag retired?
std::atomic<bool> inited; // fields have been lazily initialized
// Mutable state (guarded by locks->primary_mu).
bool modified; // Has flag value been modified?
bool on_command_line; // Specified on command line.
bool (*validator)(); // Validator function, or nullptr
FlagCallback callback; // Mutation callback, or nullptr
void* def; // Lazily initialized pointer to default value
void* cur; // Lazily initialized pointer to current value
int64_t counter; // Mutation counter
bool modified; // Has flag value been modified?
bool on_command_line; // Specified on command line.
FlagValidator validator; // Validator function, or nullptr
FlagCallback callback; // Mutation callback, or nullptr
void* def; // Lazily initialized pointer to default value
void* cur; // Lazily initialized pointer to current value
int64_t counter; // Mutation counter
// For some types, a copy of the current value is kept in an atomically
// accessible field.
......@@ -302,24 +324,26 @@ struct CommandLineFlag {
// TODO(rogeeff): fix it once Mutex has constexpr constructor
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
// based on current flag's value
void Read(void* dst, const flags_internal::FlagOpFn dst_op) const;
// updates flag's value to *src (locked)
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 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.
// 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
......@@ -332,15 +356,9 @@ absl::Mutex* InitFlagIfNecessary(CommandLineFlag* flag);
// 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 UpdateCopy(CommandLineFlag* flag, absl::Mutex* primary_lock);
void UpdateCopy(CommandLineFlag* flag);
// Return true iff flag value was changed via direct-access.
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
// expect to perform lock free operations on. Specifically it generates code,
......
......@@ -100,39 +100,39 @@ TEST_F(CommandLineFlagTest, TestSetFromStringCurrentValue) {
std::string err;
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,
flags::kProgrammaticChange, &err));
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,
flags::kProgrammaticChange, &err));
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,
flags::kProgrammaticChange, &err));
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123);
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,
flags::kProgrammaticChange, &err));
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123);
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,
flags::kProgrammaticChange, &err));
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,
flags::kCommandLine, &err));
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,
flags::kProgrammaticChange, &err));
......
......@@ -24,40 +24,58 @@ namespace flags_internal {
// This is "unspecified" implementation of absl::Flag<T> type.
template <typename T>
class Flag {
class Flag : public flags_internal::CommandLineFlag {
public:
constexpr Flag(const char* name, const flags_internal::HelpGenFunc help_gen,
const char* filename,
const flags_internal::FlagMarshallingOpFn marshalling_op,
const flags_internal::FlagMarshallingOpFn marshalling_op_arg,
const flags_internal::InitialValGenFunc initial_value_gen)
: internal(name, flags_internal::HelpText::FromFunctionPointer(help_gen),
filename, &flags_internal::FlagOps<T>, marshalling_op,
initial_value_gen,
/*retired_arg=*/false, /*def_arg=*/nullptr,
/*cur_arg=*/nullptr) {}
// Not copyable/assignable.
Flag(const Flag<T>&) = delete;
Flag<T>& operator=(const Flag<T>&) = delete;
absl::string_view Name() const { return internal.Name(); }
std::string Help() const { return internal.Help(); }
std::string Filename() const { return internal.Filename(); }
: flags_internal::CommandLineFlag(
name, flags_internal::HelpText::FromFunctionPointer(help_gen),
filename, &flags_internal::FlagOps<T>, marshalling_op_arg,
initial_value_gen,
/*retired_arg=*/false, /*def_arg=*/nullptr,
/*cur_arg=*/nullptr) {}
T Get() const {
// Implementation notes:
//
// We are wrapping a union around the value of `T` to serve three purposes:
//
// 1. `U.value` has correct size and alignment for a value of type `T`
// 2. The `U.value` constructor is not invoked since U's constructor does
// not
// do it explicitly.
// 3. The `U.value` destructor is invoked since U's destructor does it
// explicitly. This makes `U` a kind of RAII wrapper around non default
// constructible value of T, which is destructed when we leave the
// scope. We do need to destroy U.value, which is constructed by
// CommandLineFlag::Read even though we left it in a moved-from state
// after std::move.
//
// All of this serves to avoid requiring `T` being default constructible.
union U {
T value;
U() {}
~U() { value.~T(); }
};
U u;
this->Read(&u.value, &flags_internal::FlagOps<T>);
return std::move(u.value);
}
absl::flags_internal::CommandLineFlag internal;
bool AtomicGet(T* v) const {
const int64_t r = this->atomic.load(std::memory_order_acquire);
if (r != flags_internal::CommandLineFlag::kAtomicInit) {
memcpy(v, &r, sizeof(T));
return true;
}
void SetCallback(const flags_internal::FlagCallback mutation_callback) {
internal.SetCallback(mutation_callback);
return false;
}
private:
// TODO(rogeeff): add these validations once UnparseFlag invocation is fixed
// for built-in types and when we cleanup existing code from operating on
// forward declared types.
// auto IsCopyConstructible(const T& v) -> decltype(T(v));
// auto HasAbslParseFlag(absl::string_view in, T* dst, std::string* err)
// -> decltype(AbslParseFlag(in, dst, err));
// auto HasAbslUnparseFlag(const T& v) -> decltype(AbslUnparseFlag(v));
void Set(const T& v) { this->Write(&v, &flags_internal::FlagOps<T>); }
};
// This class facilitates Flag object registration and tail expression-based
......@@ -67,7 +85,7 @@ template <typename T, bool do_register>
class FlagRegistrar {
public:
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) && {
......
......@@ -34,13 +34,7 @@ namespace flags_internal {
namespace {
void DestroyFlag(CommandLineFlag* flag) NO_THREAD_SAFETY_ANALYSIS {
// Values are heap allocated for retired and Abseil Flags.
if (flag->IsRetired() || flag->IsAbseilFlag()) {
if (flag->cur) Delete(flag->op, flag->cur);
if (flag->def) Delete(flag->op, flag->def);
}
delete flag->locks;
flag->Destroy();
// CommandLineFlag handle object is heap allocated for non Abseil Flags.
if (!flag->IsAbseilFlag()) {
......@@ -48,6 +42,8 @@ void DestroyFlag(CommandLineFlag* flag) NO_THREAD_SAFETY_ANALYSIS {
}
}
} // namespace
// --------------------------------------------------------------------
// FlagRegistry
// A FlagRegistry singleton object holds all flag objects indexed
......@@ -105,8 +101,6 @@ class FlagPtrMap {
};
constexpr size_t FlagPtrMap::kNumBuckets;
} // namespace
class FlagRegistry {
public:
FlagRegistry() = default;
......@@ -292,10 +286,10 @@ class FlagSaverImpl {
saved.op = flag->op;
saved.marshalling_op = flag->marshalling_op;
{
absl::MutexLock l(InitFlagIfNecessary(flag));
absl::MutexLock l(flag->InitFlagIfNecessary());
saved.validator = flag->validator;
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.default_value = Clone(saved.op, flag->def);
saved.counter = flag->counter;
......@@ -318,34 +312,34 @@ class FlagSaverImpl {
bool restored = false;
{
absl::Mutex* mu = InitFlagIfNecessary(flag);
absl::MutexLock l(mu);
absl::MutexLock l(flag->InitFlagIfNecessary());
flag->validator = src.validator;
flag->modified = src.modified;
flag->on_command_line = src.on_command_line;
if (flag->counter != src.counter ||
ChangedDirectly(flag, src.default_value, flag->def)) {
flag->counter++;
restored = true;
Copy(src.op, src.default_value, flag->def);
}
if (flag->counter != src.counter ||
ChangedDirectly(flag, src.current, flag->cur)) {
restored = true;
flag->counter++;
Copy(src.op, src.current, flag->cur);
UpdateCopy(flag, mu);
// Revalidate the flag because the validator might store state based
// on the flag's value, which just changed due to the restore.
// Failing validation is ignored because it's assumed that the flag
// was valid previously and there's little that can be done about it
// here, anyway.
Validate(flag, flag->cur);
UpdateCopy(flag);
flag->InvokeCallback();
}
}
// Log statements must be done when no flag lock is held.
if (restored) {
flag->counter++;
// Revalidate the flag because the validator might store state based
// on the flag's value, which just changed due to the restore.
// Failing validation is ignored because it's assumed that the flag
// was valid previously and there's little that can be done about it
// here, anyway.
flag->ValidateInputValue(flag->CurrentValue());
ABSL_INTERNAL_LOG(
INFO, absl::StrCat("Restore saved value of ", flag->Name(), ": ",
Unparse(src.marshalling_op, src.current)));
......@@ -412,13 +406,17 @@ void FillCommandLineFlagInfo(CommandLineFlag* flag,
result->description = flag->Help();
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->default_value = flag->DefaultValue();
result->is_default = !flag->modified;
result->has_validator_fn = (flag->validator != nullptr);
result->is_default = !flag->IsModified();
result->has_validator_fn = flag->HasValidatorFn();
absl::MutexLock l(flag->InitFlagIfNecessary());
result->flag_ptr = flag->IsAbseilFlag() ? nullptr : flag->cur;
}
......
......@@ -32,7 +32,6 @@ bool GetCommandLineOption(absl::string_view name, std::string* value) {
return false;
}
absl::MutexLock l(InitFlagIfNecessary(flag));
*value = flag->CurrentValue();
return true;
}
......@@ -88,22 +87,9 @@ bool SetCommandLineOptionWithMode(absl::string_view name,
bool IsValidFlagValue(absl::string_view name, absl::string_view value) {
CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name);
if (flag == nullptr) {
return false;
}
if (flag->IsRetired()) {
return true;
}
// 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;
return flag != nullptr &&
(flag->IsRetired() || flag->ValidateInputValue(value));
}
// --------------------------------------------------------------------
......@@ -111,7 +97,6 @@ bool IsValidFlagValue(absl::string_view name, absl::string_view value) {
bool SpecifiedOnCommandLine(absl::string_view name) {
CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name);
if (flag != nullptr && !flag->IsRetired()) {
absl::MutexLock l(InitFlagIfNecessary(flag));
return flag->IsSpecifiedOnCommandLine();
}
return false;
......
......@@ -21,11 +21,11 @@
#include "absl/flags/flag.h"
#include "absl/flags/internal/path_util.h"
#include "absl/flags/internal/program_name.h"
#include "absl/flags/usage.h"
#include "absl/flags/usage_config.h"
#include "absl/strings/ascii.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
ABSL_FLAG(bool, help, false,
......@@ -185,7 +185,7 @@ void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag,
}
printer.Write(absl::StrCat("default: ", dflt_val, ";"));
if (flag.modified) {
if (flag.IsModified()) {
std::string curr_val = flag.CurrentValue();
if (flag.IsOfType<std::string>()) {
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'
// and its variants.
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) {
out << flags_internal::ShortProgramInvocationName() << ": "
<< absl::ProgramUsageMessage() << "\n\n";
<< program_usage_message << "\n\n";
} else {
// XML schema is not a part of our public API for now.
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.
<< XMLElement("program", flags_internal::ShortProgramInvocationName())
<< '\n'
<< XMLElement("usage", absl::ProgramUsageMessage()) << '\n';
<< XMLElement("usage", program_usage_message) << '\n';
}
// Map of package name to
......@@ -228,8 +228,6 @@ void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
matching_flags;
flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) {
absl::MutexLock l(InitFlagIfNecessary(flag));
std::string flag_filename = flag->Filename();
// 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.
// 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) {
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.
// 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)) {
flags_internal::FlagsHelpImpl(
out, flags_internal::GetUsageConfig().contains_helpshort_flags,
HelpFormat::kHumanReadable);
HelpFormat::kHumanReadable, program_usage_message);
return 1;
}
if (absl::GetFlag(FLAGS_helpfull)) {
// show all options
flags_internal::FlagsHelp(out);
flags_internal::FlagsHelp(out, "", HelpFormat::kHumanReadable,
program_usage_message);
return 1;
}
if (!absl::GetFlag(FLAGS_helpon).empty()) {
flags_internal::FlagsHelp(
out, absl::StrCat("/", absl::GetFlag(FLAGS_helpon), "."));
out, absl::StrCat("/", absl::GetFlag(FLAGS_helpon), "."),
HelpFormat::kHumanReadable, program_usage_message);
return 1;
}
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;
}
if (absl::GetFlag(FLAGS_help)) {
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";
......@@ -338,7 +343,8 @@ int HandleUsageFlags(std::ostream& out) {
if (absl::GetFlag(FLAGS_helppackage)) {
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";
......
......@@ -47,8 +47,8 @@ void FlagHelp(std::ostream& out, const flags_internal::CommandLineFlag& flag,
// .../path/to/file.<ext>
// for any extension 'ext'. If the filter is empty this function produces help
// messages for all flags.
void FlagsHelp(std::ostream& out, absl::string_view filter = {},
HelpFormat format = HelpFormat::kHumanReadable);
void FlagsHelp(std::ostream& out, absl::string_view filter,
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.
// Non negative return values are expected to be used as an exit code for a
// binary.
int HandleUsageFlags(std::ostream& out);
int HandleUsageFlags(std::ostream& out,
absl::string_view program_usage_message);
} // namespace flags_internal
} // 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,
"usage_reporting_test_flag_04 help message");
static const char kTestUsageMessage[] = "Custom usage message";
struct UDT {
UDT() = default;
UDT(const UDT&) = default;
......@@ -83,7 +85,7 @@ class UsageReportingTest : public testing::Test {
using UsageReportingDeathTest = UsageReportingTest;
TEST_F(UsageReportingDeathTest, TestSetProgramUsageMessage) {
EXPECT_EQ(absl::ProgramUsageMessage(), "Custom usage message");
EXPECT_EQ(absl::ProgramUsageMessage(), kTestUsageMessage);
#ifndef _WIN32
// TODO(rogeeff): figure out why this does not work on Windows.
......@@ -175,22 +177,22 @@ TEST_F(UsageReportingTest, TestFlagsHelpHRF) {
std::stringstream test_buf_01;
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);
std::stringstream test_buf_02;
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);
std::stringstream test_buf_03;
flags::FlagsHelp(test_buf_03, "usage_test",
flags::HelpFormat::kHumanReadable);
flags::FlagsHelp(test_buf_03, "usage_test", flags::HelpFormat::kHumanReadable,
kTestUsageMessage);
EXPECT_EQ(test_buf_03.str(), usage_test_flags_out);
std::stringstream test_buf_04;
flags::FlagsHelp(test_buf_04, "flags/invalid_file_name.cc",
flags::HelpFormat::kHumanReadable);
flags::HelpFormat::kHumanReadable, kTestUsageMessage);
EXPECT_EQ(test_buf_04.str(),
R"(usage_test: Custom usage message
......@@ -198,7 +200,8 @@ TEST_F(UsageReportingTest, TestFlagsHelpHRF) {
)");
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();
absl::string_view test_out_str(test_out);
EXPECT_TRUE(
......@@ -217,7 +220,7 @@ TEST_F(UsageReportingTest, TestFlagsHelpHRF) {
TEST_F(UsageReportingTest, TestNoUsageFlags) {
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);
std::stringstream test_buf;
EXPECT_EQ(flags::HandleUsageFlags(test_buf), 1);
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
EXPECT_EQ(test_buf.str(),
R"(usage_test: Custom usage message
......@@ -250,7 +253,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_help) {
absl::SetFlag(&FLAGS_help, true);
std::stringstream test_buf;
EXPECT_EQ(flags::HandleUsageFlags(test_buf), 1);
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
EXPECT_EQ(test_buf.str(),
R"(usage_test: Custom usage message
......@@ -276,7 +279,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_helppackage) {
absl::SetFlag(&FLAGS_helppackage, true);
std::stringstream test_buf;
EXPECT_EQ(flags::HandleUsageFlags(test_buf), 1);
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
EXPECT_EQ(test_buf.str(),
R"(usage_test: Custom usage message
......@@ -302,10 +305,9 @@ TEST_F(UsageReportingTest, TestUsageFlag_version) {
absl::SetFlag(&FLAGS_version, true);
std::stringstream test_buf;
EXPECT_EQ(flags::HandleUsageFlags(test_buf), 0);
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 0);
#ifndef NDEBUG
EXPECT_EQ(test_buf.str(),
"usage_test\nDebug build (NDEBUG not #defined)\n");
EXPECT_EQ(test_buf.str(), "usage_test\nDebug build (NDEBUG not #defined)\n");
#else
EXPECT_EQ(test_buf.str(), "usage_test\n");
#endif
......@@ -317,7 +319,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_only_check_args) {
absl::SetFlag(&FLAGS_only_check_args, true);
std::stringstream test_buf;
EXPECT_EQ(flags::HandleUsageFlags(test_buf), 0);
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 0);
EXPECT_EQ(test_buf.str(), "");
}
......@@ -327,7 +329,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
absl::SetFlag(&FLAGS_helpon, "bla-bla");
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(),
R"(usage_test: Custom usage message
......@@ -337,7 +339,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
absl::SetFlag(&FLAGS_helpon, "usage_test");
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(),
R"(usage_test: Custom usage message
......@@ -362,7 +364,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
int main(int argc, char* argv[]) {
absl::GetFlag(FLAGS_undefok); // Force linking of parse.cc
flags::SetProgramInvocationName("usage_test");
absl::SetProgramUsageMessage("Custom usage message");
absl::SetProgramUsageMessage(kTestUsageMessage);
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
......
......@@ -16,6 +16,7 @@
#include "absl/flags/parse.h"
#include <stdlib.h>
#include <fstream>
#include <iostream>
#include <tuple>
......@@ -28,6 +29,7 @@
#include "absl/flags/internal/program_name.h"
#include "absl/flags/internal/registry.h"
#include "absl/flags/internal/usage.h"
#include "absl/flags/usage.h"
#include "absl/flags/usage_config.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/strip.h"
......@@ -280,22 +282,7 @@ void CheckDefaultValuesParsingRoundtrip() {
IGNORE_TYPE(std::vector<std::string>)
#undef IGNORE_TYPE
absl::MutexLock lock(InitFlagIfNecessary(flag));
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);
flag->CheckDefaultValueParsingRoundtrip();
});
#endif
}
......@@ -717,12 +704,14 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
#endif
if (!success) {
flags_internal::HandleUsageFlags(std::cout);
flags_internal::HandleUsageFlags(std::cout,
ProgramUsageMessage());
std::exit(1);
}
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) {
std::exit(exit_code);
......
......@@ -288,7 +288,6 @@ TEST(HashValueTest, Strings) {
// Also check that nested types maintain the same hash.
const WrapInTuple t{};
EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
//
t(std::string()), t(absl::string_view()),
t(std::string("")), t(absl::string_view("")),
t(std::string(small)), t(absl::string_view(small)),
......
......@@ -640,7 +640,8 @@ class CityHashState : public HashStateBase<CityHashState> {
#endif // ABSL_HAVE_INTRINSIC_INT128
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>
using IntegralFastPath =
......
......@@ -19,8 +19,8 @@
//
// This header file defines 128-bit integer types.
//
// Currently, this file defines `uint128`, an unsigned 128-bit integer; a signed
// 128-bit integer is forthcoming.
// Currently, this file defines `uint128`, an unsigned 128-bit integer;
// a signed 128-bit integer is forthcoming.
#ifndef ABSL_NUMERIC_INT128_H_
#define ABSL_NUMERIC_INT128_H_
......
......@@ -254,7 +254,7 @@ std::string StrJoin(const Range& range, absl::string_view separator,
template <typename T, typename Formatter>
std::string StrJoin(std::initializer_list<T> il, absl::string_view separator,
Formatter&& fmt) {
Formatter&& fmt) {
return strings_internal::JoinRange(il, separator, fmt);
}
......@@ -275,7 +275,8 @@ std::string StrJoin(const Range& range, absl::string_view separator) {
}
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);
}
......
......@@ -31,7 +31,8 @@ namespace synchronization_internal {
// ThreadIdentity storage is persistent, we maintain a free-list of previously
// 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;
// A per-thread destructor for reclaiming associated ThreadIdentity objects.
......
......@@ -179,6 +179,7 @@ class Duration {
Duration& operator%=(Duration rhs);
// Overloads that forward to either the int64_t or double overloads above.
// Integer operands must be representable as int64_t.
template <typename T>
Duration& operator*=(T 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; }
// Multiplicative Operators
// Integer operands must be representable as int64_t.
template <typename T>
Duration operator*(Duration lhs, T rhs) {
return lhs *= rhs;
......@@ -375,7 +377,8 @@ constexpr Duration InfiniteDuration();
// Hours()
//
// 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.
// 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} \
--copt="-DDYNAMIC_ANNOTATIONS_ENABLED=1" \
--copt="-DADDRESS_SANITIZER" \
--copt="-DUNDEFINED_BEHAVIOR_SANITIZER" \
--copt="-fsanitize=address" \
--copt="-fsanitize=float-divide-by-zero" \
--copt="-fsanitize=nullability" \
--copt="-fsanitize=undefined" \
--copt="-fno-sanitize=vptr" \
--copt=-Werror \
--keep_going \
--linkopt="-fsanitize=address" \
--linkopt="-fsanitize-link-c++-runtime" \
--show_timestamps \
--test_env="ASAN_SYMBOLIZER_PATH=/opt/llvm/clang/bin/llvm-symbolizer" \
--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_tag_filters="-benchmark,-noasan" \
${BAZEL_EXTRA_ARGS:-}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment