summaryrefslogtreecommitdiff
path: root/absl/flags
diff options
context:
space:
mode:
Diffstat (limited to 'absl/flags')
-rw-r--r--absl/flags/BUILD.bazel4
-rw-r--r--absl/flags/declare.h2
-rw-r--r--absl/flags/flag.cc2
-rw-r--r--absl/flags/flag.h10
-rw-r--r--absl/flags/flag_test.cc9
-rw-r--r--absl/flags/internal/commandlineflag.cc333
-rw-r--r--absl/flags/internal/commandlineflag.h187
-rw-r--r--absl/flags/internal/flag.cc337
-rw-r--r--absl/flags/internal/flag.h237
-rw-r--r--absl/flags/internal/registry.cc45
-rw-r--r--absl/flags/internal/registry.h6
11 files changed, 580 insertions, 592 deletions
diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel
index 2e0dc389..cf449198 100644
--- a/absl/flags/BUILD.bazel
+++ b/absl/flags/BUILD.bazel
@@ -40,6 +40,7 @@ cc_library(
deps = [
":handle",
":registry",
+ "//absl/base:core_headers",
"//absl/memory",
"//absl/strings",
"//absl/synchronization",
@@ -135,9 +136,6 @@ cc_library(
":config",
":marshalling",
"//absl/base:core_headers",
- "//absl/base:raw_logging_internal",
- "//absl/strings",
- "//absl/synchronization",
"//absl/types:optional",
],
)
diff --git a/absl/flags/declare.h b/absl/flags/declare.h
index 0a113a2b..4926a09e 100644
--- a/absl/flags/declare.h
+++ b/absl/flags/declare.h
@@ -39,7 +39,7 @@ class Flag;
// Flag
//
// Forward declaration of the `absl::Flag` type for use in defining the macro.
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) && !defined(__clang__)
template <typename T>
class Flag;
#else
diff --git a/absl/flags/flag.cc b/absl/flags/flag.cc
index 69038bbf..37bbce23 100644
--- a/absl/flags/flag.cc
+++ b/absl/flags/flag.cc
@@ -43,7 +43,7 @@ ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_ATOMIC_GET)
// This global nutex protects on-demand construction of flag objects in MSVC
// builds.
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) && !defined(__clang__)
namespace flags_internal {
diff --git a/absl/flags/flag.h b/absl/flags/flag.h
index 85900ef8..c0060b44 100644
--- a/absl/flags/flag.h
+++ b/absl/flags/flag.h
@@ -63,7 +63,7 @@ namespace absl {
// ABSL_FLAG(int, count, 0, "Count of items to process");
//
// No public methods of `absl::Flag<T>` are part of the Abseil Flags API.
-#if !defined(_MSC_VER)
+#if !defined(_MSC_VER) || defined(__clang__)
template <typename T>
using Flag = flags_internal::Flag<T>;
#else
@@ -119,7 +119,6 @@ class Flag {
absl::string_view Name() const { return GetImpl()->Name(); }
std::string Help() const { return GetImpl()->Help(); }
bool IsModified() const { return GetImpl()->IsModified(); }
- void SetModified(bool is_modified) { GetImpl()->SetModified(is_modified); }
bool IsSpecifiedOnCommandLine() const {
return GetImpl()->IsSpecifiedOnCommandLine();
}
@@ -127,9 +126,6 @@ class Flag {
std::string Filename() const { return GetImpl()->Filename(); }
std::string DefaultValue() const { return GetImpl()->DefaultValue(); }
std::string CurrentValue() const { return GetImpl()->CurrentValue(); }
- bool InvokeValidator(const void* value) const {
- return GetImpl()->InvokeValidator(value);
- }
template <typename T1>
inline bool IsOfType() const {
return GetImpl()->template IsOfType<T1>();
@@ -272,7 +268,7 @@ void SetFlag(absl::Flag<T>* flag, const V& v) {
#if ABSL_FLAGS_STRIP_NAMES
#define ABSL_FLAG_IMPL_FLAGNAME(txt) ""
#define ABSL_FLAG_IMPL_FILENAME() ""
-#if !defined(_MSC_VER)
+#if !defined(_MSC_VER) || defined(__clang__)
#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
absl::flags_internal::FlagRegistrar<T, false>(&flag)
#else
@@ -282,7 +278,7 @@ void SetFlag(absl::Flag<T>* flag, const V& v) {
#else
#define ABSL_FLAG_IMPL_FLAGNAME(txt) txt
#define ABSL_FLAG_IMPL_FILENAME() __FILE__
-#if !defined(_MSC_VER)
+#if !defined(_MSC_VER) || defined(__clang__)
#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
absl::flags_internal::FlagRegistrar<T, true>(&flag)
#else
diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc
index a652042c..59dc579c 100644
--- a/absl/flags/flag_test.cc
+++ b/absl/flags/flag_test.cc
@@ -42,15 +42,14 @@ void TestCallback() {}
template <typename T>
bool TestConstructionFor() {
constexpr flags::Flag<T> f1("f1", &TestHelpMsg, "file",
- &absl::flags_internal::FlagMarshallingOps<T>,
- &TestMakeDflt<T>);
+ &flags::FlagMarshallingOps<T>, &TestMakeDflt<T>);
EXPECT_EQ(f1.Name(), "f1");
EXPECT_EQ(f1.Help(), "help");
EXPECT_EQ(f1.Filename(), "file");
- ABSL_CONST_INIT static flags::Flag<T> f2(
- "f2", &TestHelpMsg, "file", &absl::flags_internal::FlagMarshallingOps<T>,
- &TestMakeDflt<T>);
+ ABSL_CONST_INIT static flags::Flag<T> f2("f2", &TestHelpMsg, "file",
+ &flags::FlagMarshallingOps<T>,
+ &TestMakeDflt<T>);
flags::FlagRegistrar<T, false>(&f2).OnUpdate(TestCallback);
EXPECT_EQ(f2.Name(), "f2");
diff --git a/absl/flags/internal/commandlineflag.cc b/absl/flags/internal/commandlineflag.cc
index 99f73611..caf33bc4 100644
--- a/absl/flags/internal/commandlineflag.cc
+++ b/absl/flags/internal/commandlineflag.cc
@@ -15,14 +15,7 @@
#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 {
@@ -35,80 +28,6 @@ namespace flags_internal {
// This is used by this file, and also in commandlineflags_reporting.cc
const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
-namespace {
-
-// Currently we only validate flag values for user-defined flag types.
-bool ShouldValidateFlagValue(const CommandLineFlag& flag) {
-#define DONT_VALIDATE(T) \
- if (flag.IsOfType<T>()) return false;
- ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(DONT_VALIDATE)
- DONT_VALIDATE(std::string)
- DONT_VALIDATE(std::vector<std::string>)
-#undef DONT_VALIDATE
-
- return true;
-}
-
-} // namespace
-
-absl::Mutex* InitFlag(CommandLineFlag* flag) {
- ABSL_CONST_INIT static absl::Mutex init_lock(absl::kConstInit);
- absl::Mutex* mu;
-
- {
- absl::MutexLock lock(&init_lock);
-
- if (flag->locks_ == nullptr) { // Must initialize Mutexes for this flag.
- flag->locks_ = new flags_internal::CommandLineFlagLocks;
- }
-
- mu = &flag->locks_->primary_mu;
- }
-
- {
- absl::MutexLock lock(mu);
-
- if (!flag->IsRetired() && flag->def_ == nullptr) {
- // Need to initialize def and cur fields.
- flag->def_ = (*flag->make_init_value_)();
- flag->cur_ = Clone(flag->op_, flag->def_);
- UpdateCopy(flag);
- flag->inited_.store(true, std::memory_order_release);
- flag->InvokeCallback();
- }
- }
-
- flag->inited_.store(true, std::memory_order_release);
- return mu;
-}
-
-// Ensure that the lazily initialized fields of *flag have been initialized,
-// and return &flag->locks_->primary_mu.
-absl::Mutex* CommandLineFlag::InitFlagIfNecessary() const
- ABSL_LOCK_RETURNED(locks_->primary_mu) {
- if (!inited_.load(std::memory_order_acquire)) {
- return InitFlag(const_cast<CommandLineFlag*>(this));
- }
-
- // All fields initialized; locks_ is therefore safe to read.
- return &locks_->primary_mu;
-}
-
-bool CommandLineFlag::IsModified() const {
- absl::MutexLock l(InitFlagIfNecessary());
- return modified_;
-}
-
-void CommandLineFlag::SetModified(bool is_modified) {
- absl::MutexLock l(InitFlagIfNecessary());
- modified_ = is_modified;
-}
-
-bool CommandLineFlag::IsSpecifiedOnCommandLine() const {
- absl::MutexLock l(InitFlagIfNecessary());
- return on_command_line_;
-}
-
absl::string_view CommandLineFlag::Typename() const {
// We do not store/report type in Abseil Flags, so that user do not rely on in
// at runtime
@@ -137,223 +56,6 @@ std::string CommandLineFlag::Filename() const {
return flags_internal::GetUsageConfig().normalize_filename(filename_);
}
-std::string CommandLineFlag::DefaultValue() const {
- absl::MutexLock l(InitFlagIfNecessary());
-
- return Unparse(marshalling_op_, def_);
-}
-
-std::string CommandLineFlag::CurrentValue() const {
- absl::MutexLock l(InitFlagIfNecessary());
-
- return Unparse(marshalling_op_, cur_);
-}
-
-int64_t CommandLineFlag::MutationCounter() const {
- absl::MutexLock l(InitFlagIfNecessary());
-
- return counter_;
-}
-
-// Attempts to parse supplied `value` string using parsing routine in the `flag`
-// argument. If parsing is successful, it will try to validate that the parsed
-// value is valid for the specified 'flag'. Finally this function stores the
-// parsed value in 'dst' assuming it is a pointer to the flag's value type. In
-// case if any error is encountered in either step, the error message is stored
-// in 'err'
-bool TryParseLocked(CommandLineFlag* flag, void* dst, absl::string_view value,
- std::string* err)
- ABSL_EXCLUSIVE_LOCKS_REQUIRED(flag->locks_->primary_mu) {
- void* tentative_value = Clone(flag->op_, flag->def_);
- std::string parse_err;
- if (!Parse(flag->marshalling_op_, value, tentative_value, &parse_err)) {
- auto type_name = flag->Typename();
- absl::string_view err_sep = parse_err.empty() ? "" : "; ";
- absl::string_view typename_sep = type_name.empty() ? "" : " ";
- *err = absl::StrCat("Illegal value '", value, "' specified for",
- typename_sep, type_name, " flag '", flag->Name(), "'",
- err_sep, parse_err);
- Delete(flag->op_, tentative_value);
- return false;
- }
-
- if (!flag->InvokeValidator(tentative_value)) {
- *err = absl::StrCat("Failed validation of new value '",
- Unparse(flag->marshalling_op_, tentative_value),
- "' for flag '", flag->Name(), "'");
- Delete(flag->op_, tentative_value);
- return false;
- }
-
- flag->counter_++;
- Copy(flag->op_, tentative_value, dst);
- Delete(flag->op_, tentative_value);
- return true;
-}
-
-// Sets the value of the flag based on specified string `value`. If the flag
-// was successfully set to new value, it returns true. Otherwise, sets `err`
-// to indicate the error, leaves the flag unchanged, and returns false. There
-// are three ways to set the flag's value:
-// * Update the current flag value
-// * Update the flag's default value
-// * Update the current flag value if it was never set before
-// The mode is selected based on 'set_mode' parameter.
-bool CommandLineFlag::SetFromString(absl::string_view value,
- FlagSettingMode set_mode,
- ValueSource source, std::string* err) {
- if (IsRetired()) return false;
-
- absl::MutexLock l(InitFlagIfNecessary());
-
- // Direct-access flags can be modified without going through the
- // flag API. Detect such changes and update the flag->modified_ bit.
- if (!IsAbseilFlag()) {
- if (!modified_ && ChangedDirectly(this, cur_, def_)) {
- modified_ = true;
- }
- }
-
- switch (set_mode) {
- case SET_FLAGS_VALUE: {
- // set or modify the flag's value
- if (!TryParseLocked(this, cur_, value, err)) return false;
- modified_ = true;
- UpdateCopy(this);
- InvokeCallback();
-
- if (source == kCommandLine) {
- on_command_line_ = true;
- }
- break;
- }
- case SET_FLAG_IF_DEFAULT: {
- // set the flag's value, but only if it hasn't been set by someone else
- if (!modified_) {
- if (!TryParseLocked(this, cur_, value, err)) return false;
- modified_ = true;
- UpdateCopy(this);
- InvokeCallback();
- } else {
- // TODO(rogeeff): review and fix this semantic. Currently we do not fail
- // in this case if flag is modified. This is misleading since the flag's
- // value is not updated even though we return true.
- // *err = absl::StrCat(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, def_, value, err)) return false;
-
- if (!modified_) {
- // Need to set both defvalue *and* current, in this case
- Copy(op_, def_, cur_);
- UpdateCopy(this);
- InvokeCallback();
- }
- break;
- }
- default: {
- // unknown set_mode
- assert(false);
- return false;
- }
- }
-
- return true;
-}
-
-void CommandLineFlag::CheckDefaultValueParsingRoundtrip() const {
- std::string v = DefaultValue();
-
- absl::MutexLock lock(InitFlagIfNecessary());
-
- void* dst = Clone(op_, def_);
- std::string error;
- if (!flags_internal::Parse(marshalling_op_, v, dst, &error)) {
- ABSL_INTERNAL_LOG(
- FATAL,
- absl::StrCat("Flag ", Name(), " (from ", Filename(),
- "): std::string form of default value '", v,
- "' could not be parsed; error=", error));
- }
-
- // We do not compare dst to def since parsing/unparsing may make
- // small changes, e.g., precision loss for floating point types.
- Delete(op_, dst);
-}
-
-bool CommandLineFlag::ValidateDefaultValue() const {
- absl::MutexLock lock(InitFlagIfNecessary());
- return InvokeValidator(def_);
-}
-
-bool CommandLineFlag::ValidateInputValue(absl::string_view value) const {
- absl::MutexLock l(InitFlagIfNecessary()); // protect default value access
-
- void* obj = Clone(op_, def_);
- std::string ignored_error;
- const bool result =
- flags_internal::Parse(marshalling_op_, value, obj, &ignored_error) &&
- InvokeValidator(obj);
- Delete(op_, obj);
- return result;
-}
-
-void CommandLineFlag::Read(void* dst,
- const flags_internal::FlagOpFn dst_op) const {
- absl::ReaderMutexLock l(InitFlagIfNecessary());
-
- // `dst_op` is the unmarshaling operation corresponding to the declaration
- // visibile at the call site. `op` is the Flag's defined unmarshalling
- // operation. They must match for this operation to be well-defined.
- if (ABSL_PREDICT_FALSE(dst_op != op_)) {
- ABSL_INTERNAL_LOG(
- ERROR,
- absl::StrCat("Flag '", Name(),
- "' is defined as one type and declared as another"));
- }
- CopyConstruct(op_, cur_, dst);
-}
-
-void CommandLineFlag::Write(const void* src,
- const flags_internal::FlagOpFn src_op) {
- absl::MutexLock l(InitFlagIfNecessary());
-
- // `src_op` is the marshalling operation corresponding to the declaration
- // visible at the call site. `op` is the Flag's defined marshalling operation.
- // They must match for this operation to be well-defined.
- if (ABSL_PREDICT_FALSE(src_op != op_)) {
- ABSL_INTERNAL_LOG(
- ERROR,
- absl::StrCat("Flag '", Name(),
- "' is defined as one type and declared as another"));
- }
-
- if (ShouldValidateFlagValue(*this)) {
- void* obj = Clone(op_, src);
- std::string ignored_error;
- std::string src_as_str = Unparse(marshalling_op_, src);
- if (!Parse(marshalling_op_, src_as_str, obj, &ignored_error) ||
- !InvokeValidator(obj)) {
- ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '", Name(),
- "' to invalid value ", src_as_str));
- }
- Delete(op_, obj);
- }
-
- modified_ = true;
- counter_++;
- Copy(op_, src, cur_);
-
- UpdateCopy(this);
- InvokeCallback();
-}
-
std::string HelpText::GetHelpText() const {
if (help_function_) return help_function_();
if (help_message_) return help_message_;
@@ -361,40 +63,5 @@ std::string HelpText::GetHelpText() const {
return {};
}
-// Update any copy of the flag value that is stored in an atomic word.
-// In addition if flag has a mutation callback this function invokes it.
-void UpdateCopy(CommandLineFlag* flag) {
-#define STORE_ATOMIC(T) \
- else if (flag->IsOfType<T>()) { \
- flag->StoreAtomic(); \
- }
-
- if (false) {
- }
- ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(STORE_ATOMIC)
-#undef STORE_ATOMIC
-}
-
-// Return true iff flag value was changed via direct-access.
-bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b) {
- if (!flag->IsAbseilFlag()) {
-// Need to compare values for direct-access flags.
-#define CHANGED_FOR_TYPE(T) \
- if (flag->IsOfType<T>()) { \
- return *reinterpret_cast<const T*>(a) != *reinterpret_cast<const T*>(b); \
- }
-
- CHANGED_FOR_TYPE(bool);
- CHANGED_FOR_TYPE(int32_t);
- CHANGED_FOR_TYPE(int64_t);
- CHANGED_FOR_TYPE(uint64_t);
- CHANGED_FOR_TYPE(double);
- CHANGED_FOR_TYPE(std::string);
-#undef CHANGED_FOR_TYPE
- }
-
- return false;
-}
-
} // namespace flags_internal
} // namespace absl
diff --git a/absl/flags/internal/commandlineflag.h b/absl/flags/internal/commandlineflag.h
index 528d3106..13a3352e 100644
--- a/absl/flags/internal/commandlineflag.h
+++ b/absl/flags/internal/commandlineflag.h
@@ -16,12 +16,10 @@
#ifndef ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
#define ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
-#include <atomic>
#include <memory>
#include "absl/base/macros.h"
#include "absl/flags/marshalling.h"
-#include "absl/synchronization/mutex.h"
#include "absl/types/optional.h"
namespace absl {
@@ -151,14 +149,6 @@ inline size_t Sizeof(FlagOpFn op) {
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*,
@@ -200,25 +190,9 @@ class FlagStateInterface {
// Holds all information for a flag.
class CommandLineFlag {
public:
- constexpr CommandLineFlag(
- const char* name, HelpText help_text, const char* filename,
- const flags_internal::FlagOpFn op,
- const flags_internal::FlagMarshallingOpFn marshalling_op,
- const flags_internal::InitialValGenFunc initial_value_gen, void* def,
- void* cur)
- : name_(name),
- help_(help_text),
- filename_(filename),
- op_(op),
- marshalling_op_(marshalling_op),
- make_init_value_(initial_value_gen),
- inited_(false),
- modified_(false),
- on_command_line_(false),
- def_(def),
- cur_(cur),
- counter_(0),
- locks_(nullptr) {}
+ constexpr CommandLineFlag(const char* name, HelpText help_text,
+ const char* filename)
+ : name_(name), help_(help_text), filename_(filename) {}
// Virtual destructor
virtual void Destroy() const = 0;
@@ -227,60 +201,73 @@ class CommandLineFlag {
CommandLineFlag(const CommandLineFlag&) = delete;
CommandLineFlag& operator=(const CommandLineFlag&) = delete;
- // Access methods.
-
- // Returns true iff this object corresponds to retired flag
- virtual bool IsRetired() const { return false; }
- // Returns true iff this is a handle to an Abseil Flag.
- virtual bool IsAbseilFlag() const { return true; }
-
+ // Non-polymorphic access methods.
absl::string_view Name() const { return name_; }
std::string Help() const { return help_.GetHelpText(); }
- bool IsModified() const;
- void SetModified(bool is_modified);
- bool IsSpecifiedOnCommandLine() const;
-
absl::string_view Typename() const;
std::string Filename() const;
- std::string DefaultValue() const;
- std::string CurrentValue() const;
-
- // Interface to store the value in atomic if one used. This is short term
- // interface. To be reworked once cur_ is moved.
- virtual void StoreAtomic() {}
-
- // Interfaces to operate on validators.
- virtual bool InvokeValidator(const void* /*value*/) const { return true; }
- // Invoke the flag validators for old flags.
- // TODO(rogeeff): implement proper validators for Abseil Flags
- bool ValidateDefaultValue() const;
- bool ValidateInputValue(absl::string_view value) const;
// Return true iff flag has type T.
template <typename T>
inline bool IsOfType() const {
- return op_ == &flags_internal::FlagOps<T>;
+ return TypeId() == &flags_internal::FlagOps<T>;
}
// Attempts to retrieve the flag value. Returns value on success,
// absl::nullopt otherwise.
template <typename T>
absl::optional<T> Get() const {
- if (IsRetired() || flags_internal::FlagOps<T> != op_) return absl::nullopt;
-
- T res;
- Read(&res, flags_internal::FlagOps<T>);
+ if (IsRetired() || !IsOfType<T>()) {
+ return absl::nullopt;
+ }
- return res;
+ // Implementation notes:
+ //
+ // We are wrapping a union around the value of `T` to serve three purposes:
+ //
+ // 1. `U.value` has correct size and alignment for a value of type `T`
+ // 2. The `U.value` constructor is not invoked since U's constructor does
+ // not
+ // do it explicitly.
+ // 3. The `U.value` destructor is invoked since U's destructor does it
+ // explicitly. This makes `U` a kind of RAII wrapper around non default
+ // constructible value of T, which is destructed when we leave the
+ // scope. We do need to destroy U.value, which is constructed by
+ // CommandLineFlag::Read even though we left it in a moved-from state
+ // after std::move.
+ //
+ // All of this serves to avoid requiring `T` being default constructible.
+ union U {
+ T value;
+ U() {}
+ ~U() { value.~T(); }
+ };
+ U u;
+
+ Read(&u.value);
+ return std::move(u.value);
}
+ // Polymorphic access methods
+
+ // Returns true iff this object corresponds to retired flag
+ virtual bool IsRetired() const { return false; }
+ // Returns true iff this is a handle to an Abseil Flag.
+ virtual bool IsAbseilFlag() const { return true; }
+ // Returns id of the flag's value type.
+ virtual flags_internal::FlagOpFn TypeId() const = 0;
+ virtual bool IsModified() const = 0;
+ virtual bool IsSpecifiedOnCommandLine() const = 0;
+ virtual std::string DefaultValue() const = 0;
+ virtual std::string CurrentValue() const = 0;
+
+ // Interfaces to operate on validators.
+ virtual bool ValidateInputValue(absl::string_view value) const = 0;
+
// Interface to save flag to some persistent state. Returns current flag state
// or nullptr if flag does not support saving and restoring a state.
virtual std::unique_ptr<FlagStateInterface> SaveState() = 0;
- // Interfaces to overate on callbacks.
- virtual void InvokeCallback() {}
-
// Sets the value of the flag based on specified std::string `value`. If the flag
// was successfully set to new value, it returns true. Otherwise, sets `error`
// to indicate the error, leaves the flag unchanged, and returns false. There
@@ -289,74 +276,28 @@ class CommandLineFlag {
// * Update the flag's default value
// * Update the current flag value if it was never set before
// The mode is selected based on `set_mode` parameter.
- bool SetFromString(absl::string_view value,
- flags_internal::FlagSettingMode set_mode,
- flags_internal::ValueSource source, std::string* error);
+ virtual bool SetFromString(absl::string_view value,
+ flags_internal::FlagSettingMode set_mode,
+ flags_internal::ValueSource source,
+ std::string* error) = 0;
- void CheckDefaultValueParsingRoundtrip() const;
+ // Checks that flags default value can be converted to std::string and back to the
+ // flag's value type.
+ virtual void CheckDefaultValueParsingRoundtrip() const = 0;
// Constant configuration for a particular flag.
protected:
~CommandLineFlag() = default;
- // Thread safe access to mutation counter.
- int64_t MutationCounter() const;
-
- const char* const name_;
- const HelpText help_;
- const char* const filename_;
-
- const FlagOpFn op_; // Type-specific handler
- const FlagMarshallingOpFn marshalling_op_; // Marshalling ops handler
- const InitialValGenFunc make_init_value_; // Makes initial value for the flag
- 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.
- void* def_; // Lazily initialized pointer to default value
- void* cur_; // Lazily initialized pointer to current value
- int64_t counter_; // Mutation counter
-
- // Lazily initialized mutexes for this flag value. We cannot inline a
- // SpinLock or Mutex here because those have non-constexpr constructors and
- // so would prevent constant initialization of this type.
- // TODO(rogeeff): fix it once Mutex has constexpr constructor
- struct CommandLineFlagLocks* locks_; // locks, laziliy allocated.
-
- // Ensure that the lazily initialized fields of *flag have been initialized,
- // and return the lock which should be locked when flag's state is mutated.
- absl::Mutex* InitFlagIfNecessary() const ABSL_LOCK_RETURNED(locks_->primary_mu);
-
- // copy construct new value of flag's type in a memory referenced by dst
- // based on current flag's value
- void Read(void* dst, const flags_internal::FlagOpFn dst_op) const;
- // updates flag's value to *src (locked)
- void Write(const void* src, const flags_internal::FlagOpFn src_op);
-
- friend class FlagRegistry;
- friend class FlagPtrMap;
- friend class FlagSaverImpl;
- friend bool TryParseLocked(CommandLineFlag* flag, void* dst,
- absl::string_view value, std::string* err);
- friend absl::Mutex* InitFlag(CommandLineFlag* flag);
-};
+ const char* const name_; // Flags name passed to ABSL_FLAG as second arg.
+ const HelpText help_; // The function generating help message.
+ const char* const filename_; // The file name where ABSL_FLAG resides.
-// Update any copy of the flag value that is stored in an atomic word.
-// In addition if flag has a mutation callback this function invokes it. While
-// callback is being invoked the primary flag's mutex is unlocked and it is
-// re-locked back after call to callback is completed. Callback invocation is
-// guarded by flag's secondary mutex instead which prevents concurrent callback
-// invocation. Note that it is possible for other thread to grab the primary
-// lock and update flag's value at any time during the callback invocation.
-// This is by design. Callback can get a value of the flag if necessary, but it
-// might be different from the value initiated the callback and it also can be
-// different by the time the callback invocation is completed.
-// Requires that *primary_lock be held in exclusive mode; it may be released
-// and reacquired by the implementation.
-void UpdateCopy(CommandLineFlag* flag);
-// Return true iff flag value was changed via direct-access.
-bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b);
+ private:
+ // Copy-construct a new value of the flag's type in a memory referenced by
+ // the dst based on the current flag's value.
+ virtual void Read(void* dst) const = 0;
+};
// This macro is the "source of truth" for the list of supported flag types we
// expect to perform lock free operations on. Specifically it generates code,
diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc
index 0f403581..061113d7 100644
--- a/absl/flags/internal/flag.cc
+++ b/absl/flags/internal/flag.cc
@@ -15,36 +15,331 @@
#include "absl/flags/internal/flag.h"
+#include "absl/base/optimization.h"
#include "absl/synchronization/mutex.h"
namespace absl {
namespace flags_internal {
+namespace {
-// 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(absl::Mutex* primary_mu, absl::Mutex* callback_mu,
- FlagCallback cb) ABSL_EXCLUSIVE_LOCKS_REQUIRED(primary_mu) {
- if (!cb) return;
-
- // When executing the callback we need the primary flag's mutex to be
- // unlocked so that callback can retrieve the flag's value.
- primary_mu->Unlock();
+// 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
+
+void FlagImpl::Init() {
+ ABSL_CONST_INIT static absl::Mutex init_lock(absl::kConstInit);
+
+ {
+ absl::MutexLock lock(&init_lock);
+
+ if (locks_ == nullptr) { // Must initialize Mutexes for this flag.
+ locks_ = new FlagImpl::CommandLineFlagLocks;
+ }
+ }
+
+ absl::MutexLock lock(&locks_->primary_mu);
+
+ if (def_ != nullptr) {
+ inited_.store(true, std::memory_order_release);
+ } else {
+ // Need to initialize def and cur fields.
+ def_ = (*initial_value_gen_)();
+ cur_ = Clone(op_, def_);
+ StoreAtomic();
+ inited_.store(true, std::memory_order_release);
+ InvokeCallback();
+ }
+}
+
+// Ensures that the lazily initialized data is initialized,
+// and returns pointer to the mutex guarding flags data.
+absl::Mutex* FlagImpl::DataGuard() const
+ ABSL_LOCK_RETURNED(locks_->primary_mu) {
+ if (ABSL_PREDICT_FALSE(!inited_.load(std::memory_order_acquire))) {
+ const_cast<FlagImpl*>(this)->Init();
+ }
+
+ // All fields initialized; locks_ is therefore safe to read.
+ return &locks_->primary_mu;
+}
+
+void FlagImpl::Destroy() const {
{
- absl::MutexLock lock(callback_mu);
- cb();
+ absl::MutexLock l(DataGuard());
+
+ // Values are heap allocated for Abseil Flags.
+ if (cur_) Delete(op_, cur_);
+ if (def_) Delete(op_, def_);
}
- primary_mu->Lock();
+ delete locks_;
+}
+
+bool FlagImpl::IsModified() const {
+ absl::MutexLock l(DataGuard());
+ return modified_;
+}
+
+bool FlagImpl::IsSpecifiedOnCommandLine() const {
+ absl::MutexLock l(DataGuard());
+ return on_command_line_;
+}
+
+std::string FlagImpl::DefaultValue() const {
+ absl::MutexLock l(DataGuard());
+
+ return Unparse(marshalling_op_, def_);
+}
+
+std::string FlagImpl::CurrentValue() const {
+ absl::MutexLock l(DataGuard());
+
+ return Unparse(marshalling_op_, cur_);
+}
+
+void FlagImpl::SetCallback(
+ const flags_internal::FlagCallback mutation_callback) {
+ absl::MutexLock l(DataGuard());
+
+ callback_ = mutation_callback;
+
+ InvokeCallback();
+}
+
+void FlagImpl::InvokeCallback() const
+ ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) {
+ if (!callback_) return;
+
+ // 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.
+ DataGuard()->Unlock();
+
+ {
+ absl::MutexLock lock(&locks_->callback_mu);
+ callback_();
+ }
+
+ DataGuard()->Lock();
+}
+
+bool FlagImpl::RestoreState(const CommandLineFlag& flag, const void* value,
+ bool modified, bool on_command_line,
+ int64_t counter) {
+ {
+ absl::MutexLock l(DataGuard());
+
+ if (counter_ == counter) return false;
+ }
+
+ Write(flag, value, op_);
+
+ {
+ absl::MutexLock l(DataGuard());
+
+ modified_ = modified;
+ on_command_line_ = on_command_line;
+ }
+
+ return true;
+}
+
+// Attempts to parse supplied `value` string using parsing routine in the `flag`
+// argument. If parsing successful, this function stores the parsed value in
+// 'dst' assuming it is a pointer to the flag's value type. In case if any error
+// is encountered in either step, the error message is stored in 'err'
+bool FlagImpl::TryParse(const CommandLineFlag& flag, void* dst,
+ absl::string_view value, std::string* err) const
+ ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) {
+ void* tentative_value = Clone(op_, def_);
+ std::string parse_err;
+ if (!Parse(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(op_, tentative_value);
+ return false;
+ }
+
+ Copy(op_, tentative_value, dst);
+ Delete(op_, tentative_value);
+ return true;
+}
+
+void FlagImpl::Read(const CommandLineFlag& flag, void* dst,
+ const flags_internal::FlagOpFn dst_op) const {
+ absl::ReaderMutexLock l(DataGuard());
+
+ // `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 '", flag.Name(),
+ "' is defined as one type and declared as another"));
+ }
+ CopyConstruct(op_, cur_, dst);
+}
+
+void FlagImpl::StoreAtomic() ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) {
+ size_t data_size = Sizeof(op_);
+
+ if (data_size <= sizeof(int64_t)) {
+ int64_t t = 0;
+ std::memcpy(&t, cur_, data_size);
+ atomic_.store(t, std::memory_order_release);
+ }
+}
+
+void FlagImpl::Write(const CommandLineFlag& flag, const void* src,
+ const flags_internal::FlagOpFn src_op) {
+ absl::MutexLock l(DataGuard());
+
+ // `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 '", flag.Name(),
+ "' is defined as one type and declared as another"));
+ }
+
+ if (ShouldValidateFlagValue(flag)) {
+ 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)) {
+ ABSL_INTERNAL_LOG(ERROR,
+ absl::StrCat("Attempt to set flag '", flag.Name(),
+ "' to invalid value ", src_as_str));
+ }
+ Delete(op_, obj);
+ }
+
+ modified_ = true;
+ counter_++;
+ Copy(op_, src, cur_);
+
+ StoreAtomic();
+ InvokeCallback();
+}
+
+// Sets the value of the flag based on specified string `value`. If the flag
+// was successfully set to new value, it returns true. Otherwise, sets `err`
+// to indicate the error, leaves the flag unchanged, and returns false. There
+// are three ways to set the flag's value:
+// * Update the current flag value
+// * Update the flag's default value
+// * Update the current flag value if it was never set before
+// The mode is selected based on 'set_mode' parameter.
+bool FlagImpl::SetFromString(const CommandLineFlag& flag,
+ absl::string_view value, FlagSettingMode set_mode,
+ ValueSource source, std::string* err) {
+ absl::MutexLock l(DataGuard());
+
+ switch (set_mode) {
+ case SET_FLAGS_VALUE: {
+ // set or modify the flag's value
+ if (!TryParse(flag, cur_, value, err)) return false;
+ modified_ = true;
+ counter_++;
+ StoreAtomic();
+ InvokeCallback();
+
+ if (source == kCommandLine) {
+ on_command_line_ = true;
+ }
+ break;
+ }
+ case SET_FLAG_IF_DEFAULT: {
+ // set the flag's value, but only if it hasn't been set by someone else
+ if (!modified_) {
+ if (!TryParse(flag, cur_, value, err)) return false;
+ modified_ = true;
+ counter_++;
+ StoreAtomic();
+ InvokeCallback();
+ } else {
+ // TODO(rogeeff): review and fix this semantic. Currently we do not fail
+ // in this case if flag is modified. This is misleading since the flag's
+ // value is not updated even though we return true.
+ // *err = absl::StrCat(Name(), " is already set to ",
+ // CurrentValue(), "\n");
+ // return false;
+ return true;
+ }
+ break;
+ }
+ case SET_FLAGS_DEFAULT: {
+ // modify the flag's default-value
+ if (!TryParse(flag, def_, value, err)) return false;
+
+ if (!modified_) {
+ // Need to set both default value *and* current, in this case
+ Copy(op_, def_, cur_);
+ StoreAtomic();
+ InvokeCallback();
+ }
+ break;
+ }
+ }
+
+ return true;
+}
+
+void FlagImpl::CheckDefaultValueParsingRoundtrip(
+ const CommandLineFlag& flag) const {
+ std::string v = DefaultValue();
+
+ absl::MutexLock lock(DataGuard());
+
+ void* dst = Clone(op_, def_);
+ std::string error;
+ if (!flags_internal::Parse(marshalling_op_, v, dst, &error)) {
+ ABSL_INTERNAL_LOG(
+ FATAL,
+ absl::StrCat("Flag ", flag.Name(), " (from ", flag.Filename(),
+ "): std::string form of default value '", v,
+ "' could not be parsed; error=", error));
+ }
+
+ // We do not compare dst to def since parsing/unparsing may make
+ // small changes, e.g., precision loss for floating point types.
+ Delete(op_, dst);
+}
+
+bool FlagImpl::ValidateInputValue(absl::string_view value) const {
+ absl::MutexLock l(DataGuard());
+
+ void* obj = Clone(op_, def_);
+ std::string ignored_error;
+ const bool result =
+ flags_internal::Parse(marshalling_op_, value, obj, &ignored_error);
+ Delete(op_, obj);
+ return result;
}
} // namespace flags_internal
diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h
index 2b21c440..ce0ccf2d 100644
--- a/absl/flags/internal/flag.h
+++ b/absl/flags/internal/flag.h
@@ -16,12 +16,15 @@
#ifndef ABSL_FLAGS_INTERNAL_FLAG_H_
#define ABSL_FLAGS_INTERNAL_FLAG_H_
+#include <atomic>
#include <cstring>
+#include "absl/base/thread_annotations.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/registry.h"
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
+#include "absl/synchronization/mutex.h"
namespace absl {
namespace flags_internal {
@@ -66,6 +69,124 @@ using FlagCallback = void (*)();
void InvokeCallback(absl::Mutex* primary_mu, absl::Mutex* callback_mu,
FlagCallback cb) ABSL_EXCLUSIVE_LOCKS_REQUIRED(primary_mu);
+// The class encapsulates the Flag's data and safe access to it.
+class FlagImpl {
+ public:
+ constexpr FlagImpl(const flags_internal::FlagOpFn op,
+ const flags_internal::FlagMarshallingOpFn marshalling_op,
+ const flags_internal::InitialValGenFunc initial_value_gen)
+ : op_(op),
+ marshalling_op_(marshalling_op),
+ initial_value_gen_(initial_value_gen) {}
+
+ // Forces destruction of the Flag's data.
+ void Destroy() const;
+
+ // Constant access methods
+ bool IsModified() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu);
+ bool IsSpecifiedOnCommandLine() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu);
+ std::string DefaultValue() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu);
+ std::string CurrentValue() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu);
+ void Read(const CommandLineFlag& flag, void* dst,
+ const flags_internal::FlagOpFn dst_op) const
+ ABSL_LOCKS_EXCLUDED(locks_->primary_mu);
+ // Attempts to parse supplied `value` std::string.
+ bool TryParse(const CommandLineFlag& flag, void* dst, absl::string_view value,
+ std::string* err) const
+ ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu);
+ template <typename T>
+ bool AtomicGet(T* v) const {
+ const int64_t r = atomic_.load(std::memory_order_acquire);
+ if (r != flags_internal::AtomicInit()) {
+ std::memcpy(v, &r, sizeof(T));
+ return true;
+ }
+
+ return false;
+ }
+
+ // Mutating access methods
+ void Write(const CommandLineFlag& flag, const void* src,
+ const flags_internal::FlagOpFn src_op)
+ ABSL_LOCKS_EXCLUDED(locks_->primary_mu);
+ bool SetFromString(const CommandLineFlag& flag, absl::string_view value,
+ FlagSettingMode set_mode, ValueSource source,
+ std::string* err) ABSL_LOCKS_EXCLUDED(locks_->primary_mu);
+ // If possible, updates copy of the Flag's value that is stored in an
+ // atomic word.
+ void StoreAtomic() ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu);
+
+ // Interfaces to operate on callbacks.
+ void SetCallback(const flags_internal::FlagCallback mutation_callback)
+ ABSL_LOCKS_EXCLUDED(locks_->primary_mu);
+ void InvokeCallback() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu);
+
+ // Interfaces to save/restore mutable flag data
+ template <typename T>
+ std::unique_ptr<flags_internal::FlagStateInterface> SaveState(
+ Flag<T>* flag) const ABSL_LOCKS_EXCLUDED(locks_->primary_mu) {
+ T&& cur_value = flag->Get();
+ absl::MutexLock l(DataGuard());
+
+ return absl::make_unique<flags_internal::FlagState<T>>(
+ flag, std::move(cur_value), modified_, on_command_line_, counter_);
+ }
+ bool RestoreState(const CommandLineFlag& flag, const void* value,
+ bool modified, bool on_command_line, int64_t counter)
+ ABSL_LOCKS_EXCLUDED(locks_->primary_mu);
+
+ // Value validation interfaces.
+ void CheckDefaultValueParsingRoundtrip(const CommandLineFlag& flag) const
+ ABSL_LOCKS_EXCLUDED(locks_->primary_mu);
+ bool ValidateInputValue(absl::string_view value) const
+ ABSL_LOCKS_EXCLUDED(locks_->primary_mu);
+
+ private:
+ // Lazy initialization of the Flag's data.
+ void Init();
+ // Ensures that the lazily initialized data is initialized,
+ // and returns pointer to the mutex guarding flags data.
+ absl::Mutex* DataGuard() const ABSL_LOCK_RETURNED(locks_->primary_mu);
+
+ // Immutable Flag's data.
+ const FlagOpFn op_; // Type-specific handler
+ const FlagMarshallingOpFn marshalling_op_; // Marshalling ops handler
+ const InitialValGenFunc initial_value_gen_; // Makes flag's initial value
+
+ // Mutable Flag's data. (guarded by locks_->primary_mu).
+ // Indicates that locks_, cur_ and def_ fields have been lazily initialized.
+ std::atomic<bool> inited_{false};
+ // Has flag value been modified?
+ bool modified_ ABSL_GUARDED_BY(locks_->primary_mu) = false;
+ // Specified on command line.
+ bool on_command_line_ ABSL_GUARDED_BY(locks_->primary_mu) = false;
+ // Lazily initialized pointer to default value
+ void* def_ ABSL_GUARDED_BY(locks_->primary_mu) = nullptr;
+ // Lazily initialized pointer to current value
+ void* cur_ ABSL_GUARDED_BY(locks_->primary_mu) = nullptr;
+ // Mutation counter
+ int64_t counter_ ABSL_GUARDED_BY(locks_->primary_mu) = 0;
+ // For some types, a copy of the current value is kept in an atomically
+ // accessible field.
+ std::atomic<int64_t> atomic_{flags_internal::AtomicInit()};
+ // Mutation callback
+ FlagCallback callback_ = nullptr;
+
+ // 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
+ // 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
+ };
+
+ CommandLineFlagLocks* locks_ = nullptr; // locks, laziliy allocated.
+};
+
// This is "unspecified" implementation of absl::Flag<T> type.
template <typename T>
class Flag final : public flags_internal::CommandLineFlag {
@@ -76,30 +197,11 @@ class Flag final : public flags_internal::CommandLineFlag {
const flags_internal::InitialValGenFunc initial_value_gen)
: flags_internal::CommandLineFlag(
name, flags_internal::HelpText::FromFunctionPointer(help_gen),
- filename, &flags_internal::FlagOps<T>, marshalling_op,
- initial_value_gen,
- /*def=*/nullptr,
- /*cur=*/nullptr),
- atomic_(flags_internal::AtomicInit()),
- callback_(nullptr) {}
+ filename),
+ impl_(&flags_internal::FlagOps<T>, marshalling_op, initial_value_gen) {}
T Get() const {
- // Implementation notes:
- //
- // We are wrapping a union around the value of `T` to serve three purposes:
- //
- // 1. `U.value` has correct size and alignment for a value of type `T`
- // 2. The `U.value` constructor is not invoked since U's constructor does
- // not
- // do it explicitly.
- // 3. The `U.value` destructor is invoked since U's destructor does it
- // explicitly. This makes `U` a kind of RAII wrapper around non default
- // constructible value of T, which is destructed when we leave the
- // scope. We do need to destroy U.value, which is constructed by
- // CommandLineFlag::Read even though we left it in a moved-from state
- // after std::move.
- //
- // All of this serves to avoid requiring `T` being default constructible.
+ // See implementation notes in CommandLineFlag::Get().
union U {
T value;
U() {}
@@ -107,89 +209,70 @@ class Flag final : public flags_internal::CommandLineFlag {
};
U u;
- Read(&u.value, &flags_internal::FlagOps<T>);
+ impl_.Read(*this, &u.value, &flags_internal::FlagOps<T>);
return std::move(u.value);
}
- bool AtomicGet(T* v) const {
- const int64_t r = atomic_.load(std::memory_order_acquire);
- if (r != flags_internal::AtomicInit()) {
- std::memcpy(v, &r, sizeof(T));
- return true;
- }
-
- return false;
- }
+ bool AtomicGet(T* v) const { return impl_.AtomicGet(v); }
- void Set(const T& v) { Write(&v, &flags_internal::FlagOps<T>); }
+ void Set(const T& v) { impl_.Write(*this, &v, &flags_internal::FlagOps<T>); }
void SetCallback(const flags_internal::FlagCallback mutation_callback) {
- absl::MutexLock l(InitFlagIfNecessary());
-
- callback_ = mutation_callback;
-
- InvokeCallback();
+ impl_.SetCallback(mutation_callback);
}
- private:
- friend class FlagState<T>;
-
- void Destroy() const override {
- // Values are heap allocated for Abseil Flags.
- if (cur_) Delete(op_, cur_);
- if (def_) Delete(op_, def_);
-
- delete locks_;
+ // CommandLineFlag interface
+ bool IsModified() const override { return impl_.IsModified(); }
+ bool IsSpecifiedOnCommandLine() const override {
+ return impl_.IsSpecifiedOnCommandLine();
}
+ std::string DefaultValue() const override { return impl_.DefaultValue(); }
+ std::string CurrentValue() const override { return impl_.CurrentValue(); }
- void StoreAtomic() override {
- if (sizeof(T) <= sizeof(int64_t)) {
- int64_t t = 0;
- std::memcpy(&t, cur_, (std::min)(sizeof(T), sizeof(int64_t)));
- atomic_.store(t, std::memory_order_release);
- }
+ bool ValidateInputValue(absl::string_view value) const override {
+ return impl_.ValidateInputValue(value);
}
// Interfaces to save and restore flags to/from persistent state.
// Returns current flag state or nullptr if flag does not support
// saving and restoring a state.
std::unique_ptr<flags_internal::FlagStateInterface> SaveState() override {
- T curr_value = Get();
-
- absl::MutexLock l(InitFlagIfNecessary());
-
- return absl::make_unique<flags_internal::FlagState<T>>(
- this, std::move(curr_value), modified_, on_command_line_, counter_);
+ return impl_.SaveState(this);
}
// Restores the flag state to the supplied state object. If there is
// nothing to restore returns false. Otherwise returns true.
bool RestoreState(const flags_internal::FlagState<T>& flag_state) {
- if (MutationCounter() == flag_state.counter_) return false;
-
- Set(flag_state.cur_value_);
+ return impl_.RestoreState(*this, &flag_state.cur_value_,
+ flag_state.modified_, flag_state.on_command_line_,
+ flag_state.counter_);
+ }
- // Race condition here? This should disappear once we move the rest of the
- // flag's data into Flag's internals.
+ bool SetFromString(absl::string_view value,
+ flags_internal::FlagSettingMode set_mode,
+ flags_internal::ValueSource source,
+ std::string* error) override {
+ return impl_.SetFromString(*this, value, set_mode, source, error);
+ }
- absl::MutexLock l(InitFlagIfNecessary());
- modified_ = flag_state.modified_;
- on_command_line_ = flag_state.on_command_line_;
- return true;
+ void CheckDefaultValueParsingRoundtrip() const override {
+ impl_.CheckDefaultValueParsingRoundtrip(*this);
}
- // Interfaces to overate on callbacks.
- void InvokeCallback() override
- ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) {
- flags_internal::InvokeCallback(&locks_->primary_mu, &locks_->callback_mu,
- callback_);
+ private:
+ friend class FlagState<T>;
+
+ void Destroy() const override { impl_.Destroy(); }
+
+ void Read(void* dst) const override {
+ impl_.Read(*this, dst, &flags_internal::FlagOps<T>);
+ }
+ flags_internal::FlagOpFn TypeId() const override {
+ return &flags_internal::FlagOps<T>;
}
// Flag's data
- // For some types, a copy of the current value is kept in an atomically
- // accessible field.
- std::atomic<int64_t> atomic_;
- FlagCallback callback_; // Mutation callback
+ FlagImpl impl_;
};
template <typename T>
diff --git a/absl/flags/internal/registry.cc b/absl/flags/internal/registry.cc
index 6b2564d1..ae7671a9 100644
--- a/absl/flags/internal/registry.cc
+++ b/absl/flags/internal/registry.cc
@@ -118,7 +118,7 @@ void FlagRegistry::RegisterFlag(CommandLineFlag* flag) {
(flag->IsRetired() ? old_flag->Filename() : flag->Filename()),
"'."),
true);
- } else if (flag->op_ != old_flag->op_) {
+ } else if (flag->TypeId() != old_flag->TypeId()) {
flags_internal::ReportUsageError(
absl::StrCat("Flag '", flag->Name(),
"' was defined more than once but with "
@@ -275,38 +275,49 @@ namespace {
class RetiredFlagObj final : public flags_internal::CommandLineFlag {
public:
- constexpr RetiredFlagObj(const char* name, FlagOpFn ops,
- FlagMarshallingOpFn marshalling_ops)
+ constexpr RetiredFlagObj(const char* name, FlagOpFn ops)
: flags_internal::CommandLineFlag(
name, flags_internal::HelpText::FromStaticCString(nullptr),
- /*filename=*/"RETIRED", ops, marshalling_ops,
- /*initial_value_gen=*/nullptr,
- /*def=*/nullptr,
- /*cur=*/nullptr) {}
+ /*filename=*/"RETIRED"),
+ op_(ops) {}
private:
- bool IsRetired() const override { return true; }
-
void Destroy() const override {
// Values are heap allocated for Retired Flags.
- if (cur_) Delete(op_, cur_);
- if (def_) Delete(op_, def_);
-
- if (locks_) delete locks_;
-
delete this;
}
+ flags_internal::FlagOpFn TypeId() const override { return op_; }
+ bool IsRetired() const override { return true; }
+ bool IsModified() const override { return false; }
+ bool IsSpecifiedOnCommandLine() const override { return false; }
+ std::string DefaultValue() const override { return ""; }
+ std::string CurrentValue() const override { return ""; }
+
+ // Any input is valid
+ bool ValidateInputValue(absl::string_view) const override { return true; }
+
std::unique_ptr<flags_internal::FlagStateInterface> SaveState() override {
return nullptr;
}
+
+ bool SetFromString(absl::string_view, flags_internal::FlagSettingMode,
+ flags_internal::ValueSource, std::string*) override {
+ return false;
+ }
+
+ void CheckDefaultValueParsingRoundtrip() const override {}
+
+ void Read(void*) const override {}
+
+ // Data members
+ const FlagOpFn op_;
};
} // namespace
-bool Retire(const char* name, FlagOpFn ops,
- FlagMarshallingOpFn marshalling_ops) {
- auto* flag = new flags_internal::RetiredFlagObj(name, ops, marshalling_ops);
+bool Retire(const char* name, FlagOpFn ops) {
+ auto* flag = new flags_internal::RetiredFlagObj(name, ops);
FlagRegistry::GlobalRegistry()->RegisterFlag(flag);
return true;
}
diff --git a/absl/flags/internal/registry.h b/absl/flags/internal/registry.h
index eb134a9f..1889f3a0 100644
--- a/absl/flags/internal/registry.h
+++ b/absl/flags/internal/registry.h
@@ -76,14 +76,12 @@ bool RegisterCommandLineFlag(CommandLineFlag*);
//
// Retire flag with name "name" and type indicated by ops.
-bool Retire(const char* name, FlagOpFn ops,
- FlagMarshallingOpFn marshalling_ops);
+bool Retire(const char* name, FlagOpFn ops);
// 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(flag_name, flags_internal::FlagOps<T>,
- flags_internal::FlagMarshallingOps<T>);
+ return flags_internal::Retire(flag_name, flags_internal::FlagOps<T>);
}
// If the flag is retired, returns true and indicates in |*type_is_bool|