diff options
Diffstat (limited to 'absl/flags')
-rw-r--r-- | absl/flags/BUILD.bazel | 23 | ||||
-rw-r--r-- | absl/flags/CMakeLists.txt | 21 | ||||
-rw-r--r-- | absl/flags/flag.cc | 26 | ||||
-rw-r--r-- | absl/flags/flag.h | 51 | ||||
-rw-r--r-- | absl/flags/internal/commandlineflag.cc | 439 | ||||
-rw-r--r-- | absl/flags/internal/commandlineflag.h | 76 | ||||
-rw-r--r-- | absl/flags/internal/commandlineflag_test.cc | 14 | ||||
-rw-r--r-- | absl/flags/internal/flag.h | 72 | ||||
-rw-r--r-- | absl/flags/internal/registry.cc | 54 | ||||
-rw-r--r-- | absl/flags/internal/type_erased.cc | 19 | ||||
-rw-r--r-- | absl/flags/internal/usage.cc | 38 | ||||
-rw-r--r-- | absl/flags/internal/usage.h | 7 | ||||
-rw-r--r-- | absl/flags/internal/usage_test.cc | 38 | ||||
-rw-r--r-- | absl/flags/parse.cc | 25 |
14 files changed, 516 insertions, 387 deletions
diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index 82e6ffcfcef3..2fe61eaa8a8b 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel @@ -155,14 +155,12 @@ cc_library( ) cc_library( - name = "usage", + name = "usage_internal", srcs = [ "internal/usage.cc", - "usage.cc", ], hdrs = [ "internal/usage.h", - "usage.h", ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, @@ -180,6 +178,23 @@ cc_library( ) cc_library( + name = "usage", + srcs = [ + "usage.cc", + ], + hdrs = [ + "usage.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":usage_internal", + "//absl/strings", + "//absl/synchronization", + ], +) + +cc_library( name = "parse", srcs = ["parse.cc"], hdrs = [ @@ -195,6 +210,7 @@ cc_library( ":internal", ":registry", ":usage", + ":usage_internal", "//absl/strings", "//absl/synchronization", ], @@ -360,6 +376,7 @@ cc_test( ":internal", ":parse", ":usage", + ":usage_internal", "//absl/memory", "//absl/strings", "@com_google_googletest//:gtest", diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt index 9c936eccc8e3..fa1d4e177b3e 100644 --- a/absl/flags/CMakeLists.txt +++ b/absl/flags/CMakeLists.txt @@ -141,13 +141,11 @@ absl_cc_library( # Internal-only target, do not depend on directly. absl_cc_library( NAME - flags_usage + flags_usage_internal SRCS "internal/usage.cc" - "usage.cc" HDRS "internal/usage.h" - "usage.h" COPTS ${ABSL_DEFAULT_COPTS} LINKOPTS @@ -163,6 +161,23 @@ absl_cc_library( absl_cc_library( NAME + flags_usage + SRCS + "usage.cc" + HDRS + "usage.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::flags_usage_internal + absl::strings + absl::synchronization +) + +absl_cc_library( + NAME flags_parse SRCS "parse.cc" diff --git a/absl/flags/flag.cc b/absl/flags/flag.cc index ba002edd24f1..f16cc75e3843 100644 --- a/absl/flags/flag.cc +++ b/absl/flags/flag.cc @@ -24,24 +24,16 @@ namespace absl { // so in debug builds we always use the slower implementation, which always // validates the type. #ifndef NDEBUG -#define ABSL_FLAGS_ATOMIC_GET(T) \ - T GetFlag(const absl::Flag<T>& flag) { \ - T result; \ - flag.internal.Read(&result, &flags_internal::FlagOps<T>); \ - return result; \ - } +#define ABSL_FLAGS_ATOMIC_GET(T) \ + T GetFlag(const absl::Flag<T>& flag) { return flag.Get(); } #else -#define ABSL_FLAGS_ATOMIC_GET(T) \ - T GetFlag(const absl::Flag<T>& flag) { \ - const int64_t r = flag.internal.atomic.load(std::memory_order_acquire); \ - if (r != flags_internal::CommandLineFlag::kAtomicInit) { \ - T t; \ - memcpy(&t, &r, sizeof(T)); \ - return t; \ - } \ - T result; \ - flag.internal.Read(&result, &flags_internal::FlagOps<T>); \ - return result; \ +#define ABSL_FLAGS_ATOMIC_GET(T) \ + T GetFlag(const absl::Flag<T>& flag) { \ + T result; \ + if (flag.AtomicGet(&result)) { \ + return result; \ + } \ + return flag.Get(); \ } #endif diff --git a/absl/flags/flag.h b/absl/flags/flag.h index 193755e56aa6..c70023aba8c7 100644 --- a/absl/flags/flag.h +++ b/absl/flags/flag.h @@ -91,30 +91,7 @@ T GetFlag(const absl::Flag<T>& flag) { ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_INTERNAL_LOCK_FREE_VALIDATE) #undef ABSL_FLAGS_INTERNAL_LOCK_FREE_VALIDATE - // Implementation notes: - // - // We are wrapping a union around the value of `T` to serve three purposes: - // - // 1. `U.value` has correct size and alignment for a value of type `T` - // 2. The `U.value` constructor is not invoked since U's constructor does not - // do it explicitly. - // 3. The `U.value` destructor is invoked since U's destructor does it - // explicitly. This makes `U` a kind of RAII wrapper around non default - // constructible value of T, which is destructed when we leave the scope. - // We do need to destroy U.value, which is constructed by - // CommandLineFlag::Read even though we left it in a moved-from state - // after std::move. - // - // All of this serves to avoid requiring `T` being default constructible. - union U { - T value; - U() {} - ~U() { value.~T(); } - }; - U u; - - flag.internal.Read(&u.value, &flags_internal::FlagOps<T>); - return std::move(u.value); + return flag.Get(); } // Overload for `GetFlag()` for types that support lock-free reads. @@ -132,7 +109,7 @@ ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_INTERNAL_LOCK_FREE_EXPORT) // but especially within performance-critical code. template <typename T> void SetFlag(absl::Flag<T>* flag, const T& v) { - flag->internal.Write(&v, &flags_internal::FlagOps<T>); + flag->Set(v); } // Overload of `SetFlag()` to allow callers to pass in a value that is @@ -141,7 +118,7 @@ void SetFlag(absl::Flag<T>* flag, const T& v) { template <typename T, typename V> void SetFlag(absl::Flag<T>* flag, const V& v) { T value(v); - SetFlag<T>(flag, value); + flag->Set(value); } } // namespace absl @@ -239,17 +216,17 @@ void SetFlag(absl::Flag<T>* flag, const V& v) { // Note: Name of registrar object is not arbitrary. It is used to "grab" // global name for FLAGS_no<flag_name> symbol, thus preventing the possibility // of defining two flags with names foo and nofoo. -#define ABSL_FLAG_IMPL(Type, name, default_value, help) \ - namespace absl {} \ - ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ - ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help) \ - absl::Flag<Type> FLAGS_##name( \ - ABSL_FLAG_IMPL_FLAGNAME(#name), \ - &AbslFlagsWrapHelp##name, \ - ABSL_FLAG_IMPL_FILENAME(), \ - &absl::flags_internal::FlagMarshallingOps<Type>, \ - &AbslFlagsInitFlag##name); \ - extern bool FLAGS_no##name; \ +#define ABSL_FLAG_IMPL(Type, name, default_value, help) \ + namespace absl {} \ + ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ + ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help) \ + absl::Flag<Type> FLAGS_##name( \ + ABSL_FLAG_IMPL_FLAGNAME(#name), \ + &AbslFlagsWrapHelp##name, \ + ABSL_FLAG_IMPL_FILENAME(), \ + &absl::flags_internal::FlagMarshallingOps<Type>, \ + &AbslFlagsInitFlag##name); \ + extern bool FLAGS_no##name; \ bool FLAGS_no##name = ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name) // ABSL_RETIRED_FLAG diff --git a/absl/flags/internal/commandlineflag.cc b/absl/flags/internal/commandlineflag.cc index 744447219c71..98a46695304b 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 4815cdc7c9a2..d82443a7760e 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 705f301ce7a0..f0d57adbd260 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 9b32f46735b6..bc50de5486bc 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 e52cbb683b9e..f37871c9d40f 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 cc103983672b..2984291c87c7 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 b1ff8b83c223..aac02db6c27a 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 33f3f969bfaf..76075b0829d9 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 fa121fc933fc..5e5f7a84cc43 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(); diff --git a/absl/flags/parse.cc b/absl/flags/parse.cc index 3caaa1c256cb..e9dd4204e8a8 100644 --- a/absl/flags/parse.cc +++ b/absl/flags/parse.cc @@ -16,6 +16,7 @@ #include "absl/flags/parse.h" #include <stdlib.h> + #include <fstream> #include <iostream> #include <tuple> @@ -28,6 +29,7 @@ #include "absl/flags/internal/program_name.h" #include "absl/flags/internal/registry.h" #include "absl/flags/internal/usage.h" +#include "absl/flags/usage.h" #include "absl/flags/usage_config.h" #include "absl/strings/str_cat.h" #include "absl/strings/strip.h" @@ -280,22 +282,7 @@ void CheckDefaultValuesParsingRoundtrip() { IGNORE_TYPE(std::vector<std::string>) #undef IGNORE_TYPE - absl::MutexLock lock(InitFlagIfNecessary(flag)); - - std::string v = flag->DefaultValue(); - void* dst = Clone(flag->op, flag->def); - std::string error; - if (!flags_internal::Parse(flag->marshalling_op, v, dst, &error)) { - ABSL_INTERNAL_LOG( - FATAL, - absl::StrCat("Flag ", flag->Name(), " (from ", flag->Filename(), - "): std::string form of default value '", v, - "' could not be parsed; error=", error)); - } - - // We do not compare dst to def since parsing/unparsing may make - // small changes, e.g., precision loss for floating point types. - Delete(flag->op, dst); + flag->CheckDefaultValueParsingRoundtrip(); }); #endif } @@ -717,12 +704,14 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[], #endif if (!success) { - flags_internal::HandleUsageFlags(std::cout); + flags_internal::HandleUsageFlags(std::cout, + ProgramUsageMessage()); std::exit(1); } if (usage_flag_act == UsageFlagsAction::kHandleUsage) { - int exit_code = flags_internal::HandleUsageFlags(std::cout); + int exit_code = flags_internal::HandleUsageFlags( + std::cout, ProgramUsageMessage()); if (exit_code != -1) { std::exit(exit_code); |