summaryrefslogtreecommitdiff
path: root/absl/flags/internal
diff options
context:
space:
mode:
Diffstat (limited to 'absl/flags/internal')
-rw-r--r--absl/flags/internal/commandlineflag.cc496
-rw-r--r--absl/flags/internal/commandlineflag.h414
-rw-r--r--absl/flags/internal/commandlineflag_test.cc35
-rw-r--r--absl/flags/internal/flag.cc382
-rw-r--r--absl/flags/internal/flag.h628
-rw-r--r--absl/flags/internal/parse.h5
-rw-r--r--absl/flags/internal/path_util.h5
-rw-r--r--absl/flags/internal/program_name.cc11
-rw-r--r--absl/flags/internal/program_name.h5
-rw-r--r--absl/flags/internal/program_name_test.cc3
-rw-r--r--absl/flags/internal/registry.cc258
-rw-r--r--absl/flags/internal/registry.h57
-rw-r--r--absl/flags/internal/type_erased.cc38
-rw-r--r--absl/flags/internal/type_erased.h17
-rw-r--r--absl/flags/internal/type_erased_test.cc14
-rw-r--r--absl/flags/internal/usage.cc42
-rw-r--r--absl/flags/internal/usage.h5
-rw-r--r--absl/flags/internal/usage_test.cc84
18 files changed, 1338 insertions, 1161 deletions
diff --git a/absl/flags/internal/commandlineflag.cc b/absl/flags/internal/commandlineflag.cc
deleted file mode 100644
index f964165e..00000000
--- a/absl/flags/internal/commandlineflag.cc
+++ /dev/null
@@ -1,496 +0,0 @@
-//
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/internal/commandlineflag.h"
-
-#include <cassert>
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/optimization.h"
-#include "absl/flags/config.h"
-#include "absl/flags/usage_config.h"
-#include "absl/strings/str_cat.h"
-#include "absl/synchronization/mutex.h"
-
-namespace absl {
-inline namespace lts_2019_08_08 {
-namespace flags_internal {
-
-// The help message indicating that the commandline flag has been
-// 'stripped'. It will not show up when doing "-help" and its
-// variants. The flag is stripped if ABSL_FLAGS_STRIP_HELP is set to 1
-// before including absl/flags/flag.h
-
-// This is used by this file, and also in commandlineflags_reporting.cc
-const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
-
-namespace {
-
-// Currently we only validate flag values for user-defined flag types.
-bool ShouldValidateFlagValue(const CommandLineFlag& flag) {
-#define DONT_VALIDATE(T) \
- if (flag.IsOfType<T>()) return false;
- ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(DONT_VALIDATE)
- DONT_VALIDATE(std::string)
- DONT_VALIDATE(std::vector<std::string>)
-#undef DONT_VALIDATE
-
- return true;
-}
-
-} // namespace
-
-absl::Mutex* InitFlag(CommandLineFlag* flag) {
- ABSL_CONST_INIT static absl::Mutex init_lock(absl::kConstInit);
- absl::Mutex* mu;
-
- {
- absl::MutexLock lock(&init_lock);
-
- if (flag->locks == nullptr) { // Must initialize Mutexes for this flag.
- flag->locks = new flags_internal::CommandLineFlagLocks;
- }
-
- mu = &flag->locks->primary_mu;
- }
-
- {
- absl::MutexLock lock(mu);
-
- if (!flag->retired && flag->def == nullptr) {
- // Need to initialize def and cur fields.
- flag->def = (*flag->make_init_value)();
- flag->cur = Clone(flag->op, flag->def);
- UpdateCopy(flag);
- flag->inited.store(true, std::memory_order_release);
- flag->InvokeCallback();
- }
- }
-
- flag->inited.store(true, std::memory_order_release);
- return mu;
-}
-
-// Ensure that the lazily initialized fields of *flag have been initialized,
-// 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));
- }
-
- // All fields initialized; this->locks is therefore safe to read.
- return &this->locks->primary_mu;
-}
-
-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);
- }
-
- delete this->locks;
-}
-
-bool CommandLineFlag::IsModified() const {
- absl::MutexLock l(InitFlagIfNecessary());
- return modified;
-}
-
-void CommandLineFlag::SetModified(bool is_modified) {
- absl::MutexLock l(InitFlagIfNecessary());
- modified = is_modified;
-}
-
-bool CommandLineFlag::IsSpecifiedOnCommandLine() const {
- absl::MutexLock l(InitFlagIfNecessary());
- return on_command_line;
-}
-
-absl::string_view CommandLineFlag::Typename() const {
- // We do not store/report type in Abseil Flags, so that user do not rely on in
- // at runtime
- if (IsAbseilFlag() || IsRetired()) return "";
-
-#define HANDLE_V1_BUILTIN_TYPE(t) \
- if (IsOfType<t>()) { \
- return #t; \
- }
-
- HANDLE_V1_BUILTIN_TYPE(bool);
- HANDLE_V1_BUILTIN_TYPE(int32_t);
- HANDLE_V1_BUILTIN_TYPE(int64_t);
- HANDLE_V1_BUILTIN_TYPE(uint64_t);
- HANDLE_V1_BUILTIN_TYPE(double);
-#undef HANDLE_V1_BUILTIN_TYPE
-
- if (IsOfType<std::string>()) {
- return "string";
- }
-
- return "";
-}
-
-std::string CommandLineFlag::Filename() const {
- return flags_internal::GetUsageConfig().normalize_filename(this->filename);
-}
-
-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::MutexLock l(InitFlagIfNecessary());
-
- callback = mutation_callback;
-
- 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`
-// argument. If parsing is successful, it will try to validate that the parsed
-// value is valid for the specified 'flag'. Finally this function stores the
-// 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'
-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)) {
- auto type_name = flag->Typename();
- absl::string_view err_sep = parse_err.empty() ? "" : "; ";
- absl::string_view typename_sep = type_name.empty() ? "" : " ";
- *err = absl::StrCat("Illegal value '", value, "' specified for",
- typename_sep, type_name, " flag '", flag->Name(), "'",
- err_sep, parse_err);
- Delete(flag->op, tentative_value);
- return false;
- }
-
- if (!flag->InvokeValidator(tentative_value)) {
- *err = absl::StrCat("Failed validation of new value '",
- Unparse(flag->marshalling_op, tentative_value),
- "' for flag '", flag->Name(), "'");
- Delete(flag->op, tentative_value);
- return false;
- }
-
- flag->counter++;
- Copy(flag->op, tentative_value, dst);
- Delete(flag->op, tentative_value);
- return true;
-}
-
-// Sets the value of the flag based on specified string `value`. If the flag
-// was successfully set to new value, it returns true. Otherwise, sets `err`
-// to indicate the error, leaves the flag unchanged, and returns false. There
-// are three ways to set the flag's value:
-// * Update the current flag value
-// * Update the flag's default value
-// * Update the current flag value if it was never set before
-// The mode is selected based on 'set_mode' parameter.
-bool CommandLineFlag::SetFromString(absl::string_view value,
- FlagSettingMode set_mode,
- ValueSource source, std::string* err) {
- if (IsRetired()) return false;
-
- absl::MutexLock l(InitFlagIfNecessary());
-
- // 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);
- InvokeCallback();
-
- if (source == kCommandLine) {
- this->on_command_line = true;
- }
- break;
- }
- case SET_FLAG_IF_DEFAULT: {
- // set the flag's value, but only if it hasn't been set by someone else
- if (!this->modified) {
- if (!TryParseLocked(this, this->cur, value, err)) return false;
- this->modified = true;
- 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
- // value is not updated even though we return true.
- // *err = absl::StrCat(this->Name(), " is already set to ",
- // CurrentValue(), "\n");
- // return false;
- return true;
- }
- break;
- }
- case SET_FLAGS_DEFAULT: {
- // modify the flag's default-value
- if (!TryParseLocked(this, this->def, value, err)) return false;
-
- if (!this->modified) {
- // Need to set both defvalue *and* current, in this case
- Copy(this->op, this->def, this->cur);
- UpdateCopy(this);
- InvokeCallback();
- }
- break;
- }
- default: {
- // unknown set_mode
- assert(false);
- return false;
- }
- }
-
- 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
-} // inline namespace lts_2019_08_08
-} // namespace absl
diff --git a/absl/flags/internal/commandlineflag.h b/absl/flags/internal/commandlineflag.h
index 382553d2..6363c661 100644
--- a/absl/flags/internal/commandlineflag.h
+++ b/absl/flags/internal/commandlineflag.h
@@ -16,30 +16,41 @@
#ifndef ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
#define ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
-#include <atomic>
+#include <stddef.h>
+#include <stdint.h>
+#include <memory>
+#include <string>
+#include <typeinfo>
+
+#include "absl/base/config.h"
#include "absl/base/macros.h"
+#include "absl/flags/config.h"
#include "absl/flags/marshalling.h"
-#include "absl/synchronization/mutex.h"
+#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
-// Type-specific operations, eg., parsing, copying, etc. are provided
-// by function specific to that type with a signature matching FlagOpFn.
-enum FlagOp {
- kDelete,
- kClone,
- kCopy,
- kCopyConstruct,
- kSizeof,
- kParse,
- kUnparse
-};
-using FlagOpFn = void* (*)(FlagOp, const void*, void*);
-using FlagMarshallingOpFn = void* (*)(FlagOp, const void*, void*, void*);
+// An alias for flag static type id. Values of type identify the flag value type
+// simialarly to typeid(T), but without relying on RTTI being available. In most
+// cases this id is enough to uniquely identify the flag's value type. In a few
+// cases we'll have to resort to using actual RTTI implementation if it is
+// available.
+using FlagStaticTypeId = void* (*)();
+
+// Address of this function template is used in current implementation as a flag
+// static type id.
+template <typename T>
+void* FlagStaticTypeIdGen() {
+#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI)
+ return const_cast<std::type_info*>(&typeid(T));
+#else
+ return nullptr;
+#endif
+}
// Options that control SetCommandLineOptionWithMode.
enum FlagSettingMode {
@@ -62,215 +73,95 @@ enum ValueSource {
kProgrammaticChange,
};
-// Signature for the help generation function used as an argument for the
-// absl::Flag constructor.
-using HelpGenFunc = std::string (*)();
-
-// Signature for the function generating the initial flag value based (usually
-// 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
-template <typename T>
-void* FlagOps(FlagOp op, const void* v1, void* v2) {
- switch (op) {
- case kDelete:
- delete static_cast<const T*>(v1);
- return nullptr;
- case kClone:
- return new T(*static_cast<const T*>(v1));
- case kCopy:
- *static_cast<T*>(v2) = *static_cast<const T*>(v1);
- return nullptr;
- case kCopyConstruct:
- new (v2) T(*static_cast<const T*>(v1));
- return nullptr;
- case kSizeof:
- return reinterpret_cast<void*>(sizeof(T));
- default:
- return nullptr;
- }
-}
-
-template <typename T>
-void* FlagMarshallingOps(FlagOp op, const void* v1, void* v2, void* v3) {
- switch (op) {
- case kParse: {
- // initialize the temporary instance of type T based on current value in
- // destination (which is going to be flag's default value).
- T temp(*static_cast<T*>(v2));
- if (!absl::ParseFlag<T>(*static_cast<const absl::string_view*>(v1), &temp,
- static_cast<std::string*>(v3))) {
- return nullptr;
- }
- *static_cast<T*>(v2) = std::move(temp);
- return v2;
- }
- case kUnparse:
- *static_cast<std::string*>(v2) =
- absl::UnparseFlag<T>(*static_cast<const T*>(v1));
- return nullptr;
- default:
- return nullptr;
- }
-}
-
-// Functions that invoke flag-type-specific operations.
-inline void Delete(FlagOpFn op, const void* obj) {
- op(flags_internal::kDelete, obj, nullptr);
-}
-
-inline void* Clone(FlagOpFn op, const void* obj) {
- return op(flags_internal::kClone, obj, nullptr);
-}
-
-inline void Copy(FlagOpFn op, const void* src, void* dst) {
- op(flags_internal::kCopy, src, dst);
-}
-
-inline void CopyConstruct(FlagOpFn op, const void* src, void* dst) {
- op(flags_internal::kCopyConstruct, src, dst);
-}
-
-inline bool Parse(FlagMarshallingOpFn op, absl::string_view text, void* dst,
- std::string* error) {
- return op(flags_internal::kParse, &text, dst, error) != nullptr;
-}
-
-inline std::string Unparse(FlagMarshallingOpFn op, const void* val) {
- std::string result;
- op(flags_internal::kUnparse, val, &result, nullptr);
- return result;
-}
-
-inline size_t Sizeof(FlagOpFn op) {
- // This sequence of casts reverses the sequence from base::internal::FlagOps()
- return static_cast<size_t>(reinterpret_cast<intptr_t>(
- op(flags_internal::kSizeof, nullptr, nullptr)));
-}
-
-// The following struct contains the locks in a CommandLineFlag struct.
-// They are in a separate struct that is lazily allocated to avoid problems
-// with static initialization and to avoid multiple allocations.
-struct CommandLineFlagLocks {
- absl::Mutex primary_mu; // protects several fields in CommandLineFlag
- absl::Mutex callback_mu; // used to serialize callbacks
-};
-
-// Holds either a pointer to help text or a function which produces it. This is
-// needed for supporting both static initialization of Flags while supporting
-// the legacy registration framework. We can't use absl::variant<const char*,
-// const char*(*)()> since anybody passing 0 or nullptr in to a CommandLineFlag
-// would find an ambiguity.
-class HelpText {
+// Handle to FlagState objects. Specific flag state objects will restore state
+// of a flag produced this flag state from method CommandLineFlag::SaveState().
+class FlagStateInterface {
public:
- static constexpr HelpText FromFunctionPointer(const HelpGenFunc fn) {
- return HelpText(fn, nullptr);
- }
- static constexpr HelpText FromStaticCString(const char* msg) {
- return HelpText(nullptr, msg);
- }
-
- std::string GetHelpText() const;
-
- HelpText() = delete;
- HelpText(const HelpText&) = default;
- HelpText(HelpText&&) = default;
+ virtual ~FlagStateInterface() {}
- private:
- explicit constexpr HelpText(const HelpGenFunc fn, const char* msg)
- : help_function_(fn), help_message_(msg) {}
-
- HelpGenFunc help_function_;
- const char* help_message_;
+ // Restores the flag originated this object to the saved state.
+ virtual void Restore() const = 0;
};
// Holds all information for a flag.
-struct CommandLineFlag {
- constexpr CommandLineFlag(
- const char* name_arg, HelpText help_text, const char* filename_arg,
- const flags_internal::FlagOpFn op_arg,
- const flags_internal::FlagMarshallingOpFn marshalling_op_arg,
- const flags_internal::InitialValGenFunc initial_value_gen,
- const bool retired_arg, void* def_arg, void* cur_arg)
- : name(name_arg),
- help(help_text),
- filename(filename_arg),
- op(op_arg),
- marshalling_op(marshalling_op_arg),
- make_init_value(initial_value_gen),
- retired(retired_arg),
- inited(false),
- modified(false),
- on_command_line(false),
- validator(nullptr),
- callback(nullptr),
- def(def_arg),
- cur(cur_arg),
- counter(0),
- atomic(kAtomicInit),
- locks(nullptr) {}
-
- // Revert the init routine.
- void Destroy() const;
+class CommandLineFlag {
+ public:
+ constexpr CommandLineFlag() = default;
// Not copyable/assignable.
CommandLineFlag(const CommandLineFlag&) = delete;
CommandLineFlag& operator=(const CommandLineFlag&) = delete;
- absl::string_view Name() const { return name; }
- std::string Help() const { return help.GetHelpText(); }
- bool IsRetired() const { return this->retired; }
- 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
- return this->make_init_value != nullptr;
- }
-
- absl::string_view Typename() const;
- std::string Filename() const;
- std::string DefaultValue() const;
- std::string CurrentValue() const;
-
- bool HasValidatorFn() const;
- bool SetValidatorFn(FlagValidator fn);
- bool InvokeValidator(const void* value) const;
+ // Non-polymorphic access methods.
// Return true iff flag has type T.
template <typename T>
inline bool IsOfType() const {
- return this->op == &flags_internal::FlagOps<T>;
+ return TypeId() == &flags_internal::FlagStaticTypeIdGen<T>;
}
// Attempts to retrieve the flag value. Returns value on success,
// absl::nullopt otherwise.
template <typename T>
absl::optional<T> Get() const {
- if (IsRetired() || flags_internal::FlagOps<T> != this->op)
+ if (IsRetired() || !IsOfType<T>()) {
return absl::nullopt;
+ }
- T res;
- Read(&res, flags_internal::FlagOps<T>);
-
- return res;
+ // 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;
+
+ Read(&u.value);
+ return std::move(u.value);
}
- void SetCallback(const flags_internal::FlagCallback mutation_callback);
- void InvokeCallback();
+ // Polymorphic access methods
+
+ // Returns name of this flag.
+ virtual absl::string_view Name() const = 0;
+ // Returns name of the file where this flag is defined.
+ virtual std::string Filename() const = 0;
+ // Returns name of the flag's value type for some built-in types or empty
+ // std::string.
+ virtual absl::string_view Typename() const = 0;
+ // Returns help message associated with this flag.
+ virtual std::string Help() const = 0;
+ // Returns true iff this object corresponds to retired flag.
+ virtual bool IsRetired() const { return false; }
+ // Returns true iff this is a handle to an Abseil Flag.
+ virtual bool IsAbseilFlag() const { return true; }
+ // Returns id of the flag's value type.
+ virtual FlagStaticTypeId TypeId() const = 0;
+ virtual bool IsModified() const = 0;
+ virtual bool IsSpecifiedOnCommandLine() const = 0;
+ virtual std::string DefaultValue() const = 0;
+ virtual std::string CurrentValue() const = 0;
+
+ // Interfaces to operate on validators.
+ virtual bool ValidateInputValue(absl::string_view value) const = 0;
+
+ // Interface to save flag to some persistent state. Returns current flag state
+ // or nullptr if flag does not support saving and restoring a state.
+ virtual std::unique_ptr<FlagStateInterface> SaveState() = 0;
// 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`
@@ -280,106 +171,43 @@ struct CommandLineFlag {
// * Update the flag's default value
// * Update the current flag value if it was never set before
// The mode is selected based on `set_mode` parameter.
- bool SetFromString(absl::string_view value,
- flags_internal::FlagSettingMode set_mode,
- flags_internal::ValueSource source, std::string* error);
-
- void StoreAtomic(size_t size);
+ virtual bool SetFromString(absl::string_view value,
+ flags_internal::FlagSettingMode set_mode,
+ flags_internal::ValueSource source,
+ std::string* error) = 0;
- 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;
+ // Checks that flags default value can be converted to std::string and back to the
+ // flag's value type.
+ virtual void CheckDefaultValueParsingRoundtrip() const = 0;
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
+ ~CommandLineFlag() = default;
- // Mutable state (guarded by locks->primary_mu).
- 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.
- static const int64_t kAtomicInit = 0xababababababababll;
- std::atomic<int64_t> atomic;
-
- // Lazily initialized mutexes for this flag value. We cannot inline a
- // SpinLock or Mutex here because those have non-constexpr constructors and
- // so would prevent constant initialization of this type.
- // 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);
-
- 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);
+ private:
+ // Copy-construct a new value of the flag's type in a memory referenced by
+ // the dst based on the current flag's value.
+ virtual void Read(void* dst) const = 0;
};
-// 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
-// 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 UpdateCopy(CommandLineFlag* flag);
-// Return true iff flag value was changed via direct-access.
-bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b);
-
-// 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,
-// a one argument macro operating on a type, supplied as a macro argument, for
-// each type in the list.
-#define ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(A) \
- A(bool) \
- A(short) \
- A(unsigned short) \
- A(int) \
- A(unsigned int) \
- A(long) \
- A(unsigned long) \
- A(long long) \
- A(unsigned long long) \
- A(double) \
- A(float)
+// This macro is the "source of truth" for the list of supported flag built-in
+// types.
+#define ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(A) \
+ A(bool) \
+ A(short) \
+ A(unsigned short) \
+ A(int) \
+ A(unsigned int) \
+ A(long) \
+ A(unsigned long) \
+ A(long long) \
+ A(unsigned long long) \
+ A(double) \
+ A(float) \
+ A(std::string) \
+ A(std::vector<std::string>)
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
diff --git a/absl/flags/internal/commandlineflag_test.cc b/absl/flags/internal/commandlineflag_test.cc
index f0d57adb..0e8bc313 100644
--- a/absl/flags/internal/commandlineflag_test.cc
+++ b/absl/flags/internal/commandlineflag_test.cc
@@ -15,12 +15,17 @@
#include "absl/flags/internal/commandlineflag.h"
+#include <memory>
+#include <string>
+
#include "gtest/gtest.h"
#include "absl/flags/flag.h"
#include "absl/flags/internal/registry.h"
+#include "absl/flags/usage_config.h"
#include "absl/memory/memory.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
ABSL_FLAG(int, int_flag, 201, "int_flag help");
ABSL_FLAG(std::string, string_flag, "dflt",
@@ -33,10 +38,26 @@ namespace flags = absl::flags_internal;
class CommandLineFlagTest : public testing::Test {
protected:
+ static void SetUpTestSuite() {
+ // Install a function to normalize filenames before this test is run.
+ absl::FlagsUsageConfig default_config;
+ default_config.normalize_filename = &CommandLineFlagTest::NormalizeFileName;
+ absl::SetFlagsUsageConfig(default_config);
+ }
+
void SetUp() override { flag_saver_ = absl::make_unique<flags::FlagSaver>(); }
void TearDown() override { flag_saver_.reset(); }
private:
+ static std::string NormalizeFileName(absl::string_view fname) {
+#ifdef _WIN32
+ std::string normalized(fname);
+ std::replace(normalized.begin(), normalized.end(), '\\', '/');
+ fname = normalized;
+#endif
+ return std::string(fname);
+ }
+
std::unique_ptr<flags::FlagSaver> flag_saver_;
};
@@ -49,9 +70,10 @@ TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) {
EXPECT_EQ(flag_01->Typename(), "");
EXPECT_TRUE(!flag_01->IsRetired());
EXPECT_TRUE(flag_01->IsOfType<int>());
- EXPECT_TRUE(absl::EndsWith(
- flag_01->Filename(),
- "absl/flags/internal/commandlineflag_test.cc"));
+ EXPECT_TRUE(
+ absl::EndsWith(flag_01->Filename(),
+ "absl/flags/internal/commandlineflag_test.cc"))
+ << flag_01->Filename();
auto* flag_02 = flags::FindCommandLineFlag("string_flag");
@@ -61,9 +83,10 @@ TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) {
EXPECT_EQ(flag_02->Typename(), "");
EXPECT_TRUE(!flag_02->IsRetired());
EXPECT_TRUE(flag_02->IsOfType<std::string>());
- EXPECT_TRUE(absl::EndsWith(
- flag_02->Filename(),
- "absl/flags/internal/commandlineflag_test.cc"));
+ EXPECT_TRUE(
+ absl::EndsWith(flag_02->Filename(),
+ "absl/flags/internal/commandlineflag_test.cc"))
+ << flag_02->Filename();
auto* flag_03 = flags::FindRetiredFlag("bool_retired_flag");
diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc
new file mode 100644
index 00000000..5a921e28
--- /dev/null
+++ b/absl/flags/internal/flag.cc
@@ -0,0 +1,382 @@
+//
+// Copyright 2019 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/flags/internal/flag.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <atomic>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/base/const_init.h"
+#include "absl/base/optimization.h"
+#include "absl/flags/internal/commandlineflag.h"
+#include "absl/flags/usage_config.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "absl/synchronization/mutex.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace flags_internal {
+
+// The help message indicating that the commandline flag has been
+// 'stripped'. It will not show up when doing "-help" and its
+// variants. The flag is stripped if ABSL_FLAGS_STRIP_HELP is set to 1
+// before including absl/flags/flag.h
+const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
+
+namespace {
+
+// Currently we only validate flag values for user-defined flag types.
+bool ShouldValidateFlagValue(FlagStaticTypeId flag_type_id) {
+#define DONT_VALIDATE(T) \
+ if (flag_type_id == &FlagStaticTypeIdGen<T>) return false;
+ ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(DONT_VALIDATE)
+#undef DONT_VALIDATE
+
+ return true;
+}
+
+// RAII helper used to temporarily unlock and relock `absl::Mutex`.
+// This is used when we need to ensure that locks are released while
+// invoking user supplied callbacks and then reacquired, since callbacks may
+// need to acquire these locks themselves.
+class MutexRelock {
+ public:
+ explicit MutexRelock(absl::Mutex* mu) : mu_(mu) { mu_->Unlock(); }
+ ~MutexRelock() { mu_->Lock(); }
+
+ MutexRelock(const MutexRelock&) = delete;
+ MutexRelock& operator=(const MutexRelock&) = delete;
+
+ private:
+ absl::Mutex* mu_;
+};
+
+} // namespace
+
+void FlagImpl::Init() {
+ new (&data_guard_) absl::Mutex;
+
+ absl::MutexLock lock(reinterpret_cast<absl::Mutex*>(&data_guard_));
+
+ value_.dynamic = MakeInitValue().release();
+ StoreAtomic();
+}
+
+// Ensures that the lazily initialized data is initialized,
+// and returns pointer to the mutex guarding flags data.
+absl::Mutex* FlagImpl::DataGuard() const {
+ absl::call_once(const_cast<FlagImpl*>(this)->init_control_, &FlagImpl::Init,
+ const_cast<FlagImpl*>(this));
+
+ // data_guard_ is initialized.
+ return reinterpret_cast<absl::Mutex*>(&data_guard_);
+}
+
+void FlagImpl::AssertValidType(FlagStaticTypeId type_id) const {
+ FlagStaticTypeId this_type_id = flags_internal::StaticTypeId(op_);
+
+ // `type_id` is the type id corresponding to the declaration visibile at the
+ // call site. `this_type_id` is the type id corresponding to the type stored
+ // during flag definition. They must match for this operation to be
+ // well-defined.
+ if (ABSL_PREDICT_TRUE(type_id == this_type_id)) return;
+
+ void* lhs_runtime_type_id = type_id();
+ void* rhs_runtime_type_id = this_type_id();
+
+ if (lhs_runtime_type_id == rhs_runtime_type_id) return;
+
+#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI)
+ if (*reinterpret_cast<std::type_info*>(lhs_runtime_type_id) ==
+ *reinterpret_cast<std::type_info*>(rhs_runtime_type_id))
+ return;
+#endif
+
+ ABSL_INTERNAL_LOG(
+ FATAL, absl::StrCat("Flag '", Name(),
+ "' is defined as one type and declared as another"));
+}
+
+std::unique_ptr<void, DynValueDeleter> FlagImpl::MakeInitValue() const {
+ void* res = nullptr;
+ if (DefaultKind() == FlagDefaultKind::kDynamicValue) {
+ res = flags_internal::Clone(op_, default_value_.dynamic_value);
+ } else {
+ res = (*default_value_.gen_func)();
+ }
+ return {res, DynValueDeleter{op_}};
+}
+
+void FlagImpl::StoreValue(const void* src) {
+ flags_internal::Copy(op_, src, value_.dynamic);
+ StoreAtomic();
+ modified_ = true;
+ ++counter_;
+ InvokeCallback();
+}
+
+absl::string_view FlagImpl::Name() const { return name_; }
+
+std::string FlagImpl::Filename() const {
+ return flags_internal::GetUsageConfig().normalize_filename(filename_);
+}
+
+std::string FlagImpl::Help() const {
+ return HelpSourceKind() == FlagHelpKind::kLiteral ? help_.literal
+ : help_.gen_func();
+}
+
+bool FlagImpl::IsModified() const {
+ absl::MutexLock l(DataGuard());
+ return modified_;
+}
+
+bool FlagImpl::IsSpecifiedOnCommandLine() const {
+ absl::MutexLock l(DataGuard());
+ return on_command_line_;
+}
+
+std::string FlagImpl::DefaultValue() const {
+ absl::MutexLock l(DataGuard());
+
+ auto obj = MakeInitValue();
+ return flags_internal::Unparse(op_, obj.get());
+}
+
+std::string FlagImpl::CurrentValue() const {
+ absl::MutexLock l(DataGuard());
+
+ return flags_internal::Unparse(op_, value_.dynamic);
+}
+
+void FlagImpl::SetCallback(const FlagCallbackFunc mutation_callback) {
+ absl::MutexLock l(DataGuard());
+
+ if (callback_ == nullptr) {
+ callback_ = new FlagCallback;
+ }
+ callback_->func = mutation_callback;
+
+ InvokeCallback();
+}
+
+void FlagImpl::InvokeCallback() const {
+ if (!callback_) return;
+
+ // Make a copy of the C-style function pointer that we are about to invoke
+ // before we release the lock guarding it.
+ FlagCallbackFunc cb = callback_->func;
+
+ // 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.
+ MutexRelock relock(DataGuard());
+ absl::MutexLock lock(&callback_->guard);
+ cb();
+}
+
+bool FlagImpl::RestoreState(const void* value, bool modified,
+ bool on_command_line, int64_t counter) {
+ {
+ absl::MutexLock l(DataGuard());
+
+ if (counter_ == counter) return false;
+ }
+
+ Write(value);
+
+ {
+ absl::MutexLock l(DataGuard());
+
+ modified_ = modified;
+ on_command_line_ = on_command_line;
+ }
+
+ return true;
+}
+
+// Attempts to parse supplied `value` string using parsing routine in the `flag`
+// argument. If parsing successful, this function replaces the dst with newly
+// parsed value. In case if any error is encountered in either step, the error
+// message is stored in 'err'
+std::unique_ptr<void, DynValueDeleter> FlagImpl::TryParse(
+ absl::string_view value, std::string* err) const {
+ std::unique_ptr<void, DynValueDeleter> tentative_value = MakeInitValue();
+
+ std::string parse_err;
+ if (!flags_internal::Parse(op_, value, tentative_value.get(), &parse_err)) {
+ absl::string_view err_sep = parse_err.empty() ? "" : "; ";
+ *err = absl::StrCat("Illegal value '", value, "' specified for flag '",
+ Name(), "'", err_sep, parse_err);
+ return nullptr;
+ }
+
+ return tentative_value;
+}
+
+void FlagImpl::Read(void* dst) const {
+ absl::ReaderMutexLock l(DataGuard());
+
+ flags_internal::CopyConstruct(op_, value_.dynamic, dst);
+}
+
+void FlagImpl::StoreAtomic() {
+ size_t data_size = flags_internal::Sizeof(op_);
+
+ if (data_size <= sizeof(int64_t)) {
+ int64_t t = 0;
+ std::memcpy(&t, value_.dynamic, data_size);
+ value_.atomics.small_atomic.store(t, std::memory_order_release);
+ }
+#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD)
+ else if (data_size <= sizeof(FlagsInternalTwoWordsType)) {
+ FlagsInternalTwoWordsType t{0, 0};
+ std::memcpy(&t, value_.dynamic, data_size);
+ value_.atomics.big_atomic.store(t, std::memory_order_release);
+ }
+#endif
+}
+
+void FlagImpl::Write(const void* src) {
+ absl::MutexLock l(DataGuard());
+
+ if (ShouldValidateFlagValue(flags_internal::StaticTypeId(op_))) {
+ std::unique_ptr<void, DynValueDeleter> obj{flags_internal::Clone(op_, src),
+ DynValueDeleter{op_}};
+ std::string ignored_error;
+ std::string src_as_str = flags_internal::Unparse(op_, src);
+ if (!flags_internal::Parse(op_, src_as_str, obj.get(), &ignored_error)) {
+ ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '", Name(),
+ "' to invalid value ", src_as_str));
+ }
+ }
+
+ StoreValue(src);
+}
+
+// Sets the value of the flag based on specified string `value`. If the flag
+// was successfully set to new value, it returns true. Otherwise, sets `err`
+// to indicate the error, leaves the flag unchanged, and returns false. There
+// are three ways to set the flag's value:
+// * Update the current flag value
+// * Update the flag's default value
+// * Update the current flag value if it was never set before
+// The mode is selected based on 'set_mode' parameter.
+bool FlagImpl::SetFromString(absl::string_view value, FlagSettingMode set_mode,
+ ValueSource source, std::string* err) {
+ absl::MutexLock l(DataGuard());
+
+ switch (set_mode) {
+ case SET_FLAGS_VALUE: {
+ // set or modify the flag's value
+ auto tentative_value = TryParse(value, err);
+ if (!tentative_value) return false;
+
+ StoreValue(tentative_value.get());
+
+ if (source == kCommandLine) {
+ on_command_line_ = true;
+ }
+ break;
+ }
+ case SET_FLAG_IF_DEFAULT: {
+ // set the flag's value, but only if it hasn't been set by someone else
+ if (modified_) {
+ // 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
+ // value is not updated even though we return true.
+ // *err = absl::StrCat(Name(), " is already set to ",
+ // CurrentValue(), "\n");
+ // return false;
+ return true;
+ }
+ auto tentative_value = TryParse(value, err);
+ if (!tentative_value) return false;
+
+ StoreValue(tentative_value.get());
+ break;
+ }
+ case SET_FLAGS_DEFAULT: {
+ auto tentative_value = TryParse(value, err);
+ if (!tentative_value) return false;
+
+ if (DefaultKind() == FlagDefaultKind::kDynamicValue) {
+ void* old_value = default_value_.dynamic_value;
+ default_value_.dynamic_value = tentative_value.release();
+ tentative_value.reset(old_value);
+ } else {
+ default_value_.dynamic_value = tentative_value.release();
+ def_kind_ = static_cast<uint8_t>(FlagDefaultKind::kDynamicValue);
+ }
+
+ if (!modified_) {
+ // Need to set both default value *and* current, in this case
+ StoreValue(default_value_.dynamic_value);
+ modified_ = false;
+ }
+ break;
+ }
+ }
+
+ return true;
+}
+
+void FlagImpl::CheckDefaultValueParsingRoundtrip() const {
+ std::string v = DefaultValue();
+
+ absl::MutexLock lock(DataGuard());
+
+ auto dst = MakeInitValue();
+ std::string error;
+ if (!flags_internal::Parse(op_, v, dst.get(), &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.
+}
+
+bool FlagImpl::ValidateInputValue(absl::string_view value) const {
+ absl::MutexLock l(DataGuard());
+
+ auto obj = MakeInitValue();
+ std::string ignored_error;
+ return flags_internal::Parse(op_, value, obj.get(), &ignored_error);
+}
+
+} // namespace flags_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h
index 9a5d9b4b..35a148cf 100644
--- a/absl/flags/internal/flag.h
+++ b/absl/flags/internal/flag.h
@@ -16,45 +16,547 @@
#ifndef ABSL_FLAGS_INTERNAL_FLAG_H_
#define ABSL_FLAGS_INTERNAL_FLAG_H_
+#include <stdint.h>
+
+#include <atomic>
+#include <cstring>
+#include <memory>
+#include <string>
+#include <type_traits>
+
+#include "absl/base/call_once.h"
+#include "absl/base/config.h"
+#include "absl/base/thread_annotations.h"
+#include "absl/flags/config.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/registry.h"
+#include "absl/memory/memory.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
-// This is "unspecified" implementation of absl::Flag<T> type.
template <typename T>
-class Flag : public flags_internal::CommandLineFlag {
+class Flag;
+
+///////////////////////////////////////////////////////////////////////////////
+// Flag value type operations, eg., parsing, copying, etc. are provided
+// by function specific to that type with a signature matching FlagOpFn.
+
+enum class FlagOp {
+ kDelete,
+ kClone,
+ kCopy,
+ kCopyConstruct,
+ kSizeof,
+ kStaticTypeId,
+ kParse,
+ kUnparse,
+};
+using FlagOpFn = void* (*)(FlagOp, const void*, void*, void*);
+
+// Flag value specific operations routine.
+template <typename T>
+void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3) {
+ switch (op) {
+ case FlagOp::kDelete:
+ delete static_cast<const T*>(v1);
+ return nullptr;
+ case FlagOp::kClone:
+ return new T(*static_cast<const T*>(v1));
+ case FlagOp::kCopy:
+ *static_cast<T*>(v2) = *static_cast<const T*>(v1);
+ return nullptr;
+ case FlagOp::kCopyConstruct:
+ new (v2) T(*static_cast<const T*>(v1));
+ return nullptr;
+ case FlagOp::kSizeof:
+ return reinterpret_cast<void*>(sizeof(T));
+ case FlagOp::kStaticTypeId:
+ return reinterpret_cast<void*>(&FlagStaticTypeIdGen<T>);
+ case FlagOp::kParse: {
+ // Initialize the temporary instance of type T based on current value in
+ // destination (which is going to be flag's default value).
+ T temp(*static_cast<T*>(v2));
+ if (!absl::ParseFlag<T>(*static_cast<const absl::string_view*>(v1), &temp,
+ static_cast<std::string*>(v3))) {
+ return nullptr;
+ }
+ *static_cast<T*>(v2) = std::move(temp);
+ return v2;
+ }
+ case FlagOp::kUnparse:
+ *static_cast<std::string*>(v2) =
+ absl::UnparseFlag<T>(*static_cast<const T*>(v1));
+ return nullptr;
+ default:
+ return nullptr;
+ }
+}
+
+// Deletes memory interpreting obj as flag value type pointer.
+inline void Delete(FlagOpFn op, const void* obj) {
+ op(FlagOp::kDelete, obj, nullptr, nullptr);
+}
+// Makes a copy of flag value pointed by obj.
+inline void* Clone(FlagOpFn op, const void* obj) {
+ return op(FlagOp::kClone, obj, nullptr, nullptr);
+}
+// Copies src to dst interpreting as flag value type pointers.
+inline void Copy(FlagOpFn op, const void* src, void* dst) {
+ op(FlagOp::kCopy, src, dst, nullptr);
+}
+// Construct a copy of flag value in a location pointed by dst
+// based on src - pointer to the flag's value.
+inline void CopyConstruct(FlagOpFn op, const void* src, void* dst) {
+ op(FlagOp::kCopyConstruct, src, dst, nullptr);
+}
+// Returns true if parsing of input text is successfull.
+inline bool Parse(FlagOpFn op, absl::string_view text, void* dst,
+ std::string* error) {
+ return op(FlagOp::kParse, &text, dst, error) != nullptr;
+}
+// Returns string representing supplied value.
+inline std::string Unparse(FlagOpFn op, const void* val) {
+ std::string result;
+ op(FlagOp::kUnparse, val, &result, nullptr);
+ return result;
+}
+// Returns size of flag value type.
+inline size_t Sizeof(FlagOpFn op) {
+ // This sequence of casts reverses the sequence from
+ // `flags_internal::FlagOps()`
+ return static_cast<size_t>(reinterpret_cast<intptr_t>(
+ op(FlagOp::kSizeof, nullptr, nullptr, nullptr)));
+}
+// Returns static type id coresponding to the value type.
+inline FlagStaticTypeId StaticTypeId(FlagOpFn op) {
+ return reinterpret_cast<FlagStaticTypeId>(
+ op(FlagOp::kStaticTypeId, nullptr, nullptr, nullptr));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Persistent state of the flag data.
+
+template <typename T>
+class FlagState : public flags_internal::FlagStateInterface {
+ public:
+ FlagState(Flag<T>* flag, T&& cur, bool modified, bool on_command_line,
+ int64_t counter)
+ : flag_(flag),
+ cur_value_(std::move(cur)),
+ modified_(modified),
+ on_command_line_(on_command_line),
+ counter_(counter) {}
+
+ ~FlagState() override = default;
+
+ private:
+ friend class Flag<T>;
+
+ // Restores the flag to the saved state.
+ void Restore() const override;
+
+ // Flag and saved flag data.
+ Flag<T>* flag_;
+ T cur_value_;
+ bool modified_;
+ bool on_command_line_;
+ int64_t counter_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Flag help auxiliary structs.
+
+// This is help argument for absl::Flag encapsulating the string literal pointer
+// or pointer to function generating it as well as enum descriminating two
+// cases.
+using HelpGenFunc = std::string (*)();
+
+union FlagHelpMsg {
+ constexpr explicit FlagHelpMsg(const char* help_msg) : literal(help_msg) {}
+ constexpr explicit FlagHelpMsg(HelpGenFunc help_gen) : gen_func(help_gen) {}
+
+ const char* literal;
+ HelpGenFunc gen_func;
+};
+
+enum class FlagHelpKind : uint8_t { kLiteral = 0, kGenFunc = 1 };
+
+struct FlagHelpArg {
+ FlagHelpMsg source;
+ FlagHelpKind kind;
+};
+
+extern const char kStrippedFlagHelp[];
+
+// HelpConstexprWrap is used by struct AbslFlagHelpGenFor##name generated by
+// ABSL_FLAG macro. It is only used to silence the compiler in the case where
+// help message expression is not constexpr and does not have type const char*.
+// If help message expression is indeed constexpr const char* HelpConstexprWrap
+// is just a trivial identity function.
+template <typename T>
+const char* HelpConstexprWrap(const T&) {
+ return nullptr;
+}
+constexpr const char* HelpConstexprWrap(const char* p) { return p; }
+constexpr const char* HelpConstexprWrap(char* p) { return p; }
+
+// These two HelpArg overloads allows us to select at compile time one of two
+// way to pass Help argument to absl::Flag. We'll be passing
+// AbslFlagHelpGenFor##name as T and integer 0 as a single argument to prefer
+// first overload if possible. If T::Const is evaluatable on constexpr
+// context (see non template int parameter below) we'll choose first overload.
+// In this case the help message expression is immediately evaluated and is used
+// to construct the absl::Flag. No additionl code is generated by ABSL_FLAG.
+// Otherwise SFINAE kicks in and first overload is dropped from the
+// consideration, in which case the second overload will be used. The second
+// overload does not attempt to evaluate the help message expression
+// immediately and instead delays the evaluation by returing the function
+// pointer (&T::NonConst) genering the help message when necessary. This is
+// evaluatable in constexpr context, but the cost is an extra function being
+// generated in the ABSL_FLAG code.
+template <typename T, int = (T::Const(), 1)>
+constexpr FlagHelpArg HelpArg(int) {
+ return {FlagHelpMsg(T::Const()), FlagHelpKind::kLiteral};
+}
+
+template <typename T>
+constexpr FlagHelpArg HelpArg(char) {
+ return {FlagHelpMsg(&T::NonConst), FlagHelpKind::kGenFunc};
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Flag default value auxiliary structs.
+
+// Signature for the function generating the initial flag value (usually
+// based on default value supplied in flag's definition)
+using FlagDfltGenFunc = void* (*)();
+
+union FlagDefaultSrc {
+ constexpr explicit FlagDefaultSrc(FlagDfltGenFunc gen_func_arg)
+ : gen_func(gen_func_arg) {}
+
+ void* dynamic_value;
+ FlagDfltGenFunc gen_func;
+};
+
+enum class FlagDefaultKind : uint8_t { kDynamicValue = 0, kGenFunc = 1 };
+
+///////////////////////////////////////////////////////////////////////////////
+// Flag current value auxiliary structs.
+
+// The minimum atomic size we believe to generate lock free code, i.e. all
+// trivially copyable types not bigger this size generate lock free code.
+static constexpr int kMinLockFreeAtomicSize = 8;
+
+// The same as kMinLockFreeAtomicSize but maximum atomic size. As double words
+// might use two registers, we want to dispatch the logic for them.
+#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD)
+static constexpr int kMaxLockFreeAtomicSize = 16;
+#else
+static constexpr int kMaxLockFreeAtomicSize = 8;
+#endif
+
+// We can use atomic in cases when it fits in the register, trivially copyable
+// in order to make memcpy operations.
+template <typename T>
+struct IsAtomicFlagTypeTrait {
+ static constexpr bool value =
+ (sizeof(T) <= kMaxLockFreeAtomicSize &&
+ type_traits_internal::is_trivially_copyable<T>::value);
+};
+
+// Clang does not always produce cmpxchg16b instruction when alignment of a 16
+// bytes type is not 16.
+struct alignas(16) FlagsInternalTwoWordsType {
+ int64_t first;
+ int64_t second;
+};
+
+constexpr bool operator==(const FlagsInternalTwoWordsType& that,
+ const FlagsInternalTwoWordsType& other) {
+ return that.first == other.first && that.second == other.second;
+}
+constexpr bool operator!=(const FlagsInternalTwoWordsType& that,
+ const FlagsInternalTwoWordsType& other) {
+ return !(that == other);
+}
+
+constexpr int64_t SmallAtomicInit() { return 0xababababababababll; }
+
+template <typename T, typename S = void>
+struct BestAtomicType {
+ using type = int64_t;
+ static constexpr int64_t AtomicInit() { return SmallAtomicInit(); }
+};
+
+template <typename T>
+struct BestAtomicType<
+ T, typename std::enable_if<(kMinLockFreeAtomicSize < sizeof(T) &&
+ sizeof(T) <= kMaxLockFreeAtomicSize),
+ void>::type> {
+ using type = FlagsInternalTwoWordsType;
+ static constexpr FlagsInternalTwoWordsType AtomicInit() {
+ return {SmallAtomicInit(), SmallAtomicInit()};
+ }
+};
+
+struct FlagValue {
+ // Heap allocated value.
+ void* dynamic = nullptr;
+ // For some types, a copy of the current value is kept in an atomically
+ // accessible field.
+ union Atomics {
+ // Using small atomic for small types.
+ std::atomic<int64_t> small_atomic;
+ template <typename T,
+ typename K = typename std::enable_if<
+ (sizeof(T) <= kMinLockFreeAtomicSize), void>::type>
+ int64_t load() const {
+ return small_atomic.load(std::memory_order_acquire);
+ }
+
+#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD)
+ // Using big atomics for big types.
+ std::atomic<FlagsInternalTwoWordsType> big_atomic;
+ template <typename T, typename K = typename std::enable_if<
+ (kMinLockFreeAtomicSize < sizeof(T) &&
+ sizeof(T) <= kMaxLockFreeAtomicSize),
+ void>::type>
+ FlagsInternalTwoWordsType load() const {
+ return big_atomic.load(std::memory_order_acquire);
+ }
+ constexpr Atomics()
+ : big_atomic{FlagsInternalTwoWordsType{SmallAtomicInit(),
+ SmallAtomicInit()}} {}
+#else
+ constexpr Atomics() : small_atomic{SmallAtomicInit()} {}
+#endif
+ };
+ Atomics atomics{};
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Flag callback auxiliary structs.
+
+// Signature for the mutation callback used by watched Flags
+// The callback is noexcept.
+// TODO(rogeeff): add noexcept after C++17 support is added.
+using FlagCallbackFunc = void (*)();
+
+struct FlagCallback {
+ FlagCallbackFunc func;
+ absl::Mutex guard; // Guard for concurrent callback invocations.
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Flag implementation, which does not depend on flag value type.
+// The class encapsulates the Flag's data and access to it.
+
+struct DynValueDeleter {
+ explicit DynValueDeleter(FlagOpFn op_arg = nullptr) : op(op_arg) {}
+ void operator()(void* ptr) const {
+ if (op != nullptr) Delete(op, ptr);
+ }
+
+ FlagOpFn op;
+};
+
+class FlagImpl {
public:
- constexpr Flag(const char* name, const flags_internal::HelpGenFunc help_gen,
- const char* filename,
- const flags_internal::FlagMarshallingOpFn marshalling_op_arg,
- const flags_internal::InitialValGenFunc initial_value_gen)
- : 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) {}
+ constexpr FlagImpl(const char* name, const char* filename, FlagOpFn op,
+ FlagHelpArg help, FlagDfltGenFunc default_value_gen)
+ : name_(name),
+ filename_(filename),
+ op_(op),
+ help_(help.source),
+ help_source_kind_(static_cast<uint8_t>(help.kind)),
+ def_kind_(static_cast<uint8_t>(FlagDefaultKind::kGenFunc)),
+ modified_(false),
+ on_command_line_(false),
+ counter_(0),
+ callback_(nullptr),
+ default_value_(default_value_gen),
+ data_guard_{} {}
+
+ // Constant access methods
+ absl::string_view Name() const;
+ std::string Filename() const;
+ std::string Help() const;
+ bool IsModified() const ABSL_LOCKS_EXCLUDED(*DataGuard());
+ bool IsSpecifiedOnCommandLine() const ABSL_LOCKS_EXCLUDED(*DataGuard());
+ std::string DefaultValue() const ABSL_LOCKS_EXCLUDED(*DataGuard());
+ std::string CurrentValue() const ABSL_LOCKS_EXCLUDED(*DataGuard());
+ void Read(void* dst) const ABSL_LOCKS_EXCLUDED(*DataGuard());
+
+ template <typename T, typename std::enable_if<
+ !IsAtomicFlagTypeTrait<T>::value, int>::type = 0>
+ void Get(T* dst) const {
+ AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>);
+ Read(dst);
+ }
+ // Overload for `GetFlag()` for types that support lock-free reads.
+ template <typename T, typename std::enable_if<IsAtomicFlagTypeTrait<T>::value,
+ int>::type = 0>
+ void Get(T* dst) const {
+ // For flags of types which can be accessed "atomically" we want to avoid
+ // slowing down flag value access due to type validation. That's why
+ // this validation is hidden behind !NDEBUG
+#ifndef NDEBUG
+ AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>);
+#endif
+ using U = flags_internal::BestAtomicType<T>;
+ typename U::type r = value_.atomics.template load<T>();
+ if (r != U::AtomicInit()) {
+ std::memcpy(static_cast<void*>(dst), &r, sizeof(T));
+ } else {
+ Read(dst);
+ }
+ }
+ template <typename T>
+ void Set(const T& src) {
+ AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>);
+ Write(&src);
+ }
+
+ // Mutating access methods
+ void Write(const void* src) ABSL_LOCKS_EXCLUDED(*DataGuard());
+ bool SetFromString(absl::string_view value, FlagSettingMode set_mode,
+ ValueSource source, std::string* err)
+ ABSL_LOCKS_EXCLUDED(*DataGuard());
+ // If possible, updates copy of the Flag's value that is stored in an
+ // atomic word.
+ void StoreAtomic() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
+
+ // Interfaces to operate on callbacks.
+ void SetCallback(const FlagCallbackFunc mutation_callback)
+ ABSL_LOCKS_EXCLUDED(*DataGuard());
+ void InvokeCallback() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
+
+ // Interfaces to save/restore mutable flag data
+ template <typename T>
+ std::unique_ptr<FlagStateInterface> SaveState(Flag<T>* flag) const
+ ABSL_LOCKS_EXCLUDED(*DataGuard()) {
+ T&& cur_value = flag->Get();
+ absl::MutexLock l(DataGuard());
+
+ return absl::make_unique<FlagState<T>>(
+ flag, std::move(cur_value), modified_, on_command_line_, counter_);
+ }
+ bool RestoreState(const void* value, bool modified, bool on_command_line,
+ int64_t counter) ABSL_LOCKS_EXCLUDED(*DataGuard());
+
+ // Value validation interfaces.
+ void CheckDefaultValueParsingRoundtrip() const
+ ABSL_LOCKS_EXCLUDED(*DataGuard());
+ bool ValidateInputValue(absl::string_view value) const
+ ABSL_LOCKS_EXCLUDED(*DataGuard());
+
+ private:
+ // Ensures that `data_guard_` is initialized and returns it.
+ absl::Mutex* DataGuard() const ABSL_LOCK_RETURNED((absl::Mutex*)&data_guard_);
+ // Returns heap allocated value of type T initialized with default value.
+ std::unique_ptr<void, DynValueDeleter> MakeInitValue() const
+ ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
+ // Flag initialization called via absl::call_once.
+ void Init();
+ // Attempts to parse supplied `value` std::string. If parsing is successful,
+ // returns new value. Otherwise returns nullptr.
+ std::unique_ptr<void, DynValueDeleter> TryParse(absl::string_view value,
+ std::string* err) const
+ ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
+ // Stores the flag value based on the pointer to the source.
+ void StoreValue(const void* src) ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
+
+ FlagHelpKind HelpSourceKind() const {
+ return static_cast<FlagHelpKind>(help_source_kind_);
+ }
+ FlagDefaultKind DefaultKind() const
+ ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()) {
+ return static_cast<FlagDefaultKind>(def_kind_);
+ }
+ // Used in read/write operations to validate source/target has correct type.
+ // For example if flag is declared as absl::Flag<int> FLAGS_foo, a call to
+ // absl::GetFlag(FLAGS_foo) validates that the type of FLAGS_foo is indeed
+ // int. To do that we pass the "assumed" type id (which is deduced from type
+ // int) as an argument `op`, which is in turn is validated against the type id
+ // stored in flag object by flag definition statement.
+ void AssertValidType(FlagStaticTypeId type_id) const;
+
+ // Immutable flag's state.
+
+ // Flags name passed to ABSL_FLAG as second arg.
+ const char* const name_;
+ // The file name where ABSL_FLAG resides.
+ const char* const filename_;
+ // Type-specific operations "vtable".
+ const FlagOpFn op_;
+ // Help message literal or function to generate it.
+ const FlagHelpMsg help_;
+ // Indicates if help message was supplied as literal or generator func.
+ const uint8_t help_source_kind_ : 1;
+
+ // ------------------------------------------------------------------------
+ // The bytes containing the const bitfields must not be shared with bytes
+ // containing the mutable bitfields.
+ // ------------------------------------------------------------------------
+
+ // Unique tag for absl::call_once call to initialize this flag.
+ //
+ // The placement of this variable between the immutable and mutable bitfields
+ // is important as prevents them from occupying the same byte. If you remove
+ // this variable, make sure to maintain this property.
+ absl::once_flag init_control_;
+
+ // Mutable flag's state (guarded by `data_guard_`).
+
+ // If def_kind_ == kDynamicValue, default_value_ holds a dynamically allocated
+ // value.
+ uint8_t def_kind_ : 1 ABSL_GUARDED_BY(*DataGuard());
+ // Has this flag's value been modified?
+ bool modified_ : 1 ABSL_GUARDED_BY(*DataGuard());
+ // Has this flag been specified on command line.
+ bool on_command_line_ : 1 ABSL_GUARDED_BY(*DataGuard());
+
+ // Mutation counter
+ int64_t counter_ ABSL_GUARDED_BY(*DataGuard());
+ // Optional flag's callback and absl::Mutex to guard the invocations.
+ FlagCallback* callback_ ABSL_GUARDED_BY(*DataGuard());
+ // Either a pointer to the function generating the default value based on the
+ // value specified in ABSL_FLAG or pointer to the dynamically set default
+ // value via SetCommandLineOptionWithMode. def_kind_ is used to distinguish
+ // these two cases.
+ FlagDefaultSrc default_value_ ABSL_GUARDED_BY(*DataGuard());
+ // Current Flag Value
+ FlagValue value_;
+
+ // This is reserved space for an absl::Mutex to guard flag data. It will be
+ // initialized in FlagImpl::Init via placement new.
+ // We can't use "absl::Mutex data_guard_", since this class is not literal.
+ // We do not want to use "absl::Mutex* data_guard_", since this would require
+ // heap allocation during initialization, which is both slows program startup
+ // and can fail. Using reserved space + placement new allows us to avoid both
+ // problems.
+ alignas(absl::Mutex) mutable char data_guard_[sizeof(absl::Mutex)];
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// The Flag object parameterized by the flag's value type. This class implements
+// flag reflection handle interface.
+
+template <typename T>
+class Flag final : public flags_internal::CommandLineFlag {
+ public:
+ constexpr Flag(const char* name, const char* filename, const FlagHelpArg help,
+ const FlagDfltGenFunc default_value_gen)
+ : impl_(name, filename, &FlagOps<T>, help, default_value_gen) {}
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.
+ // See implementation notes in CommandLineFlag::Get().
union U {
T value;
U() {}
@@ -62,23 +564,69 @@ class Flag : public flags_internal::CommandLineFlag {
};
U u;
- this->Read(&u.value, &flags_internal::FlagOps<T>);
+ impl_.Get(&u.value);
return std::move(u.value);
}
+ void Set(const T& v) { impl_.Set(v); }
+ void SetCallback(const FlagCallbackFunc mutation_callback) {
+ impl_.SetCallback(mutation_callback);
+ }
- 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;
- }
+ // CommandLineFlag interface
+ absl::string_view Name() const override { return impl_.Name(); }
+ std::string Filename() const override { return impl_.Filename(); }
+ absl::string_view Typename() const override { return ""; }
+ std::string Help() const override { return impl_.Help(); }
+ bool IsModified() const override { return impl_.IsModified(); }
+ bool IsSpecifiedOnCommandLine() const override {
+ return impl_.IsSpecifiedOnCommandLine();
+ }
+ std::string DefaultValue() const override { return impl_.DefaultValue(); }
+ std::string CurrentValue() const override { return impl_.CurrentValue(); }
+ bool ValidateInputValue(absl::string_view value) const override {
+ return impl_.ValidateInputValue(value);
+ }
+
+ // Interfaces to save and restore flags to/from persistent state.
+ // Returns current flag state or nullptr if flag does not support
+ // saving and restoring a state.
+ std::unique_ptr<FlagStateInterface> SaveState() override {
+ return impl_.SaveState(this);
+ }
- return false;
+ // Restores the flag state to the supplied state object. If there is
+ // nothing to restore returns false. Otherwise returns true.
+ bool RestoreState(const FlagState<T>& flag_state) {
+ return impl_.RestoreState(&flag_state.cur_value_, flag_state.modified_,
+ flag_state.on_command_line_, flag_state.counter_);
+ }
+ bool SetFromString(absl::string_view value, FlagSettingMode set_mode,
+ ValueSource source, std::string* error) override {
+ return impl_.SetFromString(value, set_mode, source, error);
}
+ void CheckDefaultValueParsingRoundtrip() const override {
+ impl_.CheckDefaultValueParsingRoundtrip();
+ }
+
+ private:
+ friend class FlagState<T>;
+
+ void Read(void* dst) const override { impl_.Read(dst); }
+ FlagStaticTypeId TypeId() const override { return &FlagStaticTypeIdGen<T>; }
- void Set(const T& v) { this->Write(&v, &flags_internal::FlagOps<T>); }
+ // Flag's data
+ FlagImpl impl_;
};
+template <typename T>
+inline void FlagState<T>::Restore() const {
+ if (flag_->RestoreState(*this)) {
+ ABSL_INTERNAL_LOG(INFO,
+ absl::StrCat("Restore saved value of ", flag_->Name(),
+ " to: ", flag_->CurrentValue()));
+ }
+}
+
// This class facilitates Flag object registration and tail expression-based
// flag definition, for example:
// ABSL_FLAG(int, foo, 42, "Foo help").OnUpdate(NotifyFooWatcher);
@@ -89,7 +637,7 @@ class FlagRegistrar {
if (do_register) flags_internal::RegisterCommandLineFlag(flag_);
}
- FlagRegistrar& OnUpdate(flags_internal::FlagCallback cb) && {
+ FlagRegistrar& OnUpdate(FlagCallbackFunc cb) && {
flag_->SetCallback(cb);
return *this;
}
@@ -117,7 +665,7 @@ T* MakeFromDefaultValue(EmptyBraces) {
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_INTERNAL_FLAG_H_
diff --git a/absl/flags/internal/parse.h b/absl/flags/internal/parse.h
index 6c79dc87..03e8a07b 100644
--- a/absl/flags/internal/parse.h
+++ b/absl/flags/internal/parse.h
@@ -19,6 +19,7 @@
#include <string>
#include <vector>
+#include "absl/base/config.h"
#include "absl/flags/declare.h"
ABSL_DECLARE_FLAG(std::vector<std::string>, flagfile);
@@ -27,7 +28,7 @@ ABSL_DECLARE_FLAG(std::vector<std::string>, tryfromenv);
ABSL_DECLARE_FLAG(std::vector<std::string>, undefok);
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
enum class ArgvListAction { kRemoveParsedArgs, kKeepParsedArgs };
@@ -44,7 +45,7 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
OnUndefinedFlag on_undef_flag);
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_INTERNAL_PARSE_H_
diff --git a/absl/flags/internal/path_util.h b/absl/flags/internal/path_util.h
index 623a7bc9..365c8305 100644
--- a/absl/flags/internal/path_util.h
+++ b/absl/flags/internal/path_util.h
@@ -16,11 +16,12 @@
#ifndef ABSL_FLAGS_INTERNAL_PATH_UTIL_H_
#define ABSL_FLAGS_INTERNAL_PATH_UTIL_H_
+#include "absl/base/config.h"
#include "absl/strings/match.h"
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
// A portable interface that returns the basename of the filename passed as an
@@ -56,7 +57,7 @@ inline absl::string_view Package(absl::string_view filename) {
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_INTERNAL_PATH_UTIL_H_
diff --git a/absl/flags/internal/program_name.cc b/absl/flags/internal/program_name.cc
index 28b8ed38..51d698da 100644
--- a/absl/flags/internal/program_name.cc
+++ b/absl/flags/internal/program_name.cc
@@ -17,16 +17,21 @@
#include <string>
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/base/const_init.h"
+#include "absl/base/thread_annotations.h"
#include "absl/flags/internal/path_util.h"
+#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
ABSL_CONST_INIT static absl::Mutex program_name_guard(absl::kConstInit);
ABSL_CONST_INIT static std::string* program_name
- GUARDED_BY(program_name_guard) = nullptr;
+ ABSL_GUARDED_BY(program_name_guard) = nullptr;
std::string ProgramInvocationName() {
absl::MutexLock l(&program_name_guard);
@@ -51,5 +56,5 @@ void SetProgramInvocationName(absl::string_view prog_name_str) {
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/internal/program_name.h b/absl/flags/internal/program_name.h
index a2f0ca10..b99b94fe 100644
--- a/absl/flags/internal/program_name.h
+++ b/absl/flags/internal/program_name.h
@@ -18,13 +18,14 @@
#include <string>
+#include "absl/base/config.h"
#include "absl/strings/string_view.h"
// --------------------------------------------------------------------
// Program name
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
// Returns program invocation name or "UNKNOWN" if `SetProgramInvocationName()`
@@ -43,7 +44,7 @@ std::string ShortProgramInvocationName();
void SetProgramInvocationName(absl::string_view prog_name_str);
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_INTERNAL_PROGRAM_NAME_H_
diff --git a/absl/flags/internal/program_name_test.cc b/absl/flags/internal/program_name_test.cc
index ed69218b..269142f2 100644
--- a/absl/flags/internal/program_name_test.cc
+++ b/absl/flags/internal/program_name_test.cc
@@ -15,8 +15,11 @@
#include "absl/flags/internal/program_name.h"
+#include <string>
+
#include "gtest/gtest.h"
#include "absl/strings/match.h"
+#include "absl/strings/string_view.h"
namespace {
diff --git a/absl/flags/internal/registry.cc b/absl/flags/internal/registry.cc
index e39264e5..e434a859 100644
--- a/absl/flags/internal/registry.cc
+++ b/absl/flags/internal/registry.cc
@@ -15,9 +15,20 @@
#include "absl/flags/internal/registry.h"
-#include "absl/base/dynamic_annotations.h"
+#include <assert.h>
+#include <stdlib.h>
+
+#include <functional>
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
-#include "absl/flags/config.h"
+#include "absl/base/thread_annotations.h"
+#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/usage_config.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
@@ -30,20 +41,8 @@
// set it.
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
-namespace {
-
-void DestroyFlag(CommandLineFlag* flag) NO_THREAD_SAFETY_ANALYSIS {
- flag->Destroy();
-
- // CommandLineFlag handle object is heap allocated for non Abseil Flags.
- if (!flag->IsAbseilFlag()) {
- delete flag;
- }
-}
-
-} // namespace
// --------------------------------------------------------------------
// FlagRegistry
@@ -58,17 +57,13 @@ void DestroyFlag(CommandLineFlag* flag) NO_THREAD_SAFETY_ANALYSIS {
class FlagRegistry {
public:
FlagRegistry() = default;
- ~FlagRegistry() {
- for (auto& p : flags_) {
- DestroyFlag(p.second);
- }
- }
+ ~FlagRegistry() = default;
// Store a flag in this registry. Takes ownership of *flag.
void RegisterFlag(CommandLineFlag* flag);
- void Lock() EXCLUSIVE_LOCK_FUNCTION(lock_) { lock_.Lock(); }
- void Unlock() UNLOCK_FUNCTION(lock_) { lock_.Unlock(); }
+ void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION(lock_) { lock_.Lock(); }
+ void Unlock() ABSL_UNLOCK_FUNCTION(lock_) { lock_.Unlock(); }
// Returns the flag object for the specified name, or nullptr if not found.
// Will emit a warning if a 'retired' flag is specified.
@@ -114,6 +109,7 @@ class FlagRegistryLock {
FlagRegistry* const fr_;
};
+void DestroyRetiredFlag(CommandLineFlag* flag);
} // namespace
void FlagRegistry::RegisterFlag(CommandLineFlag* flag) {
@@ -131,7 +127,7 @@ void FlagRegistry::RegisterFlag(CommandLineFlag* flag) {
(flag->IsRetired() ? old_flag->Filename() : flag->Filename()),
"'."),
true);
- } else if (flag->op != old_flag->op) {
+ } else if (flag->TypeId() != old_flag->TypeId()) {
flags_internal::ReportUsageError(
absl::StrCat("Flag '", flag->Name(),
"' was defined more than once but with "
@@ -141,8 +137,8 @@ void FlagRegistry::RegisterFlag(CommandLineFlag* flag) {
flag->Typename(), "', respectively."),
true);
} else if (old_flag->IsRetired()) {
- // Retired definitions are idempotent. Just keep the old one.
- DestroyFlag(flag);
+ // Retired flag can just be deleted.
+ DestroyRetiredFlag(flag);
return;
} else if (old_flag->Filename() != flag->Filename()) {
flags_internal::ReportUsageError(
@@ -201,112 +197,34 @@ CommandLineFlag* FlagRegistry::FindRetiredFlagLocked(absl::string_view name) {
class FlagSaverImpl {
public:
- // Constructs an empty FlagSaverImpl object.
- FlagSaverImpl() {}
- ~FlagSaverImpl() {
- // reclaim memory from each of our CommandLineFlags
- for (const SavedFlag& src : backup_registry_) {
- Delete(src.op, src.current);
- Delete(src.op, src.default_value);
- }
- }
+ FlagSaverImpl() = default;
+ FlagSaverImpl(const FlagSaverImpl&) = delete;
+ void operator=(const FlagSaverImpl&) = delete;
// Saves the flag states from the flag registry into this object.
// It's an error to call this more than once.
- // Must be called when the registry mutex is not held.
void SaveFromRegistry() {
assert(backup_registry_.empty()); // call only once!
- SavedFlag saved;
flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) {
- if (flag->IsRetired()) return;
-
- saved.name = flag->Name();
- saved.op = flag->op;
- saved.marshalling_op = flag->marshalling_op;
- {
- absl::MutexLock l(flag->InitFlagIfNecessary());
- saved.validator = flag->validator;
- saved.modified = flag->modified;
- 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;
+ if (auto flag_state = flag->SaveState()) {
+ backup_registry_.emplace_back(std::move(flag_state));
}
- backup_registry_.push_back(saved);
});
}
- // Restores the saved flag states into the flag registry. We
- // assume no flags were added or deleted from the registry since
- // the SaveFromRegistry; if they were, that's trouble! Must be
- // called when the registry mutex is not held.
+ // Restores the saved flag states into the flag registry.
void RestoreToRegistry() {
- FlagRegistry* const global_registry = FlagRegistry::GlobalRegistry();
- FlagRegistryLock frl(global_registry);
- for (const SavedFlag& src : backup_registry_) {
- CommandLineFlag* flag = global_registry->FindFlagLocked(src.name);
- // If null, flag got deleted from registry.
- if (!flag) continue;
-
- bool restored = false;
- {
- 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)) {
- restored = true;
- Copy(src.op, src.default_value, flag->def);
- }
- if (flag->counter != src.counter ||
- ChangedDirectly(flag, src.current, flag->cur)) {
- restored = true;
- Copy(src.op, src.current, flag->cur);
- UpdateCopy(flag);
- flag->InvokeCallback();
- }
- }
-
- 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)));
- }
+ for (const auto& flag_state : backup_registry_) {
+ flag_state->Restore();
}
}
private:
- struct SavedFlag {
- absl::string_view name;
- FlagOpFn op;
- FlagMarshallingOpFn marshalling_op;
- int64_t counter;
- bool modified;
- bool on_command_line;
- bool (*validator)();
- const void* current; // nullptr after restore
- const void* default_value; // nullptr after restore
- };
-
- std::vector<SavedFlag> backup_registry_;
-
- FlagSaverImpl(const FlagSaverImpl&); // no copying!
- void operator=(const FlagSaverImpl&);
+ std::vector<std::unique_ptr<flags_internal::FlagStateInterface>>
+ backup_registry_;
};
-FlagSaver::FlagSaver() : impl_(new FlagSaverImpl()) {
- impl_->SaveFromRegistry();
-}
+FlagSaver::FlagSaver() : impl_(new FlagSaverImpl) { impl_->SaveFromRegistry(); }
void FlagSaver::Ignore() {
delete impl_;
@@ -321,44 +239,6 @@ FlagSaver::~FlagSaver() {
}
// --------------------------------------------------------------------
-// GetAllFlags()
-// The main way the FlagRegistry class exposes its data. This
-// returns, as strings, all the info about all the flags in
-// the main registry, sorted first by filename they are defined
-// in, and then by flagname.
-// --------------------------------------------------------------------
-
-struct FilenameFlagnameLess {
- bool operator()(const CommandLineFlagInfo& a,
- const CommandLineFlagInfo& b) const {
- int cmp = absl::string_view(a.filename).compare(b.filename);
- if (cmp != 0) return cmp < 0;
- return a.name < b.name;
- }
-};
-
-void FillCommandLineFlagInfo(CommandLineFlag* flag,
- CommandLineFlagInfo* result) {
- result->name = std::string(flag->Name());
- result->type = std::string(flag->Typename());
- result->description = flag->Help();
- result->filename = flag->Filename();
-
- if (!flag->IsAbseilFlag()) {
- if (!flag->IsModified() && ChangedDirectly(flag, flag->cur, flag->def)) {
- flag->modified = true;
- }
- }
-
- result->current_value = flag->CurrentValue();
- result->default_value = flag->DefaultValue();
- result->is_default = !flag->IsModified();
- result->has_validator_fn = flag->HasValidatorFn();
- absl::MutexLock l(flag->InitFlagIfNecessary());
- result->flag_ptr = flag->IsAbseilFlag() ? nullptr : flag->cur;
-}
-
-// --------------------------------------------------------------------
CommandLineFlag* FindCommandLineFlag(absl::string_view name) {
if (name.empty()) return nullptr;
@@ -393,21 +273,6 @@ void ForEachFlag(std::function<void(CommandLineFlag*)> visitor) {
// --------------------------------------------------------------------
-void GetAllFlags(std::vector<CommandLineFlagInfo>* OUTPUT) {
- flags_internal::ForEachFlag([&](CommandLineFlag* flag) {
- if (flag->IsRetired()) return;
-
- CommandLineFlagInfo fi;
- FillCommandLineFlagInfo(flag, &fi);
- OUTPUT->push_back(fi);
- });
-
- // Now sort the flags, first by filename they occur in, then alphabetically
- std::sort(OUTPUT->begin(), OUTPUT->end(), FilenameFlagnameLess());
-}
-
-// --------------------------------------------------------------------
-
bool RegisterCommandLineFlag(CommandLineFlag* flag) {
FlagRegistry::GlobalRegistry()->RegisterFlag(flag);
return true;
@@ -415,14 +280,55 @@ bool RegisterCommandLineFlag(CommandLineFlag* flag) {
// --------------------------------------------------------------------
-bool Retire(FlagOpFn ops, FlagMarshallingOpFn marshalling_ops,
- const char* name) {
- auto* flag = new CommandLineFlag(
- name,
- /*help_text=*/absl::flags_internal::HelpText::FromStaticCString(nullptr),
- /*filename_arg=*/"RETIRED", ops, marshalling_ops,
- /*initial_value_gen=*/nullptr,
- /*retired_arg=*/true, nullptr, nullptr);
+namespace {
+
+class RetiredFlagObj final : public flags_internal::CommandLineFlag {
+ public:
+ constexpr RetiredFlagObj(const char* name, FlagStaticTypeId type_id)
+ : name_(name), type_id_(type_id) {}
+
+ private:
+ absl::string_view Name() const override { return name_; }
+ std::string Filename() const override { return "RETIRED"; }
+ absl::string_view Typename() const override { return ""; }
+ FlagStaticTypeId TypeId() const override { return type_id_; }
+ std::string Help() const override { return ""; }
+ bool IsRetired() const override { return true; }
+ bool IsModified() const override { return false; }
+ bool IsSpecifiedOnCommandLine() const override { return false; }
+ std::string DefaultValue() const override { return ""; }
+ std::string CurrentValue() const override { return ""; }
+
+ // Any input is valid
+ bool ValidateInputValue(absl::string_view) const override { return true; }
+
+ std::unique_ptr<flags_internal::FlagStateInterface> SaveState() override {
+ return nullptr;
+ }
+
+ bool SetFromString(absl::string_view, flags_internal::FlagSettingMode,
+ flags_internal::ValueSource, std::string*) override {
+ return false;
+ }
+
+ void CheckDefaultValueParsingRoundtrip() const override {}
+
+ void Read(void*) const override {}
+
+ // Data members
+ const char* const name_;
+ const FlagStaticTypeId type_id_;
+};
+
+void DestroyRetiredFlag(flags_internal::CommandLineFlag* flag) {
+ assert(flag->IsRetired());
+ delete static_cast<RetiredFlagObj*>(flag);
+}
+
+} // namespace
+
+bool Retire(const char* name, FlagStaticTypeId type_id) {
+ auto* flag = new flags_internal::RetiredFlagObj(name, type_id);
FlagRegistry::GlobalRegistry()->RegisterFlag(flag);
return true;
}
@@ -441,5 +347,5 @@ bool IsRetiredFlag(absl::string_view name, bool* type_is_bool) {
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/internal/registry.h b/absl/flags/internal/registry.h
index d851d245..69ff889f 100644
--- a/absl/flags/internal/registry.h
+++ b/absl/flags/internal/registry.h
@@ -20,43 +20,18 @@
#include <map>
#include <string>
+#include "absl/base/config.h"
#include "absl/base/macros.h"
#include "absl/flags/internal/commandlineflag.h"
+#include "absl/strings/string_view.h"
// --------------------------------------------------------------------
// Global flags registry API.
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
-// CommandLineFlagInfo holds all information for a flag.
-struct CommandLineFlagInfo {
- std::string name; // the name of the flag
- std::string type; // DO NOT use. Use flag->IsOfType<T>() instead.
- std::string description; // the "help text" associated with the flag
- std::string current_value; // the current value, as a std::string
- std::string default_value; // the default value, as a std::string
- std::string filename; // 'cleaned' version of filename holding the flag
- bool has_validator_fn; // true if RegisterFlagValidator called on this flag
-
- bool is_default; // true if the flag has the default value and
- // has not been set explicitly from the cmdline
- // or via SetCommandLineOption.
-
- // nullptr for ABSL_FLAG. A pointer to the flag's current value
- // otherwise. E.g., for DEFINE_int32(foo, ...), flag_ptr will be
- // &FLAGS_foo.
- const void* flag_ptr;
-};
-
-//-----------------------------------------------------------------------------
-
-void FillCommandLineFlagInfo(CommandLineFlag* flag,
- CommandLineFlagInfo* result);
-
-//-----------------------------------------------------------------------------
-
CommandLineFlag* FindCommandLineFlag(absl::string_view name);
CommandLineFlag* FindRetiredFlag(absl::string_view name);
@@ -69,11 +44,6 @@ void ForEachFlag(std::function<void(CommandLineFlag*)> visitor);
//-----------------------------------------------------------------------------
-// Store the list of all flags in *OUTPUT, sorted by file.
-void GetAllFlags(std::vector<CommandLineFlagInfo>* OUTPUT);
-
-//-----------------------------------------------------------------------------
-
bool RegisterCommandLineFlag(CommandLineFlag*);
//-----------------------------------------------------------------------------
@@ -107,29 +77,14 @@ bool RegisterCommandLineFlag(CommandLineFlag*);
// 4: Remove the old_lib 'retired' registration.
// 5: Eventually delete the graveyard registration entirely.
//
-// Returns bool to enable use in namespace-scope initializers.
-// For example:
-//
-// static const bool dummy = base::RetiredFlag<int32_t>("myflag");
-//
-// Or to declare several at once:
-//
-// static bool dummies[] = {
-// base::RetiredFlag<std::string>("some_string_flag"),
-// base::RetiredFlag<double>("some_double_flag"),
-// base::RetiredFlag<int32_t>("some_int32_flag")
-// };
// Retire flag with name "name" and type indicated by ops.
-bool Retire(FlagOpFn ops, FlagMarshallingOpFn marshalling_ops,
- const char* name);
+bool Retire(const char* name, FlagStaticTypeId type_id);
// Registered a retired flag with name 'flag_name' and type 'T'.
template <typename T>
inline bool RetiredFlag(const char* flag_name) {
- return flags_internal::Retire(flags_internal::FlagOps<T>,
- flags_internal::FlagMarshallingOps<T>,
- flag_name);
+ return flags_internal::Retire(flag_name, &FlagStaticTypeIdGen<T>);
}
// If the flag is retired, returns true and indicates in |*type_is_bool|
@@ -163,7 +118,7 @@ class FlagSaver {
};
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_INTERNAL_REGISTRY_H_
diff --git a/absl/flags/internal/type_erased.cc b/absl/flags/internal/type_erased.cc
index 6b91b261..490bc4eb 100644
--- a/absl/flags/internal/type_erased.cc
+++ b/absl/flags/internal/type_erased.cc
@@ -15,13 +15,19 @@
#include "absl/flags/internal/type_erased.h"
+#include <assert.h>
+
+#include <string>
+
+#include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
-#include "absl/flags/config.h"
+#include "absl/flags/internal/commandlineflag.h"
+#include "absl/flags/internal/registry.h"
#include "absl/flags/usage_config.h"
-#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
bool GetCommandLineOption(absl::string_view name, std::string* value) {
@@ -37,30 +43,6 @@ bool GetCommandLineOption(absl::string_view name, std::string* value) {
return true;
}
-bool GetCommandLineFlagInfo(absl::string_view name,
- CommandLineFlagInfo* OUTPUT) {
- if (name.empty()) return false;
-
- CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name);
- if (flag == nullptr || flag->IsRetired()) {
- return false;
- }
-
- assert(OUTPUT);
- FillCommandLineFlagInfo(flag, OUTPUT);
- return true;
-}
-
-CommandLineFlagInfo GetCommandLineFlagInfoOrDie(absl::string_view name) {
- CommandLineFlagInfo info;
- if (!GetCommandLineFlagInfo(name, &info)) {
- ABSL_INTERNAL_LOG(FATAL, absl::StrCat("Flag '", name, "' does not exist"));
- }
- return info;
-}
-
-// --------------------------------------------------------------------
-
bool SetCommandLineOption(absl::string_view name, absl::string_view value) {
return SetCommandLineOptionWithMode(name, value,
flags_internal::SET_FLAGS_VALUE);
@@ -104,5 +86,5 @@ bool SpecifiedOnCommandLine(absl::string_view name) {
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/internal/type_erased.h b/absl/flags/internal/type_erased.h
index c9de6de9..188429c7 100644
--- a/absl/flags/internal/type_erased.h
+++ b/absl/flags/internal/type_erased.h
@@ -18,14 +18,16 @@
#include <string>
+#include "absl/base/config.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/registry.h"
+#include "absl/strings/string_view.h"
// --------------------------------------------------------------------
// Registry interfaces operating on type erased handles.
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
// If a flag named "name" exists, store its current value in *OUTPUT
@@ -33,17 +35,6 @@ namespace flags_internal {
// Thread-safe.
bool GetCommandLineOption(absl::string_view name, std::string* value);
-// If a flag named "name" exists, store its information in *OUTPUT
-// and return true. Else return false without changing *OUTPUT.
-// Thread-safe.
-bool GetCommandLineFlagInfo(absl::string_view name,
- CommandLineFlagInfo* OUTPUT);
-
-// Returns the CommandLineFlagInfo of the flagname. exit() with an
-// error code if name not found.
-// Thread-safe.
-CommandLineFlagInfo GetCommandLineFlagInfoOrDie(absl::string_view name);
-
// Set the value of the flag named "name" to value. If successful,
// returns true. If not successful (e.g., the flag was not found or
// the value is not a valid value), returns false.
@@ -93,7 +84,7 @@ inline bool GetByName(absl::string_view name, T* dst) {
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_INTERNAL_TYPE_ERASED_H_
diff --git a/absl/flags/internal/type_erased_test.cc b/absl/flags/internal/type_erased_test.cc
index ac749a60..4ce59810 100644
--- a/absl/flags/internal/type_erased_test.cc
+++ b/absl/flags/internal/type_erased_test.cc
@@ -15,12 +15,15 @@
#include "absl/flags/internal/type_erased.h"
-#include <cmath>
+#include <memory>
+#include <string>
#include "gtest/gtest.h"
#include "absl/flags/flag.h"
+#include "absl/flags/internal/commandlineflag.h"
+#include "absl/flags/internal/registry.h"
+#include "absl/flags/marshalling.h"
#include "absl/memory/memory.h"
-#include "absl/strings/str_cat.h"
ABSL_FLAG(int, int_flag, 1, "int_flag help");
ABSL_FLAG(std::string, string_flag, "dflt", "string_flag help");
@@ -116,6 +119,13 @@ TEST_F(TypeErasedTest, TestSetCommandLineOptionWithMode_SET_FLAGS_DEFAULT) {
EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "101",
flags::SET_FLAGS_DEFAULT));
+ // Set it again to ensure that resetting logic is covered.
+ EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "102",
+ flags::SET_FLAGS_DEFAULT));
+
+ EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "103",
+ flags::SET_FLAGS_DEFAULT));
+
EXPECT_TRUE(flags::SetCommandLineOptionWithMode("string_flag", "asdfgh",
flags::SET_FLAGS_DEFAULT));
EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh");
diff --git a/absl/flags/internal/usage.cc b/absl/flags/internal/usage.cc
index a9a8a21e..ff907161 100644
--- a/absl/flags/internal/usage.cc
+++ b/absl/flags/internal/usage.cc
@@ -15,18 +15,24 @@
#include "absl/flags/internal/usage.h"
+#include <functional>
#include <map>
+#include <ostream>
#include <string>
+#include <utility>
+#include <vector>
+#include "absl/base/config.h"
#include "absl/flags/flag.h"
+#include "absl/flags/internal/commandlineflag.h"
+#include "absl/flags/internal/flag.h"
#include "absl/flags/internal/path_util.h"
#include "absl/flags/internal/program_name.h"
+#include "absl/flags/internal/registry.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,
"show help on important flags for this binary [tip: all flags can "
@@ -44,10 +50,31 @@ ABSL_FLAG(std::string, helpmatch, "",
"show help on modules whose name contains the specified substr");
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
namespace {
+absl::string_view TypenameForHelp(const flags_internal::CommandLineFlag& flag) {
+ // Only report names of v1 built-in types
+#define HANDLE_V1_BUILTIN_TYPE(t) \
+ if (flag.IsOfType<t>()) { \
+ return #t; \
+ }
+
+ HANDLE_V1_BUILTIN_TYPE(bool);
+ HANDLE_V1_BUILTIN_TYPE(int32_t);
+ HANDLE_V1_BUILTIN_TYPE(int64_t);
+ HANDLE_V1_BUILTIN_TYPE(uint64_t);
+ HANDLE_V1_BUILTIN_TYPE(double);
+#undef HANDLE_V1_BUILTIN_TYPE
+
+ if (flag.IsOfType<std::string>()) {
+ return "string";
+ }
+
+ return "";
+}
+
// This class is used to emit an XML element with `tag` and `text`.
// It adds opening and closing tags and escapes special characters in the text.
// For example:
@@ -180,14 +207,14 @@ void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag,
FlagHelpPrettyPrinter printer(80, out); // Max line length is 80.
// Flag name.
- printer.Write(absl::StrCat("-", flag.Name()));
+ printer.Write(absl::StrCat("--", flag.Name()));
// Flag help.
printer.Write(absl::StrCat("(", flag.Help(), ");"), /*wrap_line=*/true);
// Flag data type (for V1 flags only).
if (!flag.IsAbseilFlag() && !flag.IsRetired()) {
- printer.Write(absl::StrCat("type: ", flag.Typename(), ";"));
+ printer.Write(absl::StrCat("type: ", TypenameForHelp(flag), ";"));
}
// The listed default value will be the actual default from the flag
@@ -224,6 +251,9 @@ void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
} else {
// XML schema is not a part of our public API for now.
out << "<?xml version=\"1.0\"?>\n"
+ << "<!-- This output should be used with care. We do not report type "
+ "names for flags with user defined types -->\n"
+ << "<!-- Prefer flag only_check_args for validating flag inputs -->\n"
// The document.
<< "<AllFlags>\n"
// The program name and usage.
@@ -381,5 +411,5 @@ int HandleUsageFlags(std::ostream& out,
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/internal/usage.h b/absl/flags/internal/usage.h
index f3794afe..6b080fd1 100644
--- a/absl/flags/internal/usage.h
+++ b/absl/flags/internal/usage.h
@@ -19,6 +19,7 @@
#include <iosfwd>
#include <string>
+#include "absl/base/config.h"
#include "absl/flags/declare.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/strings/string_view.h"
@@ -27,7 +28,7 @@
// Usage reporting interfaces
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
// The format to report the help messages in.
@@ -65,7 +66,7 @@ int HandleUsageFlags(std::ostream& out,
absl::string_view program_usage_message);
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
ABSL_DECLARE_FLAG(bool, help);
diff --git a/absl/flags/internal/usage_test.cc b/absl/flags/internal/usage_test.cc
index 781e1d2b..e1e57e55 100644
--- a/absl/flags/internal/usage_test.cc
+++ b/absl/flags/internal/usage_test.cc
@@ -15,17 +15,23 @@
#include "absl/flags/internal/usage.h"
+#include <stdint.h>
+
#include <sstream>
+#include <string>
#include "gtest/gtest.h"
+#include "absl/flags/declare.h"
#include "absl/flags/flag.h"
+#include "absl/flags/internal/parse.h"
#include "absl/flags/internal/path_util.h"
#include "absl/flags/internal/program_name.h"
-#include "absl/flags/parse.h"
+#include "absl/flags/internal/registry.h"
#include "absl/flags/usage.h"
#include "absl/flags/usage_config.h"
#include "absl/memory/memory.h"
#include "absl/strings/match.h"
+#include "absl/strings/string_view.h"
ABSL_FLAG(int, usage_reporting_test_flag_01, 101,
"usage_reporting_test_flag_01 help message");
@@ -67,9 +73,9 @@ static std::string NormalizeFileName(absl::string_view fname) {
fname = normalized;
#endif
- auto absl_pos = fname.find("/absl/");
+ auto absl_pos = fname.rfind("absl/");
if (absl_pos != absl::string_view::npos) {
- fname = fname.substr(absl_pos + 1);
+ fname = fname.substr(absl_pos);
}
return std::string(fname);
}
@@ -111,7 +117,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_01) {
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
EXPECT_EQ(
test_buf.str(),
- R"( -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
+ R"( --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
default: 101;
)");
}
@@ -123,7 +129,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_02) {
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
EXPECT_EQ(
test_buf.str(),
- R"( -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
+ R"( --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
default: false;
)");
}
@@ -135,7 +141,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_03) {
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
EXPECT_EQ(
test_buf.str(),
- R"( -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
+ R"( --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
default: 1.03;
)");
}
@@ -147,7 +153,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_04) {
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
EXPECT_EQ(
test_buf.str(),
- R"( -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
+ R"( --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
default: 1000000000000004;
)");
}
@@ -159,7 +165,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_05) {
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
EXPECT_EQ(
test_buf.str(),
- R"( -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
+ R"( --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
default: UDT{};
)");
}
@@ -171,17 +177,17 @@ TEST_F(UsageReportingTest, TestFlagsHelpHRF) {
R"(usage_test: Custom usage message
Flags from absl/flags/internal/usage_test.cc:
- -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
+ --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
default: 101;
- -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
+ --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
default: false;
- -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
+ --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
default: 1.03;
- -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
+ --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
default: 1000000000000004;
- -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
+ --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
default: UDT{};
- -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
+ --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
Some more help.
Even more long long long long long long long long long long long long help
@@ -247,17 +253,17 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpshort) {
R"(usage_test: Custom usage message
Flags from absl/flags/internal/usage_test.cc:
- -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
+ --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
default: 101;
- -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
+ --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
default: false;
- -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
+ --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
default: 1.03;
- -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
+ --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
default: 1000000000000004;
- -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
+ --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
default: UDT{};
- -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
+ --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
Some more help.
Even more long long long long long long long long long long long long help
@@ -276,17 +282,17 @@ TEST_F(UsageReportingTest, TestUsageFlag_help) {
R"(usage_test: Custom usage message
Flags from absl/flags/internal/usage_test.cc:
- -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
+ --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
default: 101;
- -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
+ --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
default: false;
- -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
+ --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
default: 1.03;
- -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
+ --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
default: 1000000000000004;
- -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
+ --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
default: UDT{};
- -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
+ --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
Some more help.
Even more long long long long long long long long long long long long help
@@ -307,17 +313,17 @@ TEST_F(UsageReportingTest, TestUsageFlag_helppackage) {
R"(usage_test: Custom usage message
Flags from absl/flags/internal/usage_test.cc:
- -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
+ --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
default: 101;
- -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
+ --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
default: false;
- -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
+ --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
default: 1.03;
- -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
+ --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
default: 1000000000000004;
- -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
+ --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
default: UDT{};
- -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
+ --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
Some more help.
Even more long long long long long long long long long long long long help
@@ -372,17 +378,17 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
R"(usage_test: Custom usage message
Flags from absl/flags/internal/usage_test.cc:
- -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
+ --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
default: 101;
- -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
+ --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
default: false;
- -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
+ --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
default: 1.03;
- -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
+ --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
default: 1000000000000004;
- -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
+ --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
default: UDT{};
- -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
+ --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
Some more help.
Even more long long long long long long long long long long long long help
@@ -395,7 +401,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
} // namespace
int main(int argc, char* argv[]) {
- absl::GetFlag(FLAGS_undefok); // Force linking of parse.cc
+ (void)absl::GetFlag(FLAGS_undefok); // Force linking of parse.cc
flags::SetProgramInvocationName("usage_test");
absl::SetProgramUsageMessage(kTestUsageMessage);
::testing::InitGoogleTest(&argc, argv);