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:
Abseil Team 2019-07-17 16:35:47 -04:00 committed by Derek Mauro
parent 44efe96dfc
commit c6c3c1b498
32 changed files with 1168 additions and 657 deletions

View file

@ -37,44 +37,6 @@ const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
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.
bool ShouldValidateFlagValue(const CommandLineFlag& flag) {
#define DONT_VALIDATE(T) \
@ -89,145 +51,72 @@ bool ShouldValidateFlagValue(const CommandLineFlag& flag) {
} // namespace
// 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, absl::Mutex* primary_lock)
EXCLUSIVE_LOCKS_REQUIRED(primary_lock) {
#define STORE_ATOMIC(T) \
else if (flag->IsOfType<T>()) { \
StoreAtomic(flag, flag->cur, sizeof(T)); \
absl::Mutex* InitFlag(CommandLineFlag* flag) {
ABSL_CONST_INIT static absl::Mutex init_lock(absl::kConstInit);
absl::Mutex* mu;
{
absl::MutexLock lock(&init_lock);
if (flag->locks == nullptr) { // Must initialize Mutexes for this flag.
flag->locks = new flags_internal::CommandLineFlagLocks;
}
mu = &flag->locks->primary_mu;
}
if (false) {
}
ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(STORE_ATOMIC)
#undef STORE_ATOMIC
{
absl::MutexLock lock(mu);
InvokeCallback(flag, primary_lock);
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,
// and return &flag->locks->primary_mu.
absl::Mutex* InitFlagIfNecessary(CommandLineFlag* flag)
LOCK_RETURNED(flag->locks->primary_mu) {
absl::Mutex* mu;
if (!flag->inited.load(std::memory_order_acquire)) {
// 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();
mu->Lock();
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;
absl::Mutex* CommandLineFlag::InitFlagIfNecessary() const
LOCK_RETURNED(locks->primary_mu) {
if (!this->inited.load(std::memory_order_acquire)) {
return InitFlag(const_cast<CommandLineFlag*>(this));
}
return mu;
// All fields initialized; this->locks is therefore safe to read.
return &this->locks->primary_mu;
}
// 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); \
void CommandLineFlag::Destroy() const {
// Values are heap allocated for retired and Abseil Flags.
if (IsRetired() || IsAbseilFlag()) {
if (this->cur) Delete(this->op, this->cur);
if (this->def) Delete(this->op, this->def);
}
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;
delete this->locks;
}
// Direct-access flags can be modified without going through the
// flag API. Detect such changes and updated the modified bit.
void UpdateModifiedBit(CommandLineFlag* flag) {
if (!flag->IsAbseilFlag()) {
absl::MutexLock l(InitFlagIfNecessary(flag));
if (!flag->modified && ChangedDirectly(flag, flag->cur, flag->def)) {
flag->modified = true;
}
}
bool CommandLineFlag::IsModified() const {
absl::MutexLock l(InitFlagIfNecessary());
return modified;
}
bool Validate(CommandLineFlag*, const void*) {
return true;
void CommandLineFlag::SetModified(bool is_modified) {
absl::MutexLock l(InitFlagIfNecessary());
modified = is_modified;
}
std::string HelpText::GetHelpText() const {
if (help_function_) return help_function_();
if (help_message_) return help_message_;
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);
bool CommandLineFlag::IsSpecifiedOnCommandLine() const {
absl::MutexLock l(InitFlagIfNecessary());
return on_command_line;
}
absl::string_view CommandLineFlag::Typename() const {
@ -259,21 +148,96 @@ std::string CommandLineFlag::Filename() const {
}
std::string CommandLineFlag::DefaultValue() const {
absl::MutexLock l(InitFlagIfNecessary());
return Unparse(this->marshalling_op, this->def);
}
std::string CommandLineFlag::CurrentValue() const {
absl::MutexLock l(InitFlagIfNecessary());
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(
const flags_internal::FlagCallback mutation_callback) {
absl::Mutex* mu = InitFlagIfNecessary(this);
absl::MutexLock l(mu);
absl::MutexLock l(InitFlagIfNecessary());
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`
@ -282,8 +246,9 @@ void CommandLineFlag::SetCallback(
// 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
// in 'err'
static bool TryParseLocked(CommandLineFlag* flag, void* dst,
absl::string_view value, std::string* err) {
bool TryParseLocked(CommandLineFlag* flag, void* dst, absl::string_view value,
std::string* err)
EXCLUSIVE_LOCKS_REQUIRED(flag->locks->primary_mu) {
void* tentative_value = Clone(flag->op, flag->def);
std::string 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;
}
if (!Validate(flag, tentative_value)) {
if (!flag->InvokeValidator(tentative_value)) {
*err = absl::StrCat("Failed validation of new value '",
Unparse(flag->marshalling_op, tentative_value),
"' for flag '", flag->Name(), "'");
@ -324,17 +289,23 @@ bool CommandLineFlag::SetFromString(absl::string_view value,
ValueSource source, std::string* err) {
if (IsRetired()) return false;
UpdateModifiedBit(this);
absl::MutexLock l(InitFlagIfNecessary());
absl::Mutex* mu = InitFlagIfNecessary(this);
absl::MutexLock l(mu);
// Direct-access flags can be modified without going through the
// 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) {
case SET_FLAGS_VALUE: {
// set or modify the flag's value
if (!TryParseLocked(this, this->cur, value, err)) return false;
this->modified = true;
UpdateCopy(this, mu);
UpdateCopy(this);
InvokeCallback();
if (source == kCommandLine) {
this->on_command_line = true;
@ -346,7 +317,8 @@ bool CommandLineFlag::SetFromString(absl::string_view value,
if (!this->modified) {
if (!TryParseLocked(this, this->cur, value, err)) return false;
this->modified = true;
UpdateCopy(this, mu);
UpdateCopy(this);
InvokeCallback();
} else {
// 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
@ -365,7 +337,8 @@ bool CommandLineFlag::SetFromString(absl::string_view value,
if (!this->modified) {
// Need to set both defvalue *and* current, in this case
Copy(this->op, this->def, this->cur);
UpdateCopy(this, mu);
UpdateCopy(this);
InvokeCallback();
}
break;
}
@ -379,5 +352,143 @@ bool CommandLineFlag::SetFromString(absl::string_view value,
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 absl

View file

@ -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,

View file

@ -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));

View file

@ -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) {}
: 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) {}
// Not copyable/assignable.
Flag(const Flag<T>&) = delete;
Flag<T>& operator=(const Flag<T>&) = delete;
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;
absl::string_view Name() const { return internal.Name(); }
std::string Help() const { return internal.Help(); }
std::string Filename() const { return internal.Filename(); }
absl::flags_internal::CommandLineFlag internal;
void SetCallback(const flags_internal::FlagCallback mutation_callback) {
internal.SetCallback(mutation_callback);
this->Read(&u.value, &flags_internal::FlagOps<T>);
return std::move(u.value);
}
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));
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;
}
return false;
}
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) && {

View file

@ -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;
}

View file

@ -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;

View file

@ -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";

View file

@ -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

View file

@ -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();