summaryrefslogtreecommitdiff
path: root/absl/flags/internal
diff options
context:
space:
mode:
authorGravatar Abseil Team <absl-team@google.com>2019-05-07 12:56:42 -0700
committerGravatar Eric Fiselier <eric@efcs.ca>2019-05-07 16:32:35 -0400
commitaa468ad75539619b47979911297efbb629c52e44 (patch)
tree660bdd268ea4bb18d565b253cce6b6a6bbca0590 /absl/flags/internal
parentcd86d0d20ab167c33b23d3875db68d1d4bad3a3b (diff)
Export of internal Abseil changes.
-- 78293ba4eb4981991ae7e6edd25eb9245fcd7515 by Andy Soffer <asoffer@google.com>: internal changes PiperOrigin-RevId: 247073879 -- 7bd97e3aad0c3012b89a39392a6ad3f254e9f3c3 by Derek Mauro <dmauro@google.com>: Release commandline flags PiperOrigin-RevId: 247065920 -- 2a991849fd7b140a43b073076d194b61533fd199 by Tom Manshreck <shreck@google.com>: Add documentation for built-in usage flags PiperOrigin-RevId: 247058863 -- 14157b0de45841706bbd631284191fd94c313db9 by Derek Mauro <dmauro@google.com>: Internal change PiperOrigin-RevId: 247058292 -- ed67e0a80468596e30540b367727a250fa415b68 by Abseil Team <absl-team@google.com>: Internal Change. PiperOrigin-RevId: 246828655 -- e1bc8c2aa3f90b3d56c55c5b7244e718c919265d by Abseil Team <absl-team@google.com>: Internal change PiperOrigin-RevId: 246822325 -- 907a68f287199f749cb9bdb48571b50bc34731e1 by Eric Fiselier <ericwf@google.com>: Accept vector<bool>::reference in StrCat and friends. Converting vector<bool>::reference to alphanum requires two user defined conversions, which isn't allowed. In order to accept this, we need a special constructor that is only enabled for the bool proxy types. PiperOrigin-RevId: 246626732 -- fe4295fa95cc65dee8c881ba12dd8f516e68c40d by Abseil Team <absl-team@google.com>: Clarify the proper way to define AbslHashValue, and fix confusing indentation. PiperOrigin-RevId: 246406528 -- d7174681a72d4a25c8fd2b4d9f515a0763eff87c by Gennadiy Rozental <rogeeff@google.com>: Internal change PiperOrigin-RevId: 246358214 -- a7fdc19683cf1a5885e9e1af52fcdb2db1eda53b by Abseil Team <absl-team@google.com>: Fix sample template instantiation. PiperOrigin-RevId: 246354617 -- 29a23c6835f872948d09b24d890385bf195bc995 by Abseil Team <absl-team@google.com>: Fix incorrectly copy-pasted static_assert in absl::optional::value_or The static_assert for a move-constructible type previously said the type must be copy-constructible. PiperOrigin-RevId: 246313827 -- 41884d5872d4ea2c67875b00144b8c8a5859a295 by Greg Falcon <gfalcon@google.com>: Import of CCTZ from GitHub. PiperOrigin-RevId: 245418790 -- 7f90a7f94cdd5e21232c749efe952a750b5c43a2 by Abseil Team <absl-team@google.com>: Internal change PiperOrigin-RevId: 245412658 -- 730a329cf047d54b46971fce1781edd857208c2a by Greg Falcon <gfalcon@google.com>: internal change PiperOrigin-RevId: 245293234 -- cf0216be6338200cbb18167d3f3b2e98e372be77 by Greg Falcon <gfalcon@google.com>: Internal change PiperOrigin-RevId: 245288164 GitOrigin-RevId: 78293ba4eb4981991ae7e6edd25eb9245fcd7515 Change-Id: I5ea9a852c36c722bae2d6be65fb7f72473d94ab6
Diffstat (limited to 'absl/flags/internal')
-rw-r--r--absl/flags/internal/commandlineflag.cc383
-rw-r--r--absl/flags/internal/commandlineflag.h365
-rw-r--r--absl/flags/internal/commandlineflag_test.cc196
-rw-r--r--absl/flags/internal/flag.h103
-rw-r--r--absl/flags/internal/parse.h48
-rw-r--r--absl/flags/internal/path_util.h60
-rw-r--r--absl/flags/internal/path_util_test.cc46
-rw-r--r--absl/flags/internal/program_name.cc53
-rw-r--r--absl/flags/internal/program_name.h47
-rw-r--r--absl/flags/internal/program_name_test.cc60
-rw-r--r--absl/flags/internal/registry.cc529
-rw-r--r--absl/flags/internal/registry.h168
-rw-r--r--absl/flags/internal/type_erased.cc121
-rw-r--r--absl/flags/internal/type_erased.h97
-rw-r--r--absl/flags/internal/type_erased_test.cc147
-rw-r--r--absl/flags/internal/usage.cc392
-rw-r--r--absl/flags/internal/usage.h91
-rw-r--r--absl/flags/internal/usage_test.cc367
18 files changed, 3273 insertions, 0 deletions
diff --git a/absl/flags/internal/commandlineflag.cc b/absl/flags/internal/commandlineflag.cc
new file mode 100644
index 00000000..74444721
--- /dev/null
+++ b/absl/flags/internal/commandlineflag.cc
@@ -0,0 +1,383 @@
+//
+// 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 {
+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 {
+
+void StoreAtomic(CommandLineFlag* flag, const void* data, size_t size) {
+ int64_t t = 0;
+ assert(size <= sizeof(int64_t));
+ memcpy(&t, data, size);
+ flag->atomic.store(t, std::memory_order_release);
+}
+
+// If the flag has a mutation callback this function invokes it. While the
+// callback is being invoked the primary flag's mutex is unlocked and it is
+// re-locked back after call to callback is completed. Callback invocation is
+// guarded by flag's secondary mutex instead which prevents concurrent callback
+// invocation. Note that it is possible for other thread to grab the primary
+// lock and update flag's value at any time during the callback invocation.
+// This is by design. Callback can get a value of the flag if necessary, but it
+// might be different from the value initiated the callback and it also can be
+// different by the time the callback invocation is completed.
+// Requires that *primary_lock be held in exclusive mode; it may be released
+// and reacquired by the implementation.
+void InvokeCallback(CommandLineFlag* flag, absl::Mutex* primary_lock)
+ EXCLUSIVE_LOCKS_REQUIRED(primary_lock) {
+ if (!flag->callback) return;
+
+ // The callback lock is guaranteed initialized, because *primary_lock exists.
+ absl::Mutex* callback_mu = &flag->locks->callback_mu;
+
+ // When executing the callback we need the primary flag's mutex to be unlocked
+ // so that callback can retrieve the flag's value.
+ primary_lock->Unlock();
+
+ {
+ absl::MutexLock lock(callback_mu);
+
+ flag->callback();
+ }
+
+ primary_lock->Lock();
+}
+
+// Currently we only validate flag values for user-defined flag types.
+bool ShouldValidateFlagValue(const CommandLineFlag& flag) {
+#define DONT_VALIDATE(T) \
+ 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
+
+// Update any copy of the flag value that is stored in an atomic word.
+// In addition if flag has a mutation callback this function invokes it.
+void UpdateCopy(CommandLineFlag* flag, absl::Mutex* primary_lock)
+ EXCLUSIVE_LOCKS_REQUIRED(primary_lock) {
+#define STORE_ATOMIC(T) \
+ else if (flag->IsOfType<T>()) { \
+ StoreAtomic(flag, flag->cur, sizeof(T)); \
+ }
+
+ if (false) {
+ }
+ ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(STORE_ATOMIC)
+#undef STORE_ATOMIC
+
+ InvokeCallback(flag, primary_lock);
+}
+
+// Ensure that the lazily initialized fields of *flag have been initialized,
+// and return &flag->locks->primary_mu.
+absl::Mutex* InitFlagIfNecessary(CommandLineFlag* flag)
+ LOCK_RETURNED(flag->locks->primary_mu) {
+ absl::Mutex* mu;
+ if (!flag->inited.load(std::memory_order_acquire)) {
+ // Need to initialize lazily initialized fields.
+ ABSL_CONST_INIT static absl::Mutex init_lock(absl::kConstInit);
+ init_lock.Lock();
+ if (flag->locks == nullptr) { // Must initialize Mutexes for this flag.
+ flag->locks = new flags_internal::CommandLineFlagLocks;
+ }
+ mu = &flag->locks->primary_mu;
+ init_lock.Unlock();
+ mu->Lock();
+ if (!flag->retired &&
+ flag->def == nullptr) { // Need to initialize def and cur fields.
+ flag->def = (*flag->make_init_value)();
+ flag->cur = Clone(flag->op, flag->def);
+ UpdateCopy(flag, mu);
+ }
+ mu->Unlock();
+ flag->inited.store(true, std::memory_order_release);
+ } else { // All fields initialized; flag->locks is therefore safe to read.
+ mu = &flag->locks->primary_mu;
+ }
+ return mu;
+}
+
+// Return true iff flag value was changed via direct-access.
+bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b) {
+ if (!flag->IsAbseilFlag()) {
+ // Need to compare values for direct-access flags.
+#define CHANGED_FOR_TYPE(T) \
+ if (flag->IsOfType<T>()) { \
+ return *reinterpret_cast<const T*>(a) != *reinterpret_cast<const T*>(b); \
+ }
+
+ 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;
+}
+
+// Direct-access flags can be modified without going through the
+// flag API. Detect such changes and updated the modified bit.
+void UpdateModifiedBit(CommandLineFlag* flag) {
+ if (!flag->IsAbseilFlag()) {
+ absl::MutexLock l(InitFlagIfNecessary(flag));
+ if (!flag->modified && ChangedDirectly(flag, flag->cur, flag->def)) {
+ flag->modified = true;
+ }
+ }
+}
+
+bool Validate(CommandLineFlag*, const void*) {
+ return true;
+}
+
+std::string HelpText::GetHelpText() const {
+ if (help_function_) return help_function_();
+ if (help_message_) return help_message_;
+
+ return {};
+}
+
+const int64_t CommandLineFlag::kAtomicInit;
+
+void CommandLineFlag::Read(void* dst,
+ const flags_internal::FlagOpFn dst_op) const {
+ absl::ReaderMutexLock l(
+ InitFlagIfNecessary(const_cast<CommandLineFlag*>(this)));
+
+ // `dst_op` is the unmarshaling operation corresponding to the declaration
+ // visibile at the call site. `op` is the Flag's defined unmarshalling
+ // operation. They must match for this operation to be well-defined.
+ if (ABSL_PREDICT_FALSE(dst_op != op)) {
+ ABSL_INTERNAL_LOG(
+ ERROR,
+ absl::StrCat("Flag '", name,
+ "' is defined as one type and declared as another"));
+ }
+ CopyConstruct(op, cur, dst);
+}
+
+void CommandLineFlag::Write(const void* src,
+ const flags_internal::FlagOpFn src_op) {
+ absl::Mutex* mu = InitFlagIfNecessary(this);
+ absl::MutexLock l(mu);
+
+ // `src_op` is the marshalling operation corresponding to the declaration
+ // visible at the call site. `op` is the Flag's defined marshalling operation.
+ // They must match for this operation to be well-defined.
+ if (ABSL_PREDICT_FALSE(src_op != op)) {
+ ABSL_INTERNAL_LOG(
+ ERROR,
+ absl::StrCat("Flag '", name,
+ "' is defined as one type and declared as another"));
+ }
+
+ if (ShouldValidateFlagValue(*this)) {
+ void* obj = Clone(op, src);
+ std::string ignored_error;
+ std::string src_as_str = Unparse(marshalling_op, src);
+ if (!Parse(marshalling_op, src_as_str, obj, &ignored_error) ||
+ !Validate(this, obj)) {
+ ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '", name,
+ "' to invalid value ", src_as_str));
+ }
+ Delete(op, obj);
+ }
+
+ modified = true;
+ counter++;
+ Copy(op, src, cur);
+
+ UpdateCopy(this, mu);
+}
+
+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 {
+ return Unparse(this->marshalling_op, this->def);
+}
+
+std::string CommandLineFlag::CurrentValue() const {
+ return Unparse(this->marshalling_op, this->cur);
+}
+
+void CommandLineFlag::SetCallback(
+ const flags_internal::FlagCallback mutation_callback) {
+ absl::Mutex* mu = InitFlagIfNecessary(this);
+ absl::MutexLock l(mu);
+
+ callback = mutation_callback;
+
+ InvokeCallback(this, mu);
+}
+
+// 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'
+static bool TryParseLocked(CommandLineFlag* flag, void* dst,
+ absl::string_view value, std::string* err) {
+ 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 (!Validate(flag, 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;
+
+ UpdateModifiedBit(this);
+
+ absl::Mutex* mu = InitFlagIfNecessary(this);
+ absl::MutexLock l(mu);
+
+ switch (set_mode) {
+ case SET_FLAGS_VALUE: {
+ // set or modify the flag's value
+ if (!TryParseLocked(this, this->cur, value, err)) return false;
+ this->modified = true;
+ UpdateCopy(this, mu);
+
+ 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, mu);
+ } 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, mu);
+ }
+ break;
+ }
+ default: {
+ // unknown set_mode
+ assert(false);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace flags_internal
+} // namespace absl
diff --git a/absl/flags/internal/commandlineflag.h b/absl/flags/internal/commandlineflag.h
new file mode 100644
index 00000000..4815cdc7
--- /dev/null
+++ b/absl/flags/internal/commandlineflag.h
@@ -0,0 +1,365 @@
+//
+// 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.
+
+#ifndef ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
+#define ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
+
+#include <atomic>
+
+#include "absl/base/macros.h"
+#include "absl/flags/marshalling.h"
+#include "absl/synchronization/mutex.h"
+#include "absl/types/optional.h"
+
+namespace absl {
+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*);
+
+// Options that control SetCommandLineOptionWithMode.
+enum FlagSettingMode {
+ // update the flag's value unconditionally (can call this multiple times).
+ SET_FLAGS_VALUE,
+ // update the flag's value, but *only if* it has not yet been updated
+ // with SET_FLAGS_VALUE, SET_FLAG_IF_DEFAULT, or "FLAGS_xxx = nondef".
+ SET_FLAG_IF_DEFAULT,
+ // set the flag's default value to this. If the flag has not been updated
+ // yet (via SET_FLAGS_VALUE, SET_FLAG_IF_DEFAULT, or "FLAGS_xxx = nondef")
+ // change the flag's current value to the new default value as well.
+ SET_FLAGS_DEFAULT
+};
+
+// Options that control SetFromString: Source of a value.
+enum ValueSource {
+ // Flag is being set by value specified on a command line.
+ kCommandLine,
+ // Flag is being set by value specified in the code.
+ 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* (*)();
+
+// 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 (*)();
+
+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 {
+ 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;
+
+ private:
+ explicit constexpr HelpText(const HelpGenFunc fn, const char* msg)
+ : help_function_(fn), help_message_(msg) {}
+
+ HelpGenFunc help_function_;
+ const char* help_message_;
+};
+
+// 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) {}
+
+ // 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 IsSpecifiedOnCommandLine() const { return on_command_line; }
+ // 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;
+
+ // Return true iff flag has type T.
+ template <typename T>
+ inline bool IsOfType() const {
+ return this->op == &flags_internal::FlagOps<T>;
+ }
+
+ // Attempts to retrieve the flag value. Returns value on success,
+ // absl::nullopt otherwise.
+ template <typename T>
+ absl::optional<T> Get() {
+ if (IsRetired() || flags_internal::FlagOps<T> != this->op)
+ return absl::nullopt;
+
+ T res;
+ Read(&res, flags_internal::FlagOps<T>);
+
+ return res;
+ }
+
+ void SetCallback(const flags_internal::FlagCallback mutation_callback);
+
+ // 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`
+ // 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 SetFromString(absl::string_view value,
+ flags_internal::FlagSettingMode set_mode,
+ flags_internal::ValueSource source, std::string* error);
+
+ // Constant configuration for a particular flag.
+ private:
+ const char* const name;
+ const HelpText help;
+ const char* const filename;
+
+ public:
+ const FlagOpFn op; // Type-specific handler
+ 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
+
+ // Mutable state (guarded by locks->primary_mu).
+ bool modified; // Has flag value been modified?
+ bool on_command_line; // Specified on command line.
+ bool (*validator)(); // Validator function, or nullptr
+ FlagCallback callback; // Mutation callback, or nullptr
+ void* def; // Lazily initialized pointer to default value
+ void* cur; // Lazily initialized pointer to current value
+ int64_t counter; // Mutation counter
+
+ // 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.
+
+ // copy construct new value of flag's type in a memory referenced by dst
+ // based on current flag's value
+ void Read(void* dst, const flags_internal::FlagOpFn dst_op) const;
+ // updates flag's value to *src (locked)
+ void Write(const void* src, const flags_internal::FlagOpFn src_op);
+
+ ABSL_DEPRECATED(
+ "temporary until FlagName call sites are migrated and validator API is "
+ "changed")
+ const char* NameAsCString() const { return name; }
+
+ private:
+ friend class FlagRegistry;
+};
+
+// Ensure that the lazily initialized fields of *flag have been initialized,
+// and return &flag->locks->primary_mu.
+absl::Mutex* InitFlagIfNecessary(CommandLineFlag* flag);
+// Update any copy of the flag value that is stored in an atomic word.
+// In addition if flag has a mutation callback this function invokes it. While
+// callback is being invoked the primary flag's mutex is unlocked and it is
+// 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, absl::Mutex* primary_lock);
+// Return true iff flag value was changed via direct-access.
+bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b);
+// Direct-access flags can be modified without going through the
+// flag API. Detect such changes and updated the modified bit.
+void UpdateModifiedBit(CommandLineFlag* flag);
+// Invoke the flag validators for old flags.
+// TODO(rogeeff): implement proper validators for Abseil Flags
+bool Validate(CommandLineFlag* flag, const void* value);
+
+// This macro is the "source of truth" for the list of supported flag types we
+// expect to perform lock free operations on. Specifically it generates code,
+// 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)
+
+} // namespace flags_internal
+} // namespace absl
+
+#endif // ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
diff --git a/absl/flags/internal/commandlineflag_test.cc b/absl/flags/internal/commandlineflag_test.cc
new file mode 100644
index 00000000..705f301c
--- /dev/null
+++ b/absl/flags/internal/commandlineflag_test.cc
@@ -0,0 +1,196 @@
+//
+// 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 "gtest/gtest.h"
+#include "absl/flags/flag.h"
+#include "absl/flags/internal/registry.h"
+#include "absl/memory/memory.h"
+#include "absl/strings/match.h"
+#include "absl/strings/str_cat.h"
+
+ABSL_FLAG(int, int_flag, 201, "int_flag help");
+ABSL_FLAG(std::string, string_flag, "dflt",
+ absl::StrCat("string_flag", " help"));
+ABSL_RETIRED_FLAG(bool, bool_retired_flag, false, "bool_retired_flag help");
+
+namespace {
+
+namespace flags = absl::flags_internal;
+
+class CommandLineFlagTest : public testing::Test {
+ protected:
+ void SetUp() override { flag_saver_ = absl::make_unique<flags::FlagSaver>(); }
+ void TearDown() override { flag_saver_.reset(); }
+
+ private:
+ std::unique_ptr<flags::FlagSaver> flag_saver_;
+};
+
+TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) {
+ auto* flag_01 = flags::FindCommandLineFlag("int_flag");
+
+ ASSERT_TRUE(flag_01);
+ EXPECT_EQ(flag_01->Name(), "int_flag");
+ EXPECT_EQ(flag_01->Help(), "int_flag help");
+ 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"));
+
+ auto* flag_02 = flags::FindCommandLineFlag("string_flag");
+
+ ASSERT_TRUE(flag_02);
+ EXPECT_EQ(flag_02->Name(), "string_flag");
+ EXPECT_EQ(flag_02->Help(), "string_flag help");
+ 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"));
+
+ auto* flag_03 = flags::FindRetiredFlag("bool_retired_flag");
+
+ ASSERT_TRUE(flag_03);
+ EXPECT_EQ(flag_03->Name(), "bool_retired_flag");
+ EXPECT_EQ(flag_03->Help(), "");
+ EXPECT_EQ(flag_03->Typename(), "");
+ EXPECT_TRUE(flag_03->IsRetired());
+ EXPECT_TRUE(flag_03->IsOfType<bool>());
+ EXPECT_EQ(flag_03->Filename(), "RETIRED");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(CommandLineFlagTest, TestValueAccessMethods) {
+ absl::SetFlag(&FLAGS_int_flag, 301);
+ auto* flag_01 = flags::FindCommandLineFlag("int_flag");
+
+ ASSERT_TRUE(flag_01);
+ EXPECT_EQ(flag_01->CurrentValue(), "301");
+ EXPECT_EQ(flag_01->DefaultValue(), "201");
+
+ absl::SetFlag(&FLAGS_string_flag, "new_str_value");
+ auto* flag_02 = flags::FindCommandLineFlag("string_flag");
+
+ ASSERT_TRUE(flag_02);
+ EXPECT_EQ(flag_02->CurrentValue(), "new_str_value");
+ EXPECT_EQ(flag_02->DefaultValue(), "dflt");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(CommandLineFlagTest, TestSetFromStringCurrentValue) {
+ std::string err;
+
+ auto* flag_01 = flags::FindCommandLineFlag("int_flag");
+ EXPECT_FALSE(flag_01->on_command_line);
+
+ EXPECT_TRUE(flag_01->SetFromString("11", flags::SET_FLAGS_VALUE,
+ flags::kProgrammaticChange, &err));
+ EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11);
+ EXPECT_FALSE(flag_01->on_command_line);
+
+ EXPECT_TRUE(flag_01->SetFromString("-123", flags::SET_FLAGS_VALUE,
+ flags::kProgrammaticChange, &err));
+ EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123);
+ EXPECT_FALSE(flag_01->on_command_line);
+
+ EXPECT_TRUE(!flag_01->SetFromString("xyz", flags::SET_FLAGS_VALUE,
+ flags::kProgrammaticChange, &err));
+ EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123);
+ EXPECT_EQ(err, "Illegal value 'xyz' specified for flag 'int_flag'");
+ EXPECT_FALSE(flag_01->on_command_line);
+
+ EXPECT_TRUE(!flag_01->SetFromString("A1", flags::SET_FLAGS_VALUE,
+ flags::kProgrammaticChange, &err));
+ EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123);
+ EXPECT_EQ(err, "Illegal value 'A1' specified for flag 'int_flag'");
+ EXPECT_FALSE(flag_01->on_command_line);
+
+ EXPECT_TRUE(flag_01->SetFromString("0x10", flags::SET_FLAGS_VALUE,
+ flags::kProgrammaticChange, &err));
+ EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 16);
+ EXPECT_FALSE(flag_01->on_command_line);
+
+ EXPECT_TRUE(flag_01->SetFromString("011", flags::SET_FLAGS_VALUE,
+ flags::kCommandLine, &err));
+ EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11);
+ EXPECT_TRUE(flag_01->on_command_line);
+
+ EXPECT_TRUE(!flag_01->SetFromString("", flags::SET_FLAGS_VALUE,
+ flags::kProgrammaticChange, &err));
+ EXPECT_EQ(err, "Illegal value '' specified for flag 'int_flag'");
+
+ auto* flag_02 = flags::FindCommandLineFlag("string_flag");
+ EXPECT_TRUE(flag_02->SetFromString("xyz", flags::SET_FLAGS_VALUE,
+ flags::kProgrammaticChange, &err));
+ EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "xyz");
+
+ EXPECT_TRUE(flag_02->SetFromString("", flags::SET_FLAGS_VALUE,
+ flags::kProgrammaticChange, &err));
+ EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(CommandLineFlagTest, TestSetFromStringDefaultValue) {
+ std::string err;
+
+ auto* flag_01 = flags::FindCommandLineFlag("int_flag");
+
+ EXPECT_TRUE(flag_01->SetFromString("111", flags::SET_FLAGS_DEFAULT,
+ flags::kProgrammaticChange, &err));
+ EXPECT_EQ(flag_01->DefaultValue(), "111");
+
+ auto* flag_02 = flags::FindCommandLineFlag("string_flag");
+
+ EXPECT_TRUE(flag_02->SetFromString("abc", flags::SET_FLAGS_DEFAULT,
+ flags::kProgrammaticChange, &err));
+ EXPECT_EQ(flag_02->DefaultValue(), "abc");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(CommandLineFlagTest, TestSetFromStringIfDefault) {
+ std::string err;
+
+ auto* flag_01 = flags::FindCommandLineFlag("int_flag");
+
+ EXPECT_TRUE(flag_01->SetFromString("22", flags::SET_FLAG_IF_DEFAULT,
+ flags::kProgrammaticChange, &err))
+ << err;
+ EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 22);
+
+ EXPECT_TRUE(flag_01->SetFromString("33", flags::SET_FLAG_IF_DEFAULT,
+ flags::kProgrammaticChange, &err));
+ EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 22);
+ // EXPECT_EQ(err, "ERROR: int_flag is already set to 22");
+
+ // Reset back to default value
+ EXPECT_TRUE(flag_01->SetFromString("201", flags::SET_FLAGS_VALUE,
+ flags::kProgrammaticChange, &err));
+
+ EXPECT_TRUE(flag_01->SetFromString("33", flags::SET_FLAG_IF_DEFAULT,
+ flags::kProgrammaticChange, &err));
+ EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 201);
+ // EXPECT_EQ(err, "ERROR: int_flag is already set to 201");
+}
+
+} // namespace
diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h
new file mode 100644
index 00000000..6402866f
--- /dev/null
+++ b/absl/flags/internal/flag.h
@@ -0,0 +1,103 @@
+//
+// 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.
+
+#ifndef ABSL_FLAGS_INTERNAL_FLAG_H_
+#define ABSL_FLAGS_INTERNAL_FLAG_H_
+
+#include "absl/flags/internal/commandlineflag.h"
+#include "absl/flags/internal/registry.h"
+
+namespace absl {
+namespace flags_internal {
+
+// This is "unspecified" implementation of absl::Flag<T> type.
+template <typename T>
+class Flag {
+ public:
+ constexpr Flag(const char* name, const flags_internal::HelpGenFunc help_gen,
+ const char* filename,
+ const flags_internal::FlagMarshallingOpFn marshalling_op,
+ const flags_internal::InitialValGenFunc initial_value_gen)
+ : internal(name, flags_internal::HelpText::FromFunctionPointer(help_gen),
+ filename, &flags_internal::FlagOps<T>, marshalling_op,
+ initial_value_gen,
+ /*retired_arg=*/false, /*def_arg=*/nullptr,
+ /*cur_arg=*/nullptr) {}
+
+ // Not copyable/assignable.
+ Flag(const Flag<T>&) = delete;
+ Flag<T>& operator=(const Flag<T>&) = delete;
+
+ absl::string_view Name() const { return internal.Name(); }
+ std::string Help() const { return internal.Help(); }
+ std::string Filename() const { return internal.Filename(); }
+
+ absl::flags_internal::CommandLineFlag internal;
+
+ void SetCallback(const flags_internal::FlagCallback mutation_callback) {
+ internal.SetCallback(mutation_callback);
+ }
+
+ private:
+ // TODO(rogeeff): add these validations once UnparseFlag invocation is fixed
+ // for built-in types and when we cleanup existing code from operating on
+ // forward declared types.
+ // auto IsCopyConstructible(const T& v) -> decltype(T(v));
+ // auto HasAbslParseFlag(absl::string_view in, T* dst, std::string* err)
+ // -> decltype(AbslParseFlag(in, dst, GlobalStringADLGuard(err)));
+ // auto HasAbslUnparseFlag(const T& v) -> decltype(AbslUnparseFlag(v));
+};
+
+// This class facilitates Flag object registration and tail expression-based
+// flag definition, for example:
+// ABSL_FLAG(int, foo, 42, "Foo help").OnUpdate(NotifyFooWatcher);
+template <typename T, bool do_register>
+class FlagRegistrar {
+ public:
+ explicit FlagRegistrar(Flag<T>* flag) : flag_(flag) {
+ if (do_register) flags_internal::RegisterCommandLineFlag(&flag_->internal);
+ }
+
+ FlagRegistrar& OnUpdate(flags_internal::FlagCallback cb) && {
+ flag_->SetCallback(cb);
+ return *this;
+ }
+
+ // Make the registrar "die" gracefully as a bool on a line where registration
+ // happens. Registrar objects are intended to live only as temporary.
+ operator bool() const { return true; } // NOLINT
+
+ private:
+ Flag<T>* flag_; // Flag being registered (not owned).
+};
+
+// This struct and corresponding overload to MakeDefaultValue are used to
+// facilitate usage of {} as default value in ABSL_FLAG macro.
+struct EmptyBraces {};
+
+template <typename T>
+T* MakeFromDefaultValue(T t) {
+ return new T(std::move(t));
+}
+
+template <typename T>
+T* MakeFromDefaultValue(EmptyBraces) {
+ return new T;
+}
+
+} // namespace flags_internal
+} // namespace absl
+
+#endif // ABSL_FLAGS_INTERNAL_FLAG_H_
diff --git a/absl/flags/internal/parse.h b/absl/flags/internal/parse.h
new file mode 100644
index 00000000..fd3aca61
--- /dev/null
+++ b/absl/flags/internal/parse.h
@@ -0,0 +1,48 @@
+//
+// 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.
+
+#ifndef ABSL_FLAGS_INTERNAL_PARSE_H_
+#define ABSL_FLAGS_INTERNAL_PARSE_H_
+
+#include <string>
+#include <vector>
+
+#include "absl/flags/declare.h"
+
+ABSL_DECLARE_FLAG(std::vector<std::string>, flagfile);
+ABSL_DECLARE_FLAG(std::vector<std::string>, fromenv);
+ABSL_DECLARE_FLAG(std::vector<std::string>, tryfromenv);
+ABSL_DECLARE_FLAG(std::vector<std::string>, undefok);
+
+namespace absl {
+namespace flags_internal {
+
+enum class ArgvListAction { kRemoveParsedArgs, kKeepParsedArgs };
+enum class UsageFlagsAction { kHandleUsage, kIgnoreUsage };
+enum class OnUndefinedFlag {
+ kIgnoreUndefined,
+ kReportUndefined,
+ kAbortIfUndefined
+};
+
+std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
+ ArgvListAction arg_list_act,
+ UsageFlagsAction usage_flag_act,
+ OnUndefinedFlag on_undef_flag);
+
+} // namespace flags_internal
+} // namespace absl
+
+#endif // ABSL_FLAGS_INTERNAL_PARSE_H_
diff --git a/absl/flags/internal/path_util.h b/absl/flags/internal/path_util.h
new file mode 100644
index 00000000..5615c0e6
--- /dev/null
+++ b/absl/flags/internal/path_util.h
@@ -0,0 +1,60 @@
+//
+// 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.
+
+#ifndef ABSL_FLAGS_INTERNAL_PATH_UTIL_H_
+#define ABSL_FLAGS_INTERNAL_PATH_UTIL_H_
+
+#include "absl/strings/match.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+namespace flags_internal {
+
+// A portable interface that returns the basename of the filename passed as an
+// argument. It is similar to basename(3)
+// <https://linux.die.net/man/3/basename>.
+// For example:
+// flags_internal::Basename("a/b/prog/file.cc")
+// returns "file.cc"
+// flags_internal::Basename("file.cc")
+// returns "file.cc"
+inline absl::string_view Basename(absl::string_view filename) {
+ auto last_slash_pos = filename.find_last_of("/\\");
+
+ return last_slash_pos == absl::string_view::npos
+ ? filename
+ : filename.substr(last_slash_pos + 1);
+}
+
+// A portable interface that returns the directory name of the filename
+// passed as an argument, including the trailing slash.
+// Returns the empty string if a slash is not found in the input file name.
+// For example:
+// flags_internal::Package("a/b/prog/file.cc")
+// returns "a/b/prog/"
+// flags_internal::Package("file.cc")
+// returns ""
+inline absl::string_view Package(absl::string_view filename) {
+ auto last_slash_pos = filename.find_last_of("/\\");
+
+ return last_slash_pos == absl::string_view::npos
+ ? absl::string_view()
+ : filename.substr(0, last_slash_pos + 1);
+}
+
+} // namespace flags_internal
+} // namespace absl
+
+#endif // ABSL_FLAGS_INTERNAL_PATH_UTIL_H_
diff --git a/absl/flags/internal/path_util_test.cc b/absl/flags/internal/path_util_test.cc
new file mode 100644
index 00000000..2091373c
--- /dev/null
+++ b/absl/flags/internal/path_util_test.cc
@@ -0,0 +1,46 @@
+//
+// 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/path_util.h"
+
+#include "gtest/gtest.h"
+
+namespace {
+
+namespace flags = absl::flags_internal;
+
+TEST(FlagsPathUtilTest, TestBasename) {
+ EXPECT_EQ(flags::Basename(""), "");
+ EXPECT_EQ(flags::Basename("a.cc"), "a.cc");
+ EXPECT_EQ(flags::Basename("dir/a.cc"), "a.cc");
+ EXPECT_EQ(flags::Basename("dir1/dir2/a.cc"), "a.cc");
+ EXPECT_EQ(flags::Basename("../dir1/dir2/a.cc"), "a.cc");
+ EXPECT_EQ(flags::Basename("/dir1/dir2/a.cc"), "a.cc");
+ EXPECT_EQ(flags::Basename("/dir1/dir2/../dir3/a.cc"), "a.cc");
+}
+
+// --------------------------------------------------------------------
+
+TEST(FlagsPathUtilTest, TestPackage) {
+ EXPECT_EQ(flags::Package(""), "");
+ EXPECT_EQ(flags::Package("a.cc"), "");
+ EXPECT_EQ(flags::Package("dir/a.cc"), "dir/");
+ EXPECT_EQ(flags::Package("dir1/dir2/a.cc"), "dir1/dir2/");
+ EXPECT_EQ(flags::Package("../dir1/dir2/a.cc"), "../dir1/dir2/");
+ EXPECT_EQ(flags::Package("/dir1/dir2/a.cc"), "/dir1/dir2/");
+ EXPECT_EQ(flags::Package("/dir1/dir2/../dir3/a.cc"), "/dir1/dir2/../dir3/");
+}
+
+} // namespace
diff --git a/absl/flags/internal/program_name.cc b/absl/flags/internal/program_name.cc
new file mode 100644
index 00000000..62be645a
--- /dev/null
+++ b/absl/flags/internal/program_name.cc
@@ -0,0 +1,53 @@
+//
+// 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/program_name.h"
+
+#include <string>
+
+#include "absl/flags/internal/path_util.h"
+#include "absl/synchronization/mutex.h"
+
+namespace absl {
+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;
+
+std::string ProgramInvocationName() {
+ absl::MutexLock l(&program_name_guard);
+
+ return program_name ? *program_name : "UNKNOWN";
+}
+
+std::string ShortProgramInvocationName() {
+ absl::MutexLock l(&program_name_guard);
+
+ return program_name ? std::string(flags_internal::Basename(*program_name))
+ : "UNKNOWN";
+}
+
+void SetProgramInvocationName(absl::string_view prog_name_str) {
+ absl::MutexLock l(&program_name_guard);
+
+ if (!program_name)
+ program_name = new std::string(prog_name_str);
+ else
+ program_name->assign(prog_name_str.data(), prog_name_str.size());
+}
+
+} // namespace flags_internal
+} // namespace absl
diff --git a/absl/flags/internal/program_name.h b/absl/flags/internal/program_name.h
new file mode 100644
index 00000000..326f24bb
--- /dev/null
+++ b/absl/flags/internal/program_name.h
@@ -0,0 +1,47 @@
+//
+// 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.
+
+#ifndef ABSL_FLAGS_INTERNAL_PROGRAM_NAME_H_
+#define ABSL_FLAGS_INTERNAL_PROGRAM_NAME_H_
+
+#include <string>
+
+#include "absl/strings/string_view.h"
+
+// --------------------------------------------------------------------
+// Program name
+
+namespace absl {
+namespace flags_internal {
+
+// Returns program invocation name or "UNKNOWN" if `SetProgramInvocationName()`
+// is never called. At the moment this is always set to argv[0] as part of
+// library initialization.
+std::string ProgramInvocationName();
+
+// Returns base name for program invocation name. For example, if
+// ProgramInvocationName() == "a/b/mybinary"
+// then
+// ShortProgramInvocationName() == "mybinary"
+std::string ShortProgramInvocationName();
+
+// Sets program invocation name to a new value. Should only be called once
+// during program initialization, before any threads are spawned.
+void SetProgramInvocationName(absl::string_view prog_name_str);
+
+} // namespace flags_internal
+} // 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
new file mode 100644
index 00000000..ed69218b
--- /dev/null
+++ b/absl/flags/internal/program_name_test.cc
@@ -0,0 +1,60 @@
+//
+// 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/program_name.h"
+
+#include "gtest/gtest.h"
+#include "absl/strings/match.h"
+
+namespace {
+
+namespace flags = absl::flags_internal;
+
+TEST(FlagsPathUtilTest, TestInitialProgamName) {
+ flags::SetProgramInvocationName("absl/flags/program_name_test");
+ std::string program_name = flags::ProgramInvocationName();
+ for (char& c : program_name)
+ if (c == '\\') c = '/';
+
+#if !defined(__wasm__) && !defined(__asmjs__)
+ const std::string expect_name = "absl/flags/program_name_test";
+ const std::string expect_basename = "program_name_test";
+#else
+ // For targets that generate javascript or webassembly the invocation name
+ // has the special value below.
+ const std::string expect_name = "this.program";
+ const std::string expect_basename = "this.program";
+#endif
+
+ EXPECT_TRUE(absl::EndsWith(program_name, expect_name)) << program_name;
+ EXPECT_EQ(flags::ShortProgramInvocationName(), expect_basename);
+}
+
+TEST(FlagsPathUtilTest, TestProgamNameInterfaces) {
+ flags::SetProgramInvocationName("a/my_test");
+
+ EXPECT_EQ(flags::ProgramInvocationName(), "a/my_test");
+ EXPECT_EQ(flags::ShortProgramInvocationName(), "my_test");
+
+ absl::string_view not_null_terminated("absl/aaa/bbb");
+ not_null_terminated = not_null_terminated.substr(1, 10);
+
+ flags::SetProgramInvocationName(not_null_terminated);
+
+ EXPECT_EQ(flags::ProgramInvocationName(), "bsl/aaa/bb");
+ EXPECT_EQ(flags::ShortProgramInvocationName(), "bb");
+}
+
+} // namespace
diff --git a/absl/flags/internal/registry.cc b/absl/flags/internal/registry.cc
new file mode 100644
index 00000000..435c5b0b
--- /dev/null
+++ b/absl/flags/internal/registry.cc
@@ -0,0 +1,529 @@
+//
+// 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/registry.h"
+
+#include "absl/base/call_once.h"
+#include "absl/base/dynamic_annotations.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/flags/config.h"
+#include "absl/flags/usage_config.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "absl/synchronization/mutex.h"
+
+// --------------------------------------------------------------------
+// FlagRegistry implementation
+// A FlagRegistry holds all flag objects indexed
+// by their names so that if you know a flag's name you can access or
+// set it.
+
+namespace absl {
+namespace flags_internal {
+namespace {
+
+void DestroyFlag(CommandLineFlag* flag) NO_THREAD_SAFETY_ANALYSIS {
+ // Values are heap allocated for retired and Abseil Flags.
+ if (flag->IsRetired() || flag->IsAbseilFlag()) {
+ if (flag->cur) Delete(flag->op, flag->cur);
+ if (flag->def) Delete(flag->op, flag->def);
+ }
+
+ delete flag->locks;
+
+ // CommandLineFlag handle object is heap allocated for non Abseil Flags.
+ if (!flag->IsAbseilFlag()) {
+ delete flag;
+ }
+}
+
+// --------------------------------------------------------------------
+// FlagRegistry
+// A FlagRegistry singleton object holds all flag objects indexed
+// by their names so that if you know a flag's name (as a C
+// string), you can access or set it. If the function is named
+// FooLocked(), you must own the registry lock before calling
+// the function; otherwise, you should *not* hold the lock, and
+// the function will acquire it itself if needed.
+// --------------------------------------------------------------------
+
+// A map from flag pointer to CommandLineFlag*. Used when registering
+// validators.
+class FlagPtrMap {
+ public:
+ void Register(CommandLineFlag* flag) {
+ auto& vec = buckets_[BucketForFlag(flag->cur)];
+ if (vec.size() == vec.capacity()) {
+ // Bypass default 2x growth factor with 1.25 so we have fuller vectors.
+ // This saves 4% memory compared to default growth.
+ vec.reserve(vec.size() * 1.25 + 0.5);
+ }
+ vec.push_back(flag);
+ }
+
+ CommandLineFlag* FindByPtr(const void* flag_ptr) {
+ const auto& flag_vector = buckets_[BucketForFlag(flag_ptr)];
+ for (CommandLineFlag* entry : flag_vector) {
+ if (entry->cur == flag_ptr) {
+ return entry;
+ }
+ }
+ return nullptr;
+ }
+
+ private:
+ // Instead of std::map, we use a custom hash table where each bucket stores
+ // flags in a vector. This reduces memory usage 40% of the memory that would
+ // have been used by std::map.
+ //
+ // kNumBuckets was picked as a large enough prime. As of writing this code, a
+ // typical large binary has ~8k (old-style) flags, and this would gives
+ // buckets with roughly 50 elements each.
+ //
+ // Note that reads to this hash table are rare: exactly as many as we have
+ // flags with validators. As of writing, a typical binary only registers 52
+ // validated flags.
+ static constexpr size_t kNumBuckets = 163;
+ std::vector<CommandLineFlag*> buckets_[kNumBuckets];
+
+ static int BucketForFlag(const void* ptr) {
+ // Modulo a prime is good enough here. On a real program, bucket size stddev
+ // after registering 8k flags is ~5 (mean size at 51).
+ return reinterpret_cast<uintptr_t>(ptr) % kNumBuckets;
+ }
+};
+constexpr size_t FlagPtrMap::kNumBuckets;
+
+} // namespace
+
+class FlagRegistry {
+ public:
+ FlagRegistry() = default;
+ ~FlagRegistry() {
+ for (auto& p : flags_) {
+ DestroyFlag(p.second);
+ }
+ }
+
+ // Store a flag in this registry. Takes ownership of *flag.
+ // If ptr is non-null, the flag can later be found by calling
+ // FindFlagViaPtrLocked(ptr).
+ void RegisterFlag(CommandLineFlag* flag, const void* ptr);
+
+ void Lock() EXCLUSIVE_LOCK_FUNCTION(lock_) { lock_.Lock(); }
+ void Unlock() 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.
+ CommandLineFlag* FindFlagLocked(absl::string_view name);
+
+ // Returns the retired flag object for the specified name, or nullptr if not
+ // found or not retired. Does not emit a warning.
+ CommandLineFlag* FindRetiredFlagLocked(absl::string_view name);
+
+ // Returns the flag object whose current-value is stored at flag_ptr.
+ CommandLineFlag* FindFlagViaPtrLocked(const void* flag_ptr);
+
+ static FlagRegistry* GlobalRegistry(); // returns a singleton registry
+
+ private:
+ friend class FlagSaverImpl; // reads all the flags in order to copy them
+ friend void ForEachFlagUnlocked(
+ std::function<void(CommandLineFlag*)> visitor);
+
+ // The map from name to flag, for FindFlagLocked().
+ using FlagMap = std::map<absl::string_view, CommandLineFlag*>;
+ using FlagIterator = FlagMap::iterator;
+ using FlagConstIterator = FlagMap::const_iterator;
+ FlagMap flags_;
+
+ FlagPtrMap flag_ptr_map_;
+
+ static FlagRegistry* global_registry_; // a singleton registry
+
+ static absl::once_flag global_registry_once_;
+
+ static void InitGlobalRegistry();
+
+ absl::Mutex lock_;
+
+ // Disallow
+ FlagRegistry(const FlagRegistry&);
+ FlagRegistry& operator=(const FlagRegistry&);
+};
+
+// Get the singleton FlagRegistry object
+FlagRegistry* FlagRegistry::global_registry_ = nullptr;
+absl::once_flag FlagRegistry::global_registry_once_;
+
+void FlagRegistry::InitGlobalRegistry() { global_registry_ = new FlagRegistry; }
+
+FlagRegistry* FlagRegistry::GlobalRegistry() {
+ absl::call_once(global_registry_once_, &InitGlobalRegistry);
+
+ return global_registry_;
+}
+
+namespace {
+
+class FlagRegistryLock {
+ public:
+ explicit FlagRegistryLock(FlagRegistry* fr) : fr_(fr) { fr_->Lock(); }
+ ~FlagRegistryLock() { fr_->Unlock(); }
+
+ private:
+ FlagRegistry* const fr_;
+};
+
+} // namespace
+
+void FlagRegistry::RegisterFlag(CommandLineFlag* flag, const void* ptr) {
+ FlagRegistryLock registry_lock(this);
+ std::pair<FlagIterator, bool> ins =
+ flags_.insert(FlagMap::value_type(flag->Name(), flag));
+ if (ins.second == false) { // means the name was already in the map
+ CommandLineFlag* old_flag = ins.first->second;
+ if (flag->IsRetired() != old_flag->IsRetired()) {
+ // All registrations must agree on the 'retired' flag.
+ flags_internal::ReportUsageError(
+ absl::StrCat(
+ "Retired flag '", flag->Name(),
+ "' was defined normally in file '",
+ (flag->IsRetired() ? old_flag->Filename() : flag->Filename()),
+ "'."),
+ true);
+ } else if (flag->op != old_flag->op) {
+ flags_internal::ReportUsageError(
+ absl::StrCat("Flag '", flag->Name(),
+ "' was defined more than once but with "
+ "differing types. Defined in files '",
+ old_flag->Filename(), "' and '", flag->Filename(),
+ "' with types '", old_flag->Typename(), "' and '",
+ flag->Typename(), "', respectively."),
+ true);
+ } else if (old_flag->IsRetired()) {
+ // Retired definitions are idempotent. Just keep the old one.
+ DestroyFlag(flag);
+ return;
+ } else if (old_flag->Filename() != flag->Filename()) {
+ flags_internal::ReportUsageError(
+ absl::StrCat("Flag '", flag->Name(),
+ "' was defined more than once (in files '",
+ old_flag->Filename(), "' and '", flag->Filename(),
+ "')."),
+ true);
+ } else {
+ flags_internal::ReportUsageError(
+ absl::StrCat(
+ "Something wrong with flag '", flag->Name(), "' in file '",
+ flag->Filename(), "'. One possibility: file '", flag->Filename(),
+ "' is being linked both statically and dynamically into this "
+ "executable. e.g. some files listed as srcs to a test and also "
+ "listed as srcs of some shared lib deps of the same test."),
+ true);
+ }
+ // All cases above are fatal, except for the retired flags.
+ std::exit(1);
+ }
+
+ if (ptr != nullptr) {
+ // This must be the first time we're seeing this flag.
+ flag_ptr_map_.Register(flag);
+ }
+}
+
+CommandLineFlag* FlagRegistry::FindFlagLocked(absl::string_view name) {
+ FlagConstIterator i = flags_.find(name);
+ if (i == flags_.end()) {
+ return nullptr;
+ }
+
+ if (i->second->IsRetired()) {
+ flags_internal::ReportUsageError(
+ absl::StrCat("Accessing retired flag '", name, "'"), false);
+ }
+
+ return i->second;
+}
+
+CommandLineFlag* FlagRegistry::FindRetiredFlagLocked(absl::string_view name) {
+ FlagConstIterator i = flags_.find(name);
+ if (i == flags_.end() || !i->second->IsRetired()) {
+ return nullptr;
+ }
+
+ return i->second;
+}
+
+CommandLineFlag* FlagRegistry::FindFlagViaPtrLocked(const void* flag_ptr) {
+ return flag_ptr_map_.FindByPtr(flag_ptr);
+}
+
+// --------------------------------------------------------------------
+// FlagSaver
+// FlagSaverImpl
+// This class stores the states of all flags at construct time,
+// and restores all flags to that state at destruct time.
+// Its major implementation challenge is that it never modifies
+// pointers in the 'main' registry, so global FLAG_* vars always
+// point to the right place.
+// --------------------------------------------------------------------
+
+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);
+ }
+ }
+
+ // 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(InitFlagIfNecessary(flag));
+ saved.validator = flag->validator;
+ saved.modified = flag->modified;
+ saved.on_command_line = flag->IsSpecifiedOnCommandLine();
+ saved.current = Clone(saved.op, flag->cur);
+ saved.default_value = Clone(saved.op, flag->def);
+ saved.counter = flag->counter;
+ }
+ 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.
+ 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::Mutex* mu = InitFlagIfNecessary(flag);
+ absl::MutexLock l(mu);
+ flag->validator = src.validator;
+ flag->modified = src.modified;
+ flag->on_command_line = src.on_command_line;
+ if (flag->counter != src.counter ||
+ ChangedDirectly(flag, src.default_value, flag->def)) {
+ flag->counter++;
+ Copy(src.op, src.default_value, flag->def);
+ }
+ if (flag->counter != src.counter ||
+ ChangedDirectly(flag, src.current, flag->cur)) {
+ restored = true;
+ flag->counter++;
+ Copy(src.op, src.current, flag->cur);
+ UpdateCopy(flag, mu);
+
+ // Revalidate the flag because the validator might store state based
+ // on the flag's value, which just changed due to the restore.
+ // Failing validation is ignored because it's assumed that the flag
+ // was valid previously and there's little that can be done about it
+ // here, anyway.
+ Validate(flag, flag->cur);
+ }
+ }
+
+ // Log statements must be done when no flag lock is held.
+ if (restored) {
+ ABSL_INTERNAL_LOG(
+ INFO, absl::StrCat("Restore saved value of ", flag->Name(), ": ",
+ Unparse(src.marshalling_op, src.current)));
+ }
+ }
+ }
+
+ 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&);
+};
+
+FlagSaver::FlagSaver() : impl_(new FlagSaverImpl()) {
+ impl_->SaveFromRegistry();
+}
+
+void FlagSaver::Ignore() {
+ delete impl_;
+ impl_ = nullptr;
+}
+
+FlagSaver::~FlagSaver() {
+ if (!impl_) return;
+
+ impl_->RestoreToRegistry();
+ delete impl_;
+}
+
+// --------------------------------------------------------------------
+// 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();
+
+ UpdateModifiedBit(flag);
+
+ absl::MutexLock l(InitFlagIfNecessary(flag));
+ result->current_value = flag->CurrentValue();
+ result->default_value = flag->DefaultValue();
+ result->is_default = !flag->modified;
+ result->has_validator_fn = (flag->validator != nullptr);
+ result->flag_ptr = flag->IsAbseilFlag() ? nullptr : flag->cur;
+}
+
+// --------------------------------------------------------------------
+
+CommandLineFlag* FindCommandLineFlag(absl::string_view name) {
+ if (name.empty()) return nullptr;
+ FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
+ FlagRegistryLock frl(registry);
+
+ return registry->FindFlagLocked(name);
+}
+
+CommandLineFlag* FindCommandLineV1Flag(const void* flag_ptr) {
+ FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
+ FlagRegistryLock frl(registry);
+
+ return registry->FindFlagViaPtrLocked(flag_ptr);
+}
+
+CommandLineFlag* FindRetiredFlag(absl::string_view name) {
+ FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
+ FlagRegistryLock frl(registry);
+
+ return registry->FindRetiredFlagLocked(name);
+}
+
+// --------------------------------------------------------------------
+
+void ForEachFlagUnlocked(std::function<void(CommandLineFlag*)> visitor) {
+ FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
+ for (FlagRegistry::FlagConstIterator i = registry->flags_.begin();
+ i != registry->flags_.end(); ++i) {
+ visitor(i->second);
+ }
+}
+
+void ForEachFlag(std::function<void(CommandLineFlag*)> visitor) {
+ FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
+ FlagRegistryLock frl(registry);
+ ForEachFlagUnlocked(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, const void* ptr) {
+ FlagRegistry::GlobalRegistry()->RegisterFlag(flag, ptr);
+ return true;
+}
+
+// --------------------------------------------------------------------
+
+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);
+ FlagRegistry::GlobalRegistry()->RegisterFlag(flag, nullptr);
+ return true;
+}
+
+// --------------------------------------------------------------------
+
+bool IsRetiredFlag(absl::string_view name, bool* type_is_bool) {
+ assert(!name.empty());
+ CommandLineFlag* flag = flags_internal::FindRetiredFlag(name);
+ if (flag == nullptr) {
+ return false;
+ }
+ assert(type_is_bool);
+ *type_is_bool = flag->IsOfType<bool>();
+ return true;
+}
+
+} // namespace flags_internal
+} // namespace absl
diff --git a/absl/flags/internal/registry.h b/absl/flags/internal/registry.h
new file mode 100644
index 00000000..bd141e1e
--- /dev/null
+++ b/absl/flags/internal/registry.h
@@ -0,0 +1,168 @@
+//
+// 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.
+
+#ifndef ABSL_FLAGS_INTERNAL_REGISTRY_H_
+#define ABSL_FLAGS_INTERNAL_REGISTRY_H_
+
+#include <functional>
+#include <map>
+#include <string>
+
+#include "absl/base/macros.h"
+#include "absl/flags/internal/commandlineflag.h"
+
+// --------------------------------------------------------------------
+// Global flags registry API.
+
+namespace absl {
+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* FindCommandLineV1Flag(const void* flag_ptr);
+CommandLineFlag* FindRetiredFlag(absl::string_view name);
+
+// Executes specified visitor for each non-retired flag in the registry.
+// Requires the caller hold the registry lock.
+void ForEachFlagUnlocked(std::function<void(CommandLineFlag*)> visitor);
+// Executes specified visitor for each non-retired flag in the registry. While
+// callback are executed, the registry is locked and can't be changed.
+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*, const void* ptr = nullptr);
+
+//-----------------------------------------------------------------------------
+// Retired registrations:
+//
+// Retired flag registrations are treated specially. A 'retired' flag is
+// provided only for compatibility with automated invocations that still
+// name it. A 'retired' flag:
+// - is not bound to a C++ FLAGS_ reference.
+// - has a type and a value, but that value is intentionally inaccessible.
+// - does not appear in --help messages.
+// - is fully supported by _all_ flag parsing routines.
+// - consumes args normally, and complains about type mismatches in its
+// argument.
+// - emits a complaint but does not die (e.g. LOG(ERROR)) if it is
+// accessed by name through the flags API for parsing or otherwise.
+//
+// The registrations for a flag happen in an unspecified order as the
+// initializers for the namespace-scope objects of a program are run.
+// Any number of weak registrations for a flag can weakly define the flag.
+// One non-weak registration will upgrade the flag from weak to non-weak.
+// Further weak registrations of a non-weak flag are ignored.
+//
+// This mechanism is designed to support moving dead flags into a
+// 'graveyard' library. An example migration:
+//
+// 0: Remove references to this FLAGS_flagname in the C++ codebase.
+// 1: Register as 'retired' in old_lib.
+// 2: Make old_lib depend on graveyard.
+// 3: Add a redundant 'retired' registration to graveyard.
+// 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);
+
+// 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);
+}
+
+// If the flag is retired, returns true and indicates in |*type_is_bool|
+// whether the type of the retired flag is a bool.
+// Only to be called by code that needs to explicitly ignore retired flags.
+bool IsRetiredFlag(absl::string_view name, bool* type_is_bool);
+
+//-----------------------------------------------------------------------------
+// Saves the states (value, default value, whether the user has set
+// the flag, registered validators, etc) of all flags, and restores
+// them when the FlagSaver is destroyed.
+//
+// This class is thread-safe. However, its destructor writes to
+// exactly the set of flags that have changed value during its
+// lifetime, so concurrent _direct_ access to those flags
+// (i.e. FLAGS_foo instead of {Get,Set}CommandLineOption()) is unsafe.
+
+class FlagSaver {
+ public:
+ FlagSaver();
+ ~FlagSaver();
+
+ FlagSaver(const FlagSaver&) = delete;
+ void operator=(const FlagSaver&) = delete;
+
+ // Prevents saver from restoring the saved state of flags.
+ void Ignore();
+
+ private:
+ class FlagSaverImpl* impl_; // we use pimpl here to keep API steady
+};
+
+} // namespace flags_internal
+} // namespace absl
+
+#endif // ABSL_FLAGS_INTERNAL_REGISTRY_H_
diff --git a/absl/flags/internal/type_erased.cc b/absl/flags/internal/type_erased.cc
new file mode 100644
index 00000000..cc103983
--- /dev/null
+++ b/absl/flags/internal/type_erased.cc
@@ -0,0 +1,121 @@
+//
+// 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/type_erased.h"
+
+#include "absl/base/internal/raw_logging.h"
+#include "absl/flags/config.h"
+#include "absl/flags/usage_config.h"
+#include "absl/strings/str_cat.h"
+
+namespace absl {
+namespace flags_internal {
+
+bool GetCommandLineOption(absl::string_view name, std::string* value) {
+ if (name.empty()) return false;
+ assert(value);
+
+ CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name);
+ if (flag == nullptr || flag->IsRetired()) {
+ return false;
+ }
+
+ absl::MutexLock l(InitFlagIfNecessary(flag));
+ *value = flag->CurrentValue();
+ 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);
+}
+
+bool SetCommandLineOptionWithMode(absl::string_view name,
+ absl::string_view value,
+ FlagSettingMode set_mode) {
+ CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name);
+
+ if (!flag || flag->IsRetired()) return false;
+
+ std::string error;
+ if (!flag->SetFromString(value, set_mode, kProgrammaticChange, &error)) {
+ // Errors here are all of the form: the provided name was a recognized
+ // flag, but the value was invalid (bad type, or validation failed).
+ flags_internal::ReportUsageError(error, false);
+ return false;
+ }
+
+ return true;
+}
+
+// --------------------------------------------------------------------
+
+bool IsValidFlagValue(absl::string_view name, absl::string_view value) {
+ CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name);
+ if (flag == nullptr) {
+ return false;
+ }
+
+ if (flag->IsRetired()) {
+ return true;
+ }
+
+ // No need to lock the flag since we are not mutating it.
+ void* obj = Clone(flag->op, flag->def);
+ std::string ignored_error;
+ const bool result =
+ flags_internal::Parse(flag->marshalling_op, value, obj, &ignored_error) &&
+ Validate(flag, obj);
+ Delete(flag->op, obj);
+ return result;
+}
+
+// --------------------------------------------------------------------
+
+bool SpecifiedOnCommandLine(absl::string_view name) {
+ CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name);
+ if (flag != nullptr && !flag->IsRetired()) {
+ absl::MutexLock l(InitFlagIfNecessary(flag));
+ return flag->IsSpecifiedOnCommandLine();
+ }
+ return false;
+}
+
+} // namespace flags_internal
+} // namespace absl
diff --git a/absl/flags/internal/type_erased.h b/absl/flags/internal/type_erased.h
new file mode 100644
index 00000000..249d36b9
--- /dev/null
+++ b/absl/flags/internal/type_erased.h
@@ -0,0 +1,97 @@
+//
+// 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.
+
+#ifndef ABSL_FLAGS_INTERNAL_TYPE_ERASED_H_
+#define ABSL_FLAGS_INTERNAL_TYPE_ERASED_H_
+
+#include <string>
+
+#include "absl/flags/internal/commandlineflag.h"
+#include "absl/flags/internal/registry.h"
+
+// --------------------------------------------------------------------
+// Registry interfaces operating on type erased handles.
+
+namespace absl {
+namespace flags_internal {
+
+// If a flag named "name" exists, store its current value in *OUTPUT
+// and return true. Else return false without changing *OUTPUT.
+// 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.
+// Thread-safe.
+bool SetCommandLineOption(absl::string_view name, absl::string_view value);
+
+bool SetCommandLineOptionWithMode(absl::string_view name,
+ absl::string_view value,
+ FlagSettingMode set_mode);
+
+//-----------------------------------------------------------------------------
+
+// Returns true iff all of the following conditions are true:
+// (a) "name" names a registered flag
+// (b) "value" can be parsed succesfully according to the type of the flag
+// (c) parsed value passes any validator associated with the flag
+bool IsValidFlagValue(absl::string_view name, absl::string_view value);
+
+//-----------------------------------------------------------------------------
+
+// Returns true iff a flag named "name" was specified on the command line
+// (either directly, or via one of --flagfile or --fromenv or --tryfromenv).
+//
+// Any non-command-line modification of the flag does not affect the
+// result of this function. So for example, if a flag was passed on
+// the command line but then reset via SET_FLAGS_DEFAULT, this
+// function will still return true.
+bool SpecifiedOnCommandLine(absl::string_view name);
+
+//-----------------------------------------------------------------------------
+
+// If a flag with specified "name" exists and has type T, store
+// its current value in *dst and return true. Else return false
+// without touching *dst. T must obey all of the requirements for
+// types passed to DEFINE_FLAG.
+template <typename T>
+inline bool GetByName(absl::string_view name, T* dst) {
+ CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name);
+ if (!flag) return false;
+
+ if (auto val = flag->Get<T>()) {
+ *dst = *val;
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace flags_internal
+} // 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
new file mode 100644
index 00000000..ac749a60
--- /dev/null
+++ b/absl/flags/internal/type_erased_test.cc
@@ -0,0 +1,147 @@
+//
+// 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/type_erased.h"
+
+#include <cmath>
+
+#include "gtest/gtest.h"
+#include "absl/flags/flag.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");
+ABSL_RETIRED_FLAG(bool, bool_retired_flag, false, "bool_retired_flag help");
+
+namespace {
+
+namespace flags = absl::flags_internal;
+
+class TypeErasedTest : public testing::Test {
+ protected:
+ void SetUp() override { flag_saver_ = absl::make_unique<flags::FlagSaver>(); }
+ void TearDown() override { flag_saver_.reset(); }
+
+ private:
+ std::unique_ptr<flags::FlagSaver> flag_saver_;
+};
+
+// --------------------------------------------------------------------
+
+TEST_F(TypeErasedTest, TestGetCommandLineOption) {
+ std::string value;
+ EXPECT_TRUE(flags::GetCommandLineOption("int_flag", &value));
+ EXPECT_EQ(value, "1");
+
+ EXPECT_TRUE(flags::GetCommandLineOption("string_flag", &value));
+ EXPECT_EQ(value, "dflt");
+
+ EXPECT_FALSE(flags::GetCommandLineOption("bool_retired_flag", &value));
+
+ EXPECT_FALSE(flags::GetCommandLineOption("unknown_flag", &value));
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(TypeErasedTest, TestSetCommandLineOption) {
+ EXPECT_TRUE(flags::SetCommandLineOption("int_flag", "101"));
+ EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 101);
+
+ EXPECT_TRUE(flags::SetCommandLineOption("string_flag", "asdfgh"));
+ EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh");
+
+ EXPECT_FALSE(flags::SetCommandLineOption("bool_retired_flag", "true"));
+
+ EXPECT_FALSE(flags::SetCommandLineOption("unknown_flag", "true"));
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(TypeErasedTest, TestSetCommandLineOptionWithMode_SET_FLAGS_VALUE) {
+ EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "101",
+ flags::SET_FLAGS_VALUE));
+ EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 101);
+
+ EXPECT_TRUE(flags::SetCommandLineOptionWithMode("string_flag", "asdfgh",
+ flags::SET_FLAGS_VALUE));
+ EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh");
+
+ EXPECT_FALSE(flags::SetCommandLineOptionWithMode("bool_retired_flag", "true",
+ flags::SET_FLAGS_VALUE));
+
+ EXPECT_FALSE(flags::SetCommandLineOptionWithMode("unknown_flag", "true",
+ flags::SET_FLAGS_VALUE));
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(TypeErasedTest, TestSetCommandLineOptionWithMode_SET_FLAG_IF_DEFAULT) {
+ EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "101",
+ flags::SET_FLAG_IF_DEFAULT));
+ EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 101);
+
+ // This semantic is broken. We return true instead of false. Value is not
+ // updated.
+ EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "202",
+ flags::SET_FLAG_IF_DEFAULT));
+ EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 101);
+
+ EXPECT_TRUE(flags::SetCommandLineOptionWithMode("string_flag", "asdfgh",
+ flags::SET_FLAG_IF_DEFAULT));
+ EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh");
+
+ EXPECT_FALSE(flags::SetCommandLineOptionWithMode("bool_retired_flag", "true",
+ flags::SET_FLAG_IF_DEFAULT));
+
+ EXPECT_FALSE(flags::SetCommandLineOptionWithMode("unknown_flag", "true",
+ flags::SET_FLAG_IF_DEFAULT));
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(TypeErasedTest, TestSetCommandLineOptionWithMode_SET_FLAGS_DEFAULT) {
+ EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "101",
+ flags::SET_FLAGS_DEFAULT));
+
+ EXPECT_TRUE(flags::SetCommandLineOptionWithMode("string_flag", "asdfgh",
+ flags::SET_FLAGS_DEFAULT));
+ EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh");
+
+ EXPECT_FALSE(flags::SetCommandLineOptionWithMode("bool_retired_flag", "true",
+ flags::SET_FLAGS_DEFAULT));
+
+ EXPECT_FALSE(flags::SetCommandLineOptionWithMode("unknown_flag", "true",
+ flags::SET_FLAGS_DEFAULT));
+
+ // This should be successfull, since flag is still is not set
+ EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "202",
+ flags::SET_FLAG_IF_DEFAULT));
+ EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 202);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(TypeErasedTest, TestIsValidFlagValue) {
+ EXPECT_TRUE(flags::IsValidFlagValue("int_flag", "57"));
+ EXPECT_TRUE(flags::IsValidFlagValue("int_flag", "-101"));
+ EXPECT_FALSE(flags::IsValidFlagValue("int_flag", "1.1"));
+
+ EXPECT_TRUE(flags::IsValidFlagValue("string_flag", "#%^#%^$%DGHDG$W%adsf"));
+
+ EXPECT_TRUE(flags::IsValidFlagValue("bool_retired_flag", "true"));
+}
+
+} // namespace
diff --git a/absl/flags/internal/usage.cc b/absl/flags/internal/usage.cc
new file mode 100644
index 00000000..f17cc6c5
--- /dev/null
+++ b/absl/flags/internal/usage.cc
@@ -0,0 +1,392 @@
+//
+// 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/usage.h"
+
+#include <map>
+#include <string>
+
+#include "absl/flags/flag.h"
+#include "absl/flags/internal/path_util.h"
+#include "absl/flags/internal/program_name.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/synchronization/mutex.h"
+
+ABSL_FLAG(bool, help, false,
+ "show help on important flags for this binary [tip: all flags can "
+ "have two dashes]");
+ABSL_FLAG(bool, helpfull, false, "show help on all flags");
+ABSL_FLAG(bool, helpshort, false,
+ "show help on only the main module for this program");
+ABSL_FLAG(bool, helppackage, false,
+ "show help on all modules in the main package");
+ABSL_FLAG(bool, version, false, "show version and build info and exit");
+ABSL_FLAG(bool, only_check_args, false, "exit after checking all flags");
+ABSL_FLAG(std::string, helpon, "",
+ "show help on the modules named by this flag value");
+ABSL_FLAG(std::string, helpmatch, "",
+ "show help on modules whose name contains the specified substr");
+
+namespace absl {
+namespace flags_internal {
+namespace {
+
+// 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:
+// std::cout << XMLElement("title", "Milk & Cookies");
+// prints "<title>Milk &amp; Cookies</title>"
+class XMLElement {
+ public:
+ XMLElement(absl::string_view tag, absl::string_view txt)
+ : tag_(tag), txt_(txt) {}
+
+ friend std::ostream& operator<<(std::ostream& out,
+ const XMLElement& xml_elem) {
+ out << "<" << xml_elem.tag_ << ">";
+
+ for (auto c : xml_elem.txt_) {
+ switch (c) {
+ case '"':
+ out << "&quot;";
+ break;
+ case '\'':
+ out << "&apos;";
+ break;
+ case '&':
+ out << "&amp;";
+ break;
+ case '<':
+ out << "&lt;";
+ break;
+ case '>':
+ out << "&gt;";
+ break;
+ default:
+ out << c;
+ break;
+ }
+ }
+
+ return out << "</" << xml_elem.tag_ << ">";
+ }
+
+ private:
+ absl::string_view tag_;
+ absl::string_view txt_;
+};
+
+// --------------------------------------------------------------------
+// Helper class to pretty-print info about a flag.
+
+class FlagHelpPrettyPrinter {
+ public:
+ // Pretty printer holds on to the std::ostream& reference to direct an output
+ // to that stream.
+ FlagHelpPrettyPrinter(int max_line_len, std::ostream* out)
+ : out_(*out),
+ max_line_len_(max_line_len),
+ line_len_(0),
+ first_line_(true) {}
+
+ void Write(absl::string_view str, bool wrap_line = false) {
+ // Empty std::string - do nothing.
+ if (str.empty()) return;
+
+ std::vector<absl::string_view> tokens;
+ if (wrap_line) {
+ tokens = absl::StrSplit(str, absl::ByAnyChar(" \f\n\r\t\v"),
+ absl::SkipEmpty());
+ } else {
+ tokens.push_back(str);
+ }
+
+ for (auto token : tokens) {
+ bool new_line = (line_len_ == 0);
+
+ // Write the token, ending the std::string first if necessary/possible.
+ if (!new_line && (line_len_ + token.size() >= max_line_len_)) {
+ EndLine();
+ new_line = true;
+ }
+
+ if (new_line) {
+ StartLine();
+ } else {
+ out_ << ' ';
+ ++line_len_;
+ }
+
+ out_ << token;
+ line_len_ += token.size();
+ }
+ }
+
+ void StartLine() {
+ if (first_line_) {
+ out_ << " ";
+ line_len_ = 4;
+ first_line_ = false;
+ } else {
+ out_ << " ";
+ line_len_ = 6;
+ }
+ }
+ void EndLine() {
+ out_ << '\n';
+ line_len_ = 0;
+ }
+
+ private:
+ std::ostream& out_;
+ const int max_line_len_;
+ int line_len_;
+ bool first_line_;
+};
+
+void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag,
+ std::ostream* out) {
+ FlagHelpPrettyPrinter printer(80, out); // Max line length is 80.
+
+ // 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(), ";"));
+ }
+
+ // The listed default value will be the actual default from the flag
+ // definition in the originating source file, unless the value has
+ // subsequently been modified using SetCommandLineOption() with mode
+ // SET_FLAGS_DEFAULT.
+ std::string dflt_val = flag.DefaultValue();
+ if (flag.IsOfType<std::string>()) {
+ dflt_val = absl::StrCat("\"", dflt_val, "\"");
+ }
+ printer.Write(absl::StrCat("default: ", dflt_val, ";"));
+
+ if (flag.modified) {
+ std::string curr_val = flag.CurrentValue();
+ if (flag.IsOfType<std::string>()) {
+ curr_val = absl::StrCat("\"", curr_val, "\"");
+ }
+ printer.Write(absl::StrCat("currently: ", curr_val, ";"));
+ }
+
+ printer.EndLine();
+}
+
+// Shows help for every filename which matches any of the filters
+// If filters are empty, shows help for every file.
+// If a flag's help message has been stripped (e.g. by adding '#define
+// STRIP_FLAG_HELP 1' then this flag will not be displayed by '--help'
+// and its variants.
+void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
+ HelpFormat format = HelpFormat::kHumanReadable) {
+ if (format == HelpFormat::kHumanReadable) {
+ out << flags_internal::ShortProgramInvocationName() << ": "
+ << flags_internal::ProgramUsageMessage() << "\n\n";
+ } else {
+ // XML schema is not a part of our public API for now.
+ out << "<?xml version=\"1.0\"?>\n"
+ // The document.
+ << "<AllFlags>\n"
+ // The program name and usage.
+ << XMLElement("program", flags_internal::ShortProgramInvocationName())
+ << '\n'
+ << XMLElement("usage", flags_internal::ProgramUsageMessage()) << '\n';
+ }
+
+ // Map of package name to
+ // map of file name to
+ // vector of flags in the file.
+ // This map is used to output matching flags grouped by package and file
+ // name.
+ std::map<std::string,
+ std::map<std::string,
+ std::vector<const flags_internal::CommandLineFlag*>>>
+ matching_flags;
+
+ flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) {
+ absl::MutexLock l(InitFlagIfNecessary(flag));
+
+ std::string flag_filename = flag->Filename();
+
+ // Ignore retired flags.
+ if (flag->IsRetired()) return;
+
+ // If the flag has been stripped, pretend that it doesn't exist.
+ if (flag->Help() == flags_internal::kStrippedFlagHelp) return;
+
+ // Make sure flag satisfies the filter
+ if (!filter_cb || !filter_cb(flag_filename)) return;
+
+ matching_flags[std::string(flags_internal::Package(flag_filename))]
+ [flag_filename]
+ .push_back(flag);
+ });
+
+ absl::string_view
+ package_separator; // controls blank lines between packages.
+ absl::string_view file_separator; // controls blank lines between files.
+ for (const auto& package : matching_flags) {
+ if (format == HelpFormat::kHumanReadable) {
+ out << package_separator;
+ package_separator = "\n\n";
+ }
+
+ file_separator = "";
+ for (const auto& flags_in_file : package.second) {
+ if (format == HelpFormat::kHumanReadable) {
+ out << file_separator << " Flags from " << flags_in_file.first
+ << ":\n";
+ file_separator = "\n";
+ }
+
+ for (const auto* flag : flags_in_file.second) {
+ flags_internal::FlagHelp(out, *flag, format);
+ }
+ }
+ }
+
+ if (format == HelpFormat::kHumanReadable) {
+ if (filter_cb && matching_flags.empty()) {
+ out << " No modules matched: use -helpfull\n";
+ }
+ } else {
+ // The end of the document.
+ out << "</AllFlags>\n";
+ }
+}
+
+ABSL_CONST_INIT absl::Mutex usage_message_guard(absl::kConstInit);
+ABSL_CONST_INIT std::string* program_usage_message
+ GUARDED_BY(usage_message_guard) = nullptr;
+
+} // namespace
+
+// --------------------------------------------------------------------
+// Sets the "usage" message to be used by help reporting routines.
+
+void SetProgramUsageMessage(absl::string_view new_usage_message) {
+ absl::MutexLock l(&usage_message_guard);
+
+ if (flags_internal::program_usage_message != nullptr) {
+ ABSL_INTERNAL_LOG(FATAL, "SetProgramUsageMessage() called twice.");
+ std::exit(1);
+ }
+
+ program_usage_message = new std::string(new_usage_message);
+}
+
+// --------------------------------------------------------------------
+// Returns the usage message set by SetProgramUsageMessage().
+// Note: We able to return string_view here only because calling
+// SetProgramUsageMessage twice is prohibited.
+absl::string_view ProgramUsageMessage() {
+ absl::MutexLock l(&usage_message_guard);
+
+ return program_usage_message != nullptr
+ ? absl::string_view(*program_usage_message)
+ : "Warning: SetProgramUsageMessage() never called";
+}
+
+// --------------------------------------------------------------------
+// Produces the help message describing specific flag.
+void FlagHelp(std::ostream& out, const flags_internal::CommandLineFlag& flag,
+ HelpFormat format) {
+ if (format == HelpFormat::kHumanReadable)
+ flags_internal::FlagHelpHumanReadable(flag, &out);
+}
+
+// --------------------------------------------------------------------
+// Produces the help messages for all flags matching the filter.
+// If filter is empty produces help messages for all flags.
+void FlagsHelp(std::ostream& out, absl::string_view filter, HelpFormat format) {
+ flags_internal::FlagKindFilter filter_cb = [&](absl::string_view filename) {
+ return filter.empty() || filename.find(filter) != absl::string_view::npos;
+ };
+ flags_internal::FlagsHelpImpl(out, filter_cb, format);
+}
+
+// --------------------------------------------------------------------
+// Checks all the 'usage' command line flags to see if any have been set.
+// If so, handles them appropriately.
+int HandleUsageFlags(std::ostream& out) {
+ if (absl::GetFlag(FLAGS_helpshort)) {
+ flags_internal::FlagsHelpImpl(
+ out, flags_internal::GetUsageConfig().contains_helpshort_flags,
+ HelpFormat::kHumanReadable);
+ return 1;
+ }
+
+ if (absl::GetFlag(FLAGS_helpfull)) {
+ // show all options
+ flags_internal::FlagsHelp(out);
+ return 1;
+ }
+
+ if (!absl::GetFlag(FLAGS_helpon).empty()) {
+ flags_internal::FlagsHelp(
+ out, absl::StrCat("/", absl::GetFlag(FLAGS_helpon), "."));
+ return 1;
+ }
+
+ if (!absl::GetFlag(FLAGS_helpmatch).empty()) {
+ flags_internal::FlagsHelp(out, absl::GetFlag(FLAGS_helpmatch));
+ return 1;
+ }
+
+ if (absl::GetFlag(FLAGS_help)) {
+ flags_internal::FlagsHelpImpl(
+ out, flags_internal::GetUsageConfig().contains_help_flags);
+
+ out << "\nTry --helpfull to get a list of all flags.\n";
+
+ return 1;
+ }
+
+ if (absl::GetFlag(FLAGS_helppackage)) {
+ flags_internal::FlagsHelpImpl(
+ out, flags_internal::GetUsageConfig().contains_helppackage_flags);
+
+ out << "\nTry --helpfull to get a list of all flags.\n";
+
+ return 1;
+ }
+
+ if (absl::GetFlag(FLAGS_version)) {
+ if (flags_internal::GetUsageConfig().version_string)
+ out << flags_internal::GetUsageConfig().version_string();
+ // Unlike help, we may be asking for version in a script, so return 0
+ return 0;
+ }
+
+ if (absl::GetFlag(FLAGS_only_check_args)) {
+ return 0;
+ }
+
+ return -1;
+}
+
+} // namespace flags_internal
+} // namespace absl
diff --git a/absl/flags/internal/usage.h b/absl/flags/internal/usage.h
new file mode 100644
index 00000000..2d0ea7a7
--- /dev/null
+++ b/absl/flags/internal/usage.h
@@ -0,0 +1,91 @@
+//
+// 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.
+
+#ifndef ABSL_FLAGS_INTERNAL_USAGE_H_
+#define ABSL_FLAGS_INTERNAL_USAGE_H_
+
+#include <iosfwd>
+#include <string>
+
+#include "absl/flags/declare.h"
+#include "absl/flags/internal/commandlineflag.h"
+#include "absl/strings/string_view.h"
+
+// --------------------------------------------------------------------
+// Usage reporting interfaces
+
+namespace absl {
+namespace flags_internal {
+
+// Sets the "usage" message to be used by help reporting routines.
+// For example:
+// absl::SetProgramUsageMessage(
+// absl::StrCat("This program does nothing. Sample usage:\n", argv[0],
+// " <uselessarg1> <uselessarg2>"));
+// Do not include commandline flags in the usage: we do that for you!
+// Note: Calling SetProgramUsageMessage twice will trigger a call to std::exit.
+void SetProgramUsageMessage(absl::string_view new_usage_message);
+
+// Returns the usage message set by SetProgramUsageMessage().
+absl::string_view ProgramUsageMessage();
+
+// --------------------------------------------------------------------
+
+// The format to report the help messages in.
+enum class HelpFormat {
+ kHumanReadable,
+};
+
+// Outputs the help message describing specific flag.
+void FlagHelp(std::ostream& out, const flags_internal::CommandLineFlag& flag,
+ HelpFormat format = HelpFormat::kHumanReadable);
+
+// Produces the help messages for all flags matching the filter. A flag matches
+// the filter if it is defined in a file with a filename which includes
+// filter string as a substring. You can use '/' and '.' to restrict the
+// matching to a specific file names. For example:
+// FlagsHelp(out, "/path/to/file.");
+// restricts help to only flags which resides in files named like:
+// .../path/to/file.<ext>
+// for any extension 'ext'. If the filter is empty this function produces help
+// messages for all flags.
+void FlagsHelp(std::ostream& out, absl::string_view filter = {},
+ HelpFormat format = HelpFormat::kHumanReadable);
+
+// --------------------------------------------------------------------
+
+// If any of the 'usage' related command line flags (listed on the bottom of
+// this file) has been set this routine produces corresponding help message in
+// the specified output stream and returns:
+// 0 - if "version" or "only_check_flags" flags were set and handled.
+// 1 - if some other 'usage' related flag was set and handled.
+// -1 - if no usage flags were set on a commmand line.
+// Non negative return values are expected to be used as an exit code for a
+// binary.
+int HandleUsageFlags(std::ostream& out);
+
+} // namespace flags_internal
+} // namespace absl
+
+ABSL_DECLARE_FLAG(bool, help);
+ABSL_DECLARE_FLAG(bool, helpfull);
+ABSL_DECLARE_FLAG(bool, helpshort);
+ABSL_DECLARE_FLAG(bool, helppackage);
+ABSL_DECLARE_FLAG(bool, version);
+ABSL_DECLARE_FLAG(bool, only_check_args);
+ABSL_DECLARE_FLAG(std::string, helpon);
+ABSL_DECLARE_FLAG(std::string, helpmatch);
+
+#endif // ABSL_FLAGS_INTERNAL_USAGE_H_
diff --git a/absl/flags/internal/usage_test.cc b/absl/flags/internal/usage_test.cc
new file mode 100644
index 00000000..cb40aa42
--- /dev/null
+++ b/absl/flags/internal/usage_test.cc
@@ -0,0 +1,367 @@
+//
+// 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 <sstream>
+
+#include "gtest/gtest.h"
+#include "absl/flags/flag.h"
+#include "absl/flags/parse.h"
+#include "absl/flags/internal/path_util.h"
+#include "absl/flags/internal/program_name.h"
+#include "absl/flags/internal/usage.h"
+#include "absl/flags/usage_config.h"
+#include "absl/memory/memory.h"
+#include "absl/strings/match.h"
+
+ABSL_FLAG(int, usage_reporting_test_flag_01, 101,
+ "usage_reporting_test_flag_01 help message");
+ABSL_FLAG(bool, usage_reporting_test_flag_02, false,
+ "usage_reporting_test_flag_02 help message");
+ABSL_FLAG(double, usage_reporting_test_flag_03, 1.03,
+ "usage_reporting_test_flag_03 help message");
+ABSL_FLAG(int64_t, usage_reporting_test_flag_04, 1000000000000004L,
+ "usage_reporting_test_flag_04 help message");
+
+struct UDT {
+ UDT() = default;
+ UDT(const UDT&) = default;
+};
+bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; }
+std::string AbslUnparseFlag(const UDT&) { return "UDT{}"; }
+
+ABSL_FLAG(UDT, usage_reporting_test_flag_05, {},
+ "usage_reporting_test_flag_05 help message");
+
+namespace {
+
+namespace flags = absl::flags_internal;
+
+static std::string NormalizeFileName(absl::string_view fname) {
+#ifdef _WIN32
+ std::string normalized(fname);
+ std::replace(normalized.begin(), normalized.end(), '\\', '/');
+ fname = normalized;
+#endif
+
+ auto absl_pos = fname.find("/absl/");
+ if (absl_pos != absl::string_view::npos) {
+ fname = fname.substr(absl_pos + 1);
+ }
+ return std::string(fname);
+}
+
+class UsageReportingTest : public testing::Test {
+ protected:
+ UsageReportingTest() {
+ // Install default config for the use on this unit test.
+ // Binary may install a custom config before tests are run.
+ absl::FlagsUsageConfig default_config;
+ default_config.normalize_filename = &NormalizeFileName;
+ absl::SetFlagsUsageConfig(default_config);
+ }
+
+ private:
+ flags::FlagSaver flag_saver_;
+};
+
+// --------------------------------------------------------------------
+
+using UsageReportingDeathTest = UsageReportingTest;
+
+TEST_F(UsageReportingDeathTest, TestSetProgramUsageMessage) {
+ EXPECT_EQ(flags::ProgramUsageMessage(), "Custom usage message");
+
+#ifndef _WIN32
+ // TODO(rogeeff): figure out why this does not work on Windows.
+ EXPECT_DEATH(flags::SetProgramUsageMessage("custom usage message"),
+ ".*SetProgramUsageMessage\\(\\) called twice.*");
+#endif
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_01) {
+ const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_01");
+ std::stringstream test_buf;
+
+ 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);
+ default: 101;
+)");
+}
+
+TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_02) {
+ const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_02");
+ std::stringstream test_buf;
+
+ 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);
+ default: false;
+)");
+}
+
+TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_03) {
+ const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_03");
+ std::stringstream test_buf;
+
+ 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);
+ default: 1.03;
+)");
+}
+
+TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_04) {
+ const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_04");
+ std::stringstream test_buf;
+
+ 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);
+ default: 1000000000000004;
+)");
+}
+
+TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_05) {
+ const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_05");
+ std::stringstream test_buf;
+
+ 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);
+ default: UDT{};
+)");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(UsageReportingTest, TestFlagsHelpHRF) {
+ std::string usage_test_flags_out =
+ 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);
+ default: 101;
+ -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);
+ default: 1.03;
+ -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);
+ default: UDT{};
+)";
+
+ std::stringstream test_buf_01;
+ flags::FlagsHelp(test_buf_01, "usage_test.cc",
+ flags::HelpFormat::kHumanReadable);
+ EXPECT_EQ(test_buf_01.str(), usage_test_flags_out);
+
+ std::stringstream test_buf_02;
+ flags::FlagsHelp(test_buf_02, "flags/internal/usage_test.cc",
+ flags::HelpFormat::kHumanReadable);
+ EXPECT_EQ(test_buf_02.str(), usage_test_flags_out);
+
+ std::stringstream test_buf_03;
+ flags::FlagsHelp(test_buf_03, "usage_test",
+ flags::HelpFormat::kHumanReadable);
+ EXPECT_EQ(test_buf_03.str(), usage_test_flags_out);
+
+ std::stringstream test_buf_04;
+ flags::FlagsHelp(test_buf_04, "flags/invalid_file_name.cc",
+ flags::HelpFormat::kHumanReadable);
+ EXPECT_EQ(test_buf_04.str(),
+ R"(usage_test: Custom usage message
+
+ No modules matched: use -helpfull
+)");
+
+ std::stringstream test_buf_05;
+ flags::FlagsHelp(test_buf_05, "", flags::HelpFormat::kHumanReadable);
+ std::string test_out = test_buf_05.str();
+ absl::string_view test_out_str(test_out);
+ EXPECT_TRUE(
+ absl::StartsWith(test_out_str, "usage_test: Custom usage message"));
+ EXPECT_TRUE(absl::StrContains(
+ test_out_str, "Flags from absl/flags/internal/usage_test.cc:"));
+ EXPECT_TRUE(absl::StrContains(test_out_str,
+ "Flags from absl/flags/internal/usage.cc:"));
+ EXPECT_TRUE(
+ absl::StrContains(test_out_str, "-usage_reporting_test_flag_01 "));
+ EXPECT_TRUE(absl::StrContains(test_out_str, "-help (show help"))
+ << test_out_str;
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(UsageReportingTest, TestNoUsageFlags) {
+ std::stringstream test_buf;
+ EXPECT_EQ(flags::HandleUsageFlags(test_buf), -1);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(UsageReportingTest, TestUsageFlag_helpshort) {
+ absl::SetFlag(&FLAGS_helpshort, true);
+
+ std::stringstream test_buf;
+ EXPECT_EQ(flags::HandleUsageFlags(test_buf), 1);
+ EXPECT_EQ(test_buf.str(),
+ 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);
+ default: 101;
+ -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);
+ default: 1.03;
+ -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);
+ default: UDT{};
+)");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(UsageReportingTest, TestUsageFlag_help) {
+ absl::SetFlag(&FLAGS_help, true);
+
+ std::stringstream test_buf;
+ EXPECT_EQ(flags::HandleUsageFlags(test_buf), 1);
+ EXPECT_EQ(test_buf.str(),
+ 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);
+ default: 101;
+ -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);
+ default: 1.03;
+ -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);
+ default: UDT{};
+
+Try --helpfull to get a list of all flags.
+)");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(UsageReportingTest, TestUsageFlag_helppackage) {
+ absl::SetFlag(&FLAGS_helppackage, true);
+
+ std::stringstream test_buf;
+ EXPECT_EQ(flags::HandleUsageFlags(test_buf), 1);
+ EXPECT_EQ(test_buf.str(),
+ 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);
+ default: 101;
+ -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);
+ default: 1.03;
+ -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);
+ default: UDT{};
+
+Try --helpfull to get a list of all flags.
+)");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(UsageReportingTest, TestUsageFlag_version) {
+ absl::SetFlag(&FLAGS_version, true);
+
+ std::stringstream test_buf;
+ EXPECT_EQ(flags::HandleUsageFlags(test_buf), 0);
+#ifndef NDEBUG
+ EXPECT_EQ(test_buf.str(),
+ "usage_test\nDebug build (NDEBUG not #defined)\n");
+#else
+ EXPECT_EQ(test_buf.str(), "usage_test\n");
+#endif
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(UsageReportingTest, TestUsageFlag_only_check_args) {
+ absl::SetFlag(&FLAGS_only_check_args, true);
+
+ std::stringstream test_buf;
+ EXPECT_EQ(flags::HandleUsageFlags(test_buf), 0);
+ EXPECT_EQ(test_buf.str(), "");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
+ absl::SetFlag(&FLAGS_helpon, "bla-bla");
+
+ std::stringstream test_buf_01;
+ EXPECT_EQ(flags::HandleUsageFlags(test_buf_01), 1);
+ EXPECT_EQ(test_buf_01.str(),
+ R"(usage_test: Custom usage message
+
+ No modules matched: use -helpfull
+)");
+
+ absl::SetFlag(&FLAGS_helpon, "usage_test");
+
+ std::stringstream test_buf_02;
+ EXPECT_EQ(flags::HandleUsageFlags(test_buf_02), 1);
+ EXPECT_EQ(test_buf_02.str(),
+ 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);
+ default: 101;
+ -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);
+ default: 1.03;
+ -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);
+ default: UDT{};
+)");
+}
+
+// --------------------------------------------------------------------
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ absl::GetFlag(FLAGS_undefok); // Force linking of parse.cc
+ flags::SetProgramInvocationName("usage_test");
+ flags::SetProgramUsageMessage("Custom usage message");
+ ::testing::InitGoogleTest(&argc, argv);
+
+ return RUN_ALL_TESTS();
+}