diff options
Diffstat (limited to 'absl/flags/internal')
-rw-r--r-- | absl/flags/internal/commandlineflag.cc | 496 | ||||
-rw-r--r-- | absl/flags/internal/commandlineflag.h | 414 | ||||
-rw-r--r-- | absl/flags/internal/commandlineflag_test.cc | 35 | ||||
-rw-r--r-- | absl/flags/internal/flag.cc | 382 | ||||
-rw-r--r-- | absl/flags/internal/flag.h | 628 | ||||
-rw-r--r-- | absl/flags/internal/parse.h | 5 | ||||
-rw-r--r-- | absl/flags/internal/path_util.h | 5 | ||||
-rw-r--r-- | absl/flags/internal/program_name.cc | 11 | ||||
-rw-r--r-- | absl/flags/internal/program_name.h | 5 | ||||
-rw-r--r-- | absl/flags/internal/program_name_test.cc | 3 | ||||
-rw-r--r-- | absl/flags/internal/registry.cc | 258 | ||||
-rw-r--r-- | absl/flags/internal/registry.h | 57 | ||||
-rw-r--r-- | absl/flags/internal/type_erased.cc | 38 | ||||
-rw-r--r-- | absl/flags/internal/type_erased.h | 17 | ||||
-rw-r--r-- | absl/flags/internal/type_erased_test.cc | 14 | ||||
-rw-r--r-- | absl/flags/internal/usage.cc | 42 | ||||
-rw-r--r-- | absl/flags/internal/usage.h | 5 | ||||
-rw-r--r-- | absl/flags/internal/usage_test.cc | 84 |
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); |