summaryrefslogtreecommitdiff
path: root/absl/flags/internal
diff options
context:
space:
mode:
authorGravatar Abseil Team <absl-team@google.com>2019-07-17 16:35:47 -0400
committerGravatar Derek Mauro <dmauro@google.com>2019-07-17 16:40:57 -0400
commitc6c3c1b498e4ee939b24be59cae29d59c3863be8 (patch)
tree030b875cdbbd25d2d0b7bca0b68a71351eeb2c41 /absl/flags/internal
parent44efe96dfca674a17b45ca53fc77fb69f1e29bf4 (diff)
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
Diffstat (limited to 'absl/flags/internal')
-rw-r--r--absl/flags/internal/commandlineflag.cc439
-rw-r--r--absl/flags/internal/commandlineflag.h76
-rw-r--r--absl/flags/internal/commandlineflag_test.cc14
-rw-r--r--absl/flags/internal/flag.h72
-rw-r--r--absl/flags/internal/registry.cc54
-rw-r--r--absl/flags/internal/type_erased.cc19
-rw-r--r--absl/flags/internal/usage.cc38
-rw-r--r--absl/flags/internal/usage.h7
-rw-r--r--absl/flags/internal/usage_test.cc38
9 files changed, 448 insertions, 309 deletions
diff --git a/absl/flags/internal/commandlineflag.cc b/absl/flags/internal/commandlineflag.cc
index 74444721..98a46695 100644
--- a/absl/flags/internal/commandlineflag.cc
+++ b/absl/flags/internal/commandlineflag.cc
@@ -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)); \
- }
-
- if (false) {
- }
- ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(STORE_ATOMIC)
-#undef STORE_ATOMIC
+absl::Mutex* InitFlag(CommandLineFlag* flag) {
+ ABSL_CONST_INIT static absl::Mutex init_lock(absl::kConstInit);
+ absl::Mutex* mu;
- InvokeCallback(flag, primary_lock);
-}
+ {
+ absl::MutexLock lock(&init_lock);
-// 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.
+ }
+
+ {
+ absl::MutexLock lock(mu);
+
+ if (!flag->retired && flag->def == nullptr) {
+ // Need to initialize def and cur fields.
flag->def = (*flag->make_init_value)();
flag->cur = Clone(flag->op, flag->def);
- UpdateCopy(flag, mu);
+ UpdateCopy(flag);
+ flag->inited.store(true, std::memory_order_release);
+ flag->InvokeCallback();
}
- 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;
}
+
+ flag->inited.store(true, std::memory_order_release);
return 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); \
+// Ensure that the lazily initialized fields of *flag have been initialized,
+// and return &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));
}
- 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;
+ // All fields initialized; this->locks is therefore safe to read.
+ return &this->locks->primary_mu;
}
-// 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;
- }
+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);
}
-}
-bool Validate(CommandLineFlag*, const void*) {
- return true;
+ delete this->locks;
}
-std::string HelpText::GetHelpText() const {
- if (help_function_) return help_function_();
- if (help_message_) return help_message_;
-
- return {};
+bool CommandLineFlag::IsModified() const {
+ absl::MutexLock l(InitFlagIfNecessary());
+ return modified;
}
-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::SetModified(bool is_modified) {
+ absl::MutexLock l(InitFlagIfNecessary());
+ modified = is_modified;
}
-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
diff --git a/absl/flags/internal/commandlineflag.h b/absl/flags/internal/commandlineflag.h
index 4815cdc7..d82443a7 100644
--- a/absl/flags/internal/commandlineflag.h
+++ b/absl/flags/internal/commandlineflag.h
@@ -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,
diff --git a/absl/flags/internal/commandlineflag_test.cc b/absl/flags/internal/commandlineflag_test.cc
index 705f301c..f0d57adb 100644
--- a/absl/flags/internal/commandlineflag_test.cc
+++ b/absl/flags/internal/commandlineflag_test.cc
@@ -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));
diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h
index 9b32f467..bc50de54 100644
--- a/absl/flags/internal/flag.h
+++ b/absl/flags/internal/flag.h
@@ -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) && {
diff --git a/absl/flags/internal/registry.cc b/absl/flags/internal/registry.cc
index e52cbb68..f37871c9 100644
--- a/absl/flags/internal/registry.cc
+++ b/absl/flags/internal/registry.cc
@@ -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;
}
diff --git a/absl/flags/internal/type_erased.cc b/absl/flags/internal/type_erased.cc
index cc103983..2984291c 100644
--- a/absl/flags/internal/type_erased.cc
+++ b/absl/flags/internal/type_erased.cc
@@ -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;
diff --git a/absl/flags/internal/usage.cc b/absl/flags/internal/usage.cc
index b1ff8b83..aac02db6 100644
--- a/absl/flags/internal/usage.cc
+++ b/absl/flags/internal/usage.cc
@@ -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";
diff --git a/absl/flags/internal/usage.h b/absl/flags/internal/usage.h
index 33f3f969..76075b08 100644
--- a/absl/flags/internal/usage.h
+++ b/absl/flags/internal/usage.h
@@ -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
diff --git a/absl/flags/internal/usage_test.cc b/absl/flags/internal/usage_test.cc
index fa121fc9..5e5f7a84 100644
--- a/absl/flags/internal/usage_test.cc
+++ b/absl/flags/internal/usage_test.cc
@@ -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();