Export of internal Abseil changes.
-- ed3a3431eee9e48e6553b0320e0308d2dde6725c by Derek Mauro <dmauro@google.com>: Project import generated by Copybara. PiperOrigin-RevId: 258631680 GitOrigin-RevId: ed3a3431eee9e48e6553b0320e0308d2dde6725c Change-Id: I1d7ae86a79783842092d29504605ba039c369603
This commit is contained in:
parent
44efe96dfc
commit
c6c3c1b498
32 changed files with 1168 additions and 657 deletions
|
|
@ -37,44 +37,6 @@ const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
|
|||
|
||||
namespace {
|
||||
|
||||
void StoreAtomic(CommandLineFlag* flag, const void* data, size_t size) {
|
||||
int64_t t = 0;
|
||||
assert(size <= sizeof(int64_t));
|
||||
memcpy(&t, data, size);
|
||||
flag->atomic.store(t, std::memory_order_release);
|
||||
}
|
||||
|
||||
// If the flag has a mutation callback this function invokes it. While the
|
||||
// callback is being invoked the primary flag's mutex is unlocked and it is
|
||||
// re-locked back after call to callback is completed. Callback invocation is
|
||||
// guarded by flag's secondary mutex instead which prevents concurrent callback
|
||||
// invocation. Note that it is possible for other thread to grab the primary
|
||||
// lock and update flag's value at any time during the callback invocation.
|
||||
// This is by design. Callback can get a value of the flag if necessary, but it
|
||||
// might be different from the value initiated the callback and it also can be
|
||||
// different by the time the callback invocation is completed.
|
||||
// Requires that *primary_lock be held in exclusive mode; it may be released
|
||||
// and reacquired by the implementation.
|
||||
void InvokeCallback(CommandLineFlag* flag, absl::Mutex* primary_lock)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(primary_lock) {
|
||||
if (!flag->callback) return;
|
||||
|
||||
// The callback lock is guaranteed initialized, because *primary_lock exists.
|
||||
absl::Mutex* callback_mu = &flag->locks->callback_mu;
|
||||
|
||||
// When executing the callback we need the primary flag's mutex to be unlocked
|
||||
// so that callback can retrieve the flag's value.
|
||||
primary_lock->Unlock();
|
||||
|
||||
{
|
||||
absl::MutexLock lock(callback_mu);
|
||||
|
||||
flag->callback();
|
||||
}
|
||||
|
||||
primary_lock->Lock();
|
||||
}
|
||||
|
||||
// Currently we only validate flag values for user-defined flag types.
|
||||
bool ShouldValidateFlagValue(const CommandLineFlag& flag) {
|
||||
#define DONT_VALIDATE(T) \
|
||||
|
|
@ -89,145 +51,72 @@ bool ShouldValidateFlagValue(const CommandLineFlag& flag) {
|
|||
|
||||
} // namespace
|
||||
|
||||
// Update any copy of the flag value that is stored in an atomic word.
|
||||
// In addition if flag has a mutation callback this function invokes it.
|
||||
void UpdateCopy(CommandLineFlag* flag, absl::Mutex* primary_lock)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(primary_lock) {
|
||||
#define STORE_ATOMIC(T) \
|
||||
else if (flag->IsOfType<T>()) { \
|
||||
StoreAtomic(flag, flag->cur, sizeof(T)); \
|
||||
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;
|
||||
}
|
||||
|
||||
if (false) {
|
||||
}
|
||||
ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(STORE_ATOMIC)
|
||||
#undef STORE_ATOMIC
|
||||
{
|
||||
absl::MutexLock lock(mu);
|
||||
|
||||
InvokeCallback(flag, primary_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);
|
||||
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* 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;
|
||||
absl::Mutex* CommandLineFlag::InitFlagIfNecessary() const
|
||||
LOCK_RETURNED(locks->primary_mu) {
|
||||
if (!this->inited.load(std::memory_order_acquire)) {
|
||||
return InitFlag(const_cast<CommandLineFlag*>(this));
|
||||
}
|
||||
return mu;
|
||||
|
||||
// All fields initialized; this->locks is therefore safe to read.
|
||||
return &this->locks->primary_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); \
|
||||
void CommandLineFlag::Destroy() const {
|
||||
// Values are heap allocated for retired and Abseil Flags.
|
||||
if (IsRetired() || IsAbseilFlag()) {
|
||||
if (this->cur) Delete(this->op, this->cur);
|
||||
if (this->def) Delete(this->op, this->def);
|
||||
}
|
||||
|
||||
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;
|
||||
delete this->locks;
|
||||
}
|
||||
|
||||
// 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 CommandLineFlag::IsModified() const {
|
||||
absl::MutexLock l(InitFlagIfNecessary());
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool Validate(CommandLineFlag*, const void*) {
|
||||
return true;
|
||||
void CommandLineFlag::SetModified(bool is_modified) {
|
||||
absl::MutexLock l(InitFlagIfNecessary());
|
||||
modified = is_modified;
|
||||
}
|
||||
|
||||
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);
|
||||
bool CommandLineFlag::IsSpecifiedOnCommandLine() const {
|
||||
absl::MutexLock l(InitFlagIfNecessary());
|
||||
return on_command_line;
|
||||
}
|
||||
|
||||
absl::string_view CommandLineFlag::Typename() const {
|
||||
|
|
@ -259,21 +148,96 @@ std::string CommandLineFlag::Filename() const {
|
|||
}
|
||||
|
||||
std::string CommandLineFlag::DefaultValue() const {
|
||||
absl::MutexLock l(InitFlagIfNecessary());
|
||||
|
||||
return Unparse(this->marshalling_op, this->def);
|
||||
}
|
||||
|
||||
std::string CommandLineFlag::CurrentValue() const {
|
||||
absl::MutexLock l(InitFlagIfNecessary());
|
||||
|
||||
return Unparse(this->marshalling_op, this->cur);
|
||||
}
|
||||
|
||||
bool CommandLineFlag::HasValidatorFn() const {
|
||||
absl::MutexLock l(InitFlagIfNecessary());
|
||||
|
||||
return this->validator != nullptr;
|
||||
}
|
||||
|
||||
bool CommandLineFlag::SetValidatorFn(FlagValidator fn) {
|
||||
absl::MutexLock l(InitFlagIfNecessary());
|
||||
|
||||
// ok to register the same function over and over again
|
||||
if (fn == this->validator) return true;
|
||||
|
||||
// Can't set validator to a different function, unless reset first.
|
||||
if (fn != nullptr && this->validator != nullptr) {
|
||||
ABSL_INTERNAL_LOG(
|
||||
WARNING, absl::StrCat("Ignoring SetValidatorFn() for flag '", Name(),
|
||||
"': validate-fn already registered"));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
this->validator = fn;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CommandLineFlag::InvokeValidator(const void* value) const
|
||||
EXCLUSIVE_LOCKS_REQUIRED(this->locks->primary_mu) {
|
||||
if (!this->validator) {
|
||||
return true;
|
||||
}
|
||||
|
||||
(void)value;
|
||||
|
||||
ABSL_INTERNAL_LOG(
|
||||
FATAL,
|
||||
absl::StrCat("Flag '", Name(),
|
||||
"' of encapsulated type should not have a validator"));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CommandLineFlag::SetCallback(
|
||||
const flags_internal::FlagCallback mutation_callback) {
|
||||
absl::Mutex* mu = InitFlagIfNecessary(this);
|
||||
absl::MutexLock l(mu);
|
||||
absl::MutexLock l(InitFlagIfNecessary());
|
||||
|
||||
callback = mutation_callback;
|
||||
|
||||
InvokeCallback(this, mu);
|
||||
InvokeCallback();
|
||||
}
|
||||
|
||||
// If the flag has a mutation callback this function invokes it. While the
|
||||
// callback is being invoked the primary flag's mutex is unlocked and it is
|
||||
// re-locked back after call to callback is completed. Callback invocation is
|
||||
// guarded by flag's secondary mutex instead which prevents concurrent callback
|
||||
// invocation. Note that it is possible for other thread to grab the primary
|
||||
// lock and update flag's value at any time during the callback invocation.
|
||||
// This is by design. Callback can get a value of the flag if necessary, but it
|
||||
// might be different from the value initiated the callback and it also can be
|
||||
// different by the time the callback invocation is completed.
|
||||
// Requires that *primary_lock be held in exclusive mode; it may be released
|
||||
// and reacquired by the implementation.
|
||||
void CommandLineFlag::InvokeCallback()
|
||||
EXCLUSIVE_LOCKS_REQUIRED(this->locks->primary_mu) {
|
||||
if (!this->callback) return;
|
||||
|
||||
// The callback lock is guaranteed initialized, because *locks->primary_mu
|
||||
// exists.
|
||||
absl::Mutex* callback_mu = &this->locks->callback_mu;
|
||||
|
||||
// When executing the callback we need the primary flag's mutex to be unlocked
|
||||
// so that callback can retrieve the flag's value.
|
||||
this->locks->primary_mu.Unlock();
|
||||
|
||||
{
|
||||
absl::MutexLock lock(callback_mu);
|
||||
this->callback();
|
||||
}
|
||||
|
||||
this->locks->primary_mu.Lock();
|
||||
}
|
||||
|
||||
// Attempts to parse supplied `value` string using parsing routine in the `flag`
|
||||
|
|
@ -282,8 +246,9 @@ void CommandLineFlag::SetCallback(
|
|||
// parsed value in 'dst' assuming it is a pointer to the flag's value type. In
|
||||
// case if any error is encountered in either step, the error message is stored
|
||||
// in 'err'
|
||||
static bool TryParseLocked(CommandLineFlag* flag, void* dst,
|
||||
absl::string_view value, std::string* err) {
|
||||
bool TryParseLocked(CommandLineFlag* flag, void* dst, absl::string_view value,
|
||||
std::string* err)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(flag->locks->primary_mu) {
|
||||
void* tentative_value = Clone(flag->op, flag->def);
|
||||
std::string parse_err;
|
||||
if (!Parse(flag->marshalling_op, value, tentative_value, &parse_err)) {
|
||||
|
|
@ -297,7 +262,7 @@ static bool TryParseLocked(CommandLineFlag* flag, void* dst,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!Validate(flag, tentative_value)) {
|
||||
if (!flag->InvokeValidator(tentative_value)) {
|
||||
*err = absl::StrCat("Failed validation of new value '",
|
||||
Unparse(flag->marshalling_op, tentative_value),
|
||||
"' for flag '", flag->Name(), "'");
|
||||
|
|
@ -324,17 +289,23 @@ bool CommandLineFlag::SetFromString(absl::string_view value,
|
|||
ValueSource source, std::string* err) {
|
||||
if (IsRetired()) return false;
|
||||
|
||||
UpdateModifiedBit(this);
|
||||
absl::MutexLock l(InitFlagIfNecessary());
|
||||
|
||||
absl::Mutex* mu = InitFlagIfNecessary(this);
|
||||
absl::MutexLock l(mu);
|
||||
// Direct-access flags can be modified without going through the
|
||||
// flag API. Detect such changes and update the flag->modified bit.
|
||||
if (!IsAbseilFlag()) {
|
||||
if (!this->modified && ChangedDirectly(this, this->cur, this->def)) {
|
||||
this->modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
switch (set_mode) {
|
||||
case SET_FLAGS_VALUE: {
|
||||
// set or modify the flag's value
|
||||
if (!TryParseLocked(this, this->cur, value, err)) return false;
|
||||
this->modified = true;
|
||||
UpdateCopy(this, mu);
|
||||
UpdateCopy(this);
|
||||
InvokeCallback();
|
||||
|
||||
if (source == kCommandLine) {
|
||||
this->on_command_line = true;
|
||||
|
|
@ -346,7 +317,8 @@ bool CommandLineFlag::SetFromString(absl::string_view value,
|
|||
if (!this->modified) {
|
||||
if (!TryParseLocked(this, this->cur, value, err)) return false;
|
||||
this->modified = true;
|
||||
UpdateCopy(this, mu);
|
||||
UpdateCopy(this);
|
||||
InvokeCallback();
|
||||
} else {
|
||||
// TODO(rogeeff): review and fix this semantic. Currently we do not fail
|
||||
// in this case if flag is modified. This is misleading since the flag's
|
||||
|
|
@ -365,7 +337,8 @@ bool CommandLineFlag::SetFromString(absl::string_view value,
|
|||
if (!this->modified) {
|
||||
// Need to set both defvalue *and* current, in this case
|
||||
Copy(this->op, this->def, this->cur);
|
||||
UpdateCopy(this, mu);
|
||||
UpdateCopy(this);
|
||||
InvokeCallback();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -379,5 +352,143 @@ bool CommandLineFlag::SetFromString(absl::string_view value,
|
|||
return true;
|
||||
}
|
||||
|
||||
void CommandLineFlag::StoreAtomic(size_t size) {
|
||||
int64_t t = 0;
|
||||
assert(size <= sizeof(int64_t));
|
||||
memcpy(&t, this->cur, size);
|
||||
this->atomic.store(t, std::memory_order_release);
|
||||
}
|
||||
|
||||
void CommandLineFlag::CheckDefaultValueParsingRoundtrip() const {
|
||||
std::string v = DefaultValue();
|
||||
|
||||
absl::MutexLock lock(InitFlagIfNecessary());
|
||||
|
||||
void* dst = Clone(this->op, this->def);
|
||||
std::string error;
|
||||
if (!flags_internal::Parse(this->marshalling_op, v, dst, &error)) {
|
||||
ABSL_INTERNAL_LOG(
|
||||
FATAL,
|
||||
absl::StrCat("Flag ", Name(), " (from ", Filename(),
|
||||
"): std::string form of default value '", v,
|
||||
"' could not be parsed; error=", error));
|
||||
}
|
||||
|
||||
// We do not compare dst to def since parsing/unparsing may make
|
||||
// small changes, e.g., precision loss for floating point types.
|
||||
Delete(this->op, dst);
|
||||
}
|
||||
|
||||
bool CommandLineFlag::ValidateDefaultValue() const {
|
||||
absl::MutexLock lock(InitFlagIfNecessary());
|
||||
return InvokeValidator(this->def);
|
||||
}
|
||||
|
||||
bool CommandLineFlag::ValidateInputValue(absl::string_view value) const {
|
||||
absl::MutexLock l(InitFlagIfNecessary()); // protect default value access
|
||||
|
||||
void* obj = Clone(this->op, this->def);
|
||||
std::string ignored_error;
|
||||
const bool result =
|
||||
flags_internal::Parse(this->marshalling_op, value, obj, &ignored_error) &&
|
||||
InvokeValidator(obj);
|
||||
Delete(this->op, obj);
|
||||
return result;
|
||||
}
|
||||
|
||||
const int64_t CommandLineFlag::kAtomicInit;
|
||||
|
||||
void CommandLineFlag::Read(void* dst,
|
||||
const flags_internal::FlagOpFn dst_op) const {
|
||||
absl::ReaderMutexLock l(InitFlagIfNecessary());
|
||||
|
||||
// `dst_op` is the unmarshaling operation corresponding to the declaration
|
||||
// visibile at the call site. `op` is the Flag's defined unmarshalling
|
||||
// operation. They must match for this operation to be well-defined.
|
||||
if (ABSL_PREDICT_FALSE(dst_op != op)) {
|
||||
ABSL_INTERNAL_LOG(
|
||||
ERROR,
|
||||
absl::StrCat("Flag '", name,
|
||||
"' is defined as one type and declared as another"));
|
||||
}
|
||||
CopyConstruct(op, cur, dst);
|
||||
}
|
||||
|
||||
void CommandLineFlag::Write(const void* src,
|
||||
const flags_internal::FlagOpFn src_op) {
|
||||
absl::MutexLock l(InitFlagIfNecessary());
|
||||
|
||||
// `src_op` is the marshalling operation corresponding to the declaration
|
||||
// visible at the call site. `op` is the Flag's defined marshalling operation.
|
||||
// They must match for this operation to be well-defined.
|
||||
if (ABSL_PREDICT_FALSE(src_op != op)) {
|
||||
ABSL_INTERNAL_LOG(
|
||||
ERROR,
|
||||
absl::StrCat("Flag '", name,
|
||||
"' is defined as one type and declared as another"));
|
||||
}
|
||||
|
||||
if (ShouldValidateFlagValue(*this)) {
|
||||
void* obj = Clone(op, src);
|
||||
std::string ignored_error;
|
||||
std::string src_as_str = Unparse(marshalling_op, src);
|
||||
if (!Parse(marshalling_op, src_as_str, obj, &ignored_error) ||
|
||||
!InvokeValidator(obj)) {
|
||||
ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '", name,
|
||||
"' to invalid value ", src_as_str));
|
||||
}
|
||||
Delete(op, obj);
|
||||
}
|
||||
|
||||
modified = true;
|
||||
counter++;
|
||||
Copy(op, src, cur);
|
||||
|
||||
UpdateCopy(this);
|
||||
InvokeCallback();
|
||||
}
|
||||
|
||||
std::string HelpText::GetHelpText() const {
|
||||
if (help_function_) return help_function_();
|
||||
if (help_message_) return help_message_;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// Update any copy of the flag value that is stored in an atomic word.
|
||||
// In addition if flag has a mutation callback this function invokes it.
|
||||
void UpdateCopy(CommandLineFlag* flag) {
|
||||
#define STORE_ATOMIC(T) \
|
||||
else if (flag->IsOfType<T>()) { \
|
||||
flag->StoreAtomic(sizeof(T)); \
|
||||
}
|
||||
|
||||
if (false) {
|
||||
}
|
||||
ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(STORE_ATOMIC)
|
||||
#undef STORE_ATOMIC
|
||||
}
|
||||
|
||||
// Return true iff flag value was changed via direct-access.
|
||||
bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b) {
|
||||
if (!flag->IsAbseilFlag()) {
|
||||
// Need to compare values for direct-access flags.
|
||||
#define CHANGED_FOR_TYPE(T) \
|
||||
if (flag->IsOfType<T>()) { \
|
||||
return *reinterpret_cast<const T*>(a) != *reinterpret_cast<const T*>(b); \
|
||||
}
|
||||
|
||||
CHANGED_FOR_TYPE(bool);
|
||||
CHANGED_FOR_TYPE(int32_t);
|
||||
CHANGED_FOR_TYPE(int64_t);
|
||||
CHANGED_FOR_TYPE(uint64_t);
|
||||
CHANGED_FOR_TYPE(double);
|
||||
CHANGED_FOR_TYPE(std::string);
|
||||
#undef CHANGED_FOR_TYPE
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace flags_internal
|
||||
} // namespace absl
|
||||
|
|
|
|||
|
|
@ -69,11 +69,15 @@ using HelpGenFunc = std::string (*)();
|
|||
// based on default value supplied in flag's definition)
|
||||
using InitialValGenFunc = void* (*)();
|
||||
|
||||
struct CommandLineFlagInfo;
|
||||
|
||||
// Signature for the mutation callback used by watched Flags
|
||||
// The callback is noexcept.
|
||||
// TODO(rogeeff): add noexcept after C++17 support is added.
|
||||
using FlagCallback = void (*)();
|
||||
|
||||
using FlagValidator = bool (*)();
|
||||
|
||||
extern const char kStrippedFlagHelp[];
|
||||
|
||||
// The per-type function
|
||||
|
|
@ -217,6 +221,9 @@ struct CommandLineFlag {
|
|||
atomic(kAtomicInit),
|
||||
locks(nullptr) {}
|
||||
|
||||
// Revert the init routine.
|
||||
void Destroy() const;
|
||||
|
||||
// Not copyable/assignable.
|
||||
CommandLineFlag(const CommandLineFlag&) = delete;
|
||||
CommandLineFlag& operator=(const CommandLineFlag&) = delete;
|
||||
|
|
@ -224,7 +231,9 @@ struct CommandLineFlag {
|
|||
absl::string_view Name() const { return name; }
|
||||
std::string Help() const { return help.GetHelpText(); }
|
||||
bool IsRetired() const { return this->retired; }
|
||||
bool IsSpecifiedOnCommandLine() const { return on_command_line; }
|
||||
bool IsModified() const;
|
||||
void SetModified(bool is_modified);
|
||||
bool IsSpecifiedOnCommandLine() const;
|
||||
// Returns true iff this is a handle to an Abseil Flag.
|
||||
bool IsAbseilFlag() const {
|
||||
// Set to null for V1 flags
|
||||
|
|
@ -236,6 +245,10 @@ struct CommandLineFlag {
|
|||
std::string DefaultValue() const;
|
||||
std::string CurrentValue() const;
|
||||
|
||||
bool HasValidatorFn() const;
|
||||
bool SetValidatorFn(FlagValidator fn);
|
||||
bool InvokeValidator(const void* value) const;
|
||||
|
||||
// Return true iff flag has type T.
|
||||
template <typename T>
|
||||
inline bool IsOfType() const {
|
||||
|
|
@ -245,7 +258,7 @@ struct CommandLineFlag {
|
|||
// Attempts to retrieve the flag value. Returns value on success,
|
||||
// absl::nullopt otherwise.
|
||||
template <typename T>
|
||||
absl::optional<T> Get() {
|
||||
absl::optional<T> Get() const {
|
||||
if (IsRetired() || flags_internal::FlagOps<T> != this->op)
|
||||
return absl::nullopt;
|
||||
|
||||
|
|
@ -256,6 +269,7 @@ struct CommandLineFlag {
|
|||
}
|
||||
|
||||
void SetCallback(const flags_internal::FlagCallback mutation_callback);
|
||||
void InvokeCallback();
|
||||
|
||||
// Sets the value of the flag based on specified std::string `value`. If the flag
|
||||
// was successfully set to new value, it returns true. Otherwise, sets `error`
|
||||
|
|
@ -269,27 +283,35 @@ struct CommandLineFlag {
|
|||
flags_internal::FlagSettingMode set_mode,
|
||||
flags_internal::ValueSource source, std::string* error);
|
||||
|
||||
void StoreAtomic(size_t size);
|
||||
|
||||
void CheckDefaultValueParsingRoundtrip() const;
|
||||
// Invoke the flag validators for old flags.
|
||||
// TODO(rogeeff): implement proper validators for Abseil Flags
|
||||
bool ValidateDefaultValue() const;
|
||||
bool ValidateInputValue(absl::string_view value) const;
|
||||
|
||||
// Constant configuration for a particular flag.
|
||||
private:
|
||||
const char* const name;
|
||||
const HelpText help;
|
||||
const char* const filename;
|
||||
|
||||
public:
|
||||
const FlagOpFn op; // Type-specific handler
|
||||
protected:
|
||||
const FlagOpFn op; // Type-specific handler
|
||||
const FlagMarshallingOpFn marshalling_op; // Marshalling ops handler
|
||||
const InitialValGenFunc make_init_value; // Makes initial value for the flag
|
||||
const bool retired; // Is the flag retired?
|
||||
std::atomic<bool> inited; // fields have been lazily initialized
|
||||
const bool retired; // Is the flag retired?
|
||||
std::atomic<bool> inited; // fields have been lazily initialized
|
||||
|
||||
// Mutable state (guarded by locks->primary_mu).
|
||||
bool modified; // Has flag value been modified?
|
||||
bool on_command_line; // Specified on command line.
|
||||
bool (*validator)(); // Validator function, or nullptr
|
||||
FlagCallback callback; // Mutation callback, or nullptr
|
||||
void* def; // Lazily initialized pointer to default value
|
||||
void* cur; // Lazily initialized pointer to current value
|
||||
int64_t counter; // Mutation counter
|
||||
bool modified; // Has flag value been modified?
|
||||
bool on_command_line; // Specified on command line.
|
||||
FlagValidator validator; // Validator function, or nullptr
|
||||
FlagCallback callback; // Mutation callback, or nullptr
|
||||
void* def; // Lazily initialized pointer to default value
|
||||
void* cur; // Lazily initialized pointer to current value
|
||||
int64_t counter; // Mutation counter
|
||||
|
||||
// For some types, a copy of the current value is kept in an atomically
|
||||
// accessible field.
|
||||
|
|
@ -302,24 +324,26 @@ struct CommandLineFlag {
|
|||
// TODO(rogeeff): fix it once Mutex has constexpr constructor
|
||||
struct CommandLineFlagLocks* locks; // locks, laziliy allocated.
|
||||
|
||||
// Ensure that the lazily initialized fields of *flag have been initialized,
|
||||
// and return the lock which should be locked when flag's state is mutated.
|
||||
absl::Mutex* InitFlagIfNecessary() const;
|
||||
|
||||
// copy construct new value of flag's type in a memory referenced by dst
|
||||
// based on current flag's value
|
||||
void Read(void* dst, const flags_internal::FlagOpFn dst_op) const;
|
||||
// updates flag's value to *src (locked)
|
||||
void Write(const void* src, const flags_internal::FlagOpFn src_op);
|
||||
|
||||
ABSL_DEPRECATED(
|
||||
"temporary until FlagName call sites are migrated and validator API is "
|
||||
"changed")
|
||||
const char* NameAsCString() const { return name; }
|
||||
|
||||
private:
|
||||
friend class FlagRegistry;
|
||||
friend class FlagPtrMap;
|
||||
friend class FlagSaverImpl;
|
||||
friend void FillCommandLineFlagInfo(CommandLineFlag* flag,
|
||||
CommandLineFlagInfo* result);
|
||||
friend bool TryParseLocked(CommandLineFlag* flag, void* dst,
|
||||
absl::string_view value, std::string* err);
|
||||
friend absl::Mutex* InitFlag(CommandLineFlag* flag);
|
||||
};
|
||||
|
||||
// Ensure that the lazily initialized fields of *flag have been initialized,
|
||||
// and return &flag->locks->primary_mu.
|
||||
absl::Mutex* InitFlagIfNecessary(CommandLineFlag* flag);
|
||||
// Update any copy of the flag value that is stored in an atomic word.
|
||||
// In addition if flag has a mutation callback this function invokes it. While
|
||||
// callback is being invoked the primary flag's mutex is unlocked and it is
|
||||
|
|
@ -332,15 +356,9 @@ absl::Mutex* InitFlagIfNecessary(CommandLineFlag* flag);
|
|||
// different by the time the callback invocation is completed.
|
||||
// Requires that *primary_lock be held in exclusive mode; it may be released
|
||||
// and reacquired by the implementation.
|
||||
void UpdateCopy(CommandLineFlag* flag, absl::Mutex* primary_lock);
|
||||
void UpdateCopy(CommandLineFlag* flag);
|
||||
// Return true iff flag value was changed via direct-access.
|
||||
bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b);
|
||||
// Direct-access flags can be modified without going through the
|
||||
// flag API. Detect such changes and updated the modified bit.
|
||||
void UpdateModifiedBit(CommandLineFlag* flag);
|
||||
// Invoke the flag validators for old flags.
|
||||
// TODO(rogeeff): implement proper validators for Abseil Flags
|
||||
bool Validate(CommandLineFlag* flag, const void* value);
|
||||
|
||||
// This macro is the "source of truth" for the list of supported flag types we
|
||||
// expect to perform lock free operations on. Specifically it generates code,
|
||||
|
|
|
|||
|
|
@ -100,39 +100,39 @@ TEST_F(CommandLineFlagTest, TestSetFromStringCurrentValue) {
|
|||
std::string err;
|
||||
|
||||
auto* flag_01 = flags::FindCommandLineFlag("int_flag");
|
||||
EXPECT_FALSE(flag_01->on_command_line);
|
||||
EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine());
|
||||
|
||||
EXPECT_TRUE(flag_01->SetFromString("11", flags::SET_FLAGS_VALUE,
|
||||
flags::kProgrammaticChange, &err));
|
||||
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11);
|
||||
EXPECT_FALSE(flag_01->on_command_line);
|
||||
EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine());
|
||||
|
||||
EXPECT_TRUE(flag_01->SetFromString("-123", flags::SET_FLAGS_VALUE,
|
||||
flags::kProgrammaticChange, &err));
|
||||
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123);
|
||||
EXPECT_FALSE(flag_01->on_command_line);
|
||||
EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine());
|
||||
|
||||
EXPECT_TRUE(!flag_01->SetFromString("xyz", flags::SET_FLAGS_VALUE,
|
||||
flags::kProgrammaticChange, &err));
|
||||
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123);
|
||||
EXPECT_EQ(err, "Illegal value 'xyz' specified for flag 'int_flag'");
|
||||
EXPECT_FALSE(flag_01->on_command_line);
|
||||
EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine());
|
||||
|
||||
EXPECT_TRUE(!flag_01->SetFromString("A1", flags::SET_FLAGS_VALUE,
|
||||
flags::kProgrammaticChange, &err));
|
||||
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123);
|
||||
EXPECT_EQ(err, "Illegal value 'A1' specified for flag 'int_flag'");
|
||||
EXPECT_FALSE(flag_01->on_command_line);
|
||||
EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine());
|
||||
|
||||
EXPECT_TRUE(flag_01->SetFromString("0x10", flags::SET_FLAGS_VALUE,
|
||||
flags::kProgrammaticChange, &err));
|
||||
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 16);
|
||||
EXPECT_FALSE(flag_01->on_command_line);
|
||||
EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine());
|
||||
|
||||
EXPECT_TRUE(flag_01->SetFromString("011", flags::SET_FLAGS_VALUE,
|
||||
flags::kCommandLine, &err));
|
||||
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11);
|
||||
EXPECT_TRUE(flag_01->on_command_line);
|
||||
EXPECT_TRUE(flag_01->IsSpecifiedOnCommandLine());
|
||||
|
||||
EXPECT_TRUE(!flag_01->SetFromString("", flags::SET_FLAGS_VALUE,
|
||||
flags::kProgrammaticChange, &err));
|
||||
|
|
|
|||
|
|
@ -24,40 +24,58 @@ namespace flags_internal {
|
|||
|
||||
// This is "unspecified" implementation of absl::Flag<T> type.
|
||||
template <typename T>
|
||||
class Flag {
|
||||
class Flag : public flags_internal::CommandLineFlag {
|
||||
public:
|
||||
constexpr Flag(const char* name, const flags_internal::HelpGenFunc help_gen,
|
||||
const char* filename,
|
||||
const flags_internal::FlagMarshallingOpFn marshalling_op,
|
||||
const flags_internal::FlagMarshallingOpFn marshalling_op_arg,
|
||||
const flags_internal::InitialValGenFunc initial_value_gen)
|
||||
: internal(name, flags_internal::HelpText::FromFunctionPointer(help_gen),
|
||||
filename, &flags_internal::FlagOps<T>, marshalling_op,
|
||||
initial_value_gen,
|
||||
/*retired_arg=*/false, /*def_arg=*/nullptr,
|
||||
/*cur_arg=*/nullptr) {}
|
||||
: flags_internal::CommandLineFlag(
|
||||
name, flags_internal::HelpText::FromFunctionPointer(help_gen),
|
||||
filename, &flags_internal::FlagOps<T>, marshalling_op_arg,
|
||||
initial_value_gen,
|
||||
/*retired_arg=*/false, /*def_arg=*/nullptr,
|
||||
/*cur_arg=*/nullptr) {}
|
||||
|
||||
// Not copyable/assignable.
|
||||
Flag(const Flag<T>&) = delete;
|
||||
Flag<T>& operator=(const Flag<T>&) = delete;
|
||||
T Get() const {
|
||||
// Implementation notes:
|
||||
//
|
||||
// We are wrapping a union around the value of `T` to serve three purposes:
|
||||
//
|
||||
// 1. `U.value` has correct size and alignment for a value of type `T`
|
||||
// 2. The `U.value` constructor is not invoked since U's constructor does
|
||||
// not
|
||||
// do it explicitly.
|
||||
// 3. The `U.value` destructor is invoked since U's destructor does it
|
||||
// explicitly. This makes `U` a kind of RAII wrapper around non default
|
||||
// constructible value of T, which is destructed when we leave the
|
||||
// scope. We do need to destroy U.value, which is constructed by
|
||||
// CommandLineFlag::Read even though we left it in a moved-from state
|
||||
// after std::move.
|
||||
//
|
||||
// All of this serves to avoid requiring `T` being default constructible.
|
||||
union U {
|
||||
T value;
|
||||
U() {}
|
||||
~U() { value.~T(); }
|
||||
};
|
||||
U u;
|
||||
|
||||
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);
|
||||
this->Read(&u.value, &flags_internal::FlagOps<T>);
|
||||
return std::move(u.value);
|
||||
}
|
||||
|
||||
private:
|
||||
// TODO(rogeeff): add these validations once UnparseFlag invocation is fixed
|
||||
// for built-in types and when we cleanup existing code from operating on
|
||||
// forward declared types.
|
||||
// auto IsCopyConstructible(const T& v) -> decltype(T(v));
|
||||
// auto HasAbslParseFlag(absl::string_view in, T* dst, std::string* err)
|
||||
// -> decltype(AbslParseFlag(in, dst, err));
|
||||
// auto HasAbslUnparseFlag(const T& v) -> decltype(AbslUnparseFlag(v));
|
||||
bool AtomicGet(T* v) const {
|
||||
const int64_t r = this->atomic.load(std::memory_order_acquire);
|
||||
if (r != flags_internal::CommandLineFlag::kAtomicInit) {
|
||||
memcpy(v, &r, sizeof(T));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Set(const T& v) { this->Write(&v, &flags_internal::FlagOps<T>); }
|
||||
};
|
||||
|
||||
// This class facilitates Flag object registration and tail expression-based
|
||||
|
|
@ -67,7 +85,7 @@ template <typename T, bool do_register>
|
|||
class FlagRegistrar {
|
||||
public:
|
||||
explicit FlagRegistrar(Flag<T>* flag) : flag_(flag) {
|
||||
if (do_register) flags_internal::RegisterCommandLineFlag(&flag_->internal);
|
||||
if (do_register) flags_internal::RegisterCommandLineFlag(flag_);
|
||||
}
|
||||
|
||||
FlagRegistrar& OnUpdate(flags_internal::FlagCallback cb) && {
|
||||
|
|
|
|||
|
|
@ -34,13 +34,7 @@ namespace flags_internal {
|
|||
namespace {
|
||||
|
||||
void DestroyFlag(CommandLineFlag* flag) NO_THREAD_SAFETY_ANALYSIS {
|
||||
// Values are heap allocated for retired and Abseil Flags.
|
||||
if (flag->IsRetired() || flag->IsAbseilFlag()) {
|
||||
if (flag->cur) Delete(flag->op, flag->cur);
|
||||
if (flag->def) Delete(flag->op, flag->def);
|
||||
}
|
||||
|
||||
delete flag->locks;
|
||||
flag->Destroy();
|
||||
|
||||
// CommandLineFlag handle object is heap allocated for non Abseil Flags.
|
||||
if (!flag->IsAbseilFlag()) {
|
||||
|
|
@ -48,6 +42,8 @@ void DestroyFlag(CommandLineFlag* flag) NO_THREAD_SAFETY_ANALYSIS {
|
|||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// FlagRegistry
|
||||
// A FlagRegistry singleton object holds all flag objects indexed
|
||||
|
|
@ -105,8 +101,6 @@ class FlagPtrMap {
|
|||
};
|
||||
constexpr size_t FlagPtrMap::kNumBuckets;
|
||||
|
||||
} // namespace
|
||||
|
||||
class FlagRegistry {
|
||||
public:
|
||||
FlagRegistry() = default;
|
||||
|
|
@ -292,10 +286,10 @@ class FlagSaverImpl {
|
|||
saved.op = flag->op;
|
||||
saved.marshalling_op = flag->marshalling_op;
|
||||
{
|
||||
absl::MutexLock l(InitFlagIfNecessary(flag));
|
||||
absl::MutexLock l(flag->InitFlagIfNecessary());
|
||||
saved.validator = flag->validator;
|
||||
saved.modified = flag->modified;
|
||||
saved.on_command_line = flag->IsSpecifiedOnCommandLine();
|
||||
saved.on_command_line = flag->on_command_line;
|
||||
saved.current = Clone(saved.op, flag->cur);
|
||||
saved.default_value = Clone(saved.op, flag->def);
|
||||
saved.counter = flag->counter;
|
||||
|
|
@ -318,34 +312,34 @@ class FlagSaverImpl {
|
|||
|
||||
bool restored = false;
|
||||
{
|
||||
absl::Mutex* mu = InitFlagIfNecessary(flag);
|
||||
absl::MutexLock l(mu);
|
||||
absl::MutexLock l(flag->InitFlagIfNecessary());
|
||||
flag->validator = src.validator;
|
||||
flag->modified = src.modified;
|
||||
flag->on_command_line = src.on_command_line;
|
||||
if (flag->counter != src.counter ||
|
||||
ChangedDirectly(flag, src.default_value, flag->def)) {
|
||||
flag->counter++;
|
||||
restored = true;
|
||||
Copy(src.op, src.default_value, flag->def);
|
||||
}
|
||||
if (flag->counter != src.counter ||
|
||||
ChangedDirectly(flag, src.current, flag->cur)) {
|
||||
restored = true;
|
||||
flag->counter++;
|
||||
Copy(src.op, src.current, flag->cur);
|
||||
UpdateCopy(flag, mu);
|
||||
|
||||
// Revalidate the flag because the validator might store state based
|
||||
// on the flag's value, which just changed due to the restore.
|
||||
// Failing validation is ignored because it's assumed that the flag
|
||||
// was valid previously and there's little that can be done about it
|
||||
// here, anyway.
|
||||
Validate(flag, flag->cur);
|
||||
UpdateCopy(flag);
|
||||
flag->InvokeCallback();
|
||||
}
|
||||
}
|
||||
|
||||
// Log statements must be done when no flag lock is held.
|
||||
if (restored) {
|
||||
flag->counter++;
|
||||
|
||||
// Revalidate the flag because the validator might store state based
|
||||
// on the flag's value, which just changed due to the restore.
|
||||
// Failing validation is ignored because it's assumed that the flag
|
||||
// was valid previously and there's little that can be done about it
|
||||
// here, anyway.
|
||||
flag->ValidateInputValue(flag->CurrentValue());
|
||||
|
||||
ABSL_INTERNAL_LOG(
|
||||
INFO, absl::StrCat("Restore saved value of ", flag->Name(), ": ",
|
||||
Unparse(src.marshalling_op, src.current)));
|
||||
|
|
@ -412,13 +406,17 @@ void FillCommandLineFlagInfo(CommandLineFlag* flag,
|
|||
result->description = flag->Help();
|
||||
result->filename = flag->Filename();
|
||||
|
||||
UpdateModifiedBit(flag);
|
||||
if (!flag->IsAbseilFlag()) {
|
||||
if (!flag->IsModified() && ChangedDirectly(flag, flag->cur, flag->def)) {
|
||||
flag->modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
absl::MutexLock l(InitFlagIfNecessary(flag));
|
||||
result->current_value = flag->CurrentValue();
|
||||
result->default_value = flag->DefaultValue();
|
||||
result->is_default = !flag->modified;
|
||||
result->has_validator_fn = (flag->validator != nullptr);
|
||||
result->is_default = !flag->IsModified();
|
||||
result->has_validator_fn = flag->HasValidatorFn();
|
||||
absl::MutexLock l(flag->InitFlagIfNecessary());
|
||||
result->flag_ptr = flag->IsAbseilFlag() ? nullptr : flag->cur;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ bool GetCommandLineOption(absl::string_view name, std::string* value) {
|
|||
return false;
|
||||
}
|
||||
|
||||
absl::MutexLock l(InitFlagIfNecessary(flag));
|
||||
*value = flag->CurrentValue();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -88,22 +87,9 @@ bool SetCommandLineOptionWithMode(absl::string_view name,
|
|||
|
||||
bool IsValidFlagValue(absl::string_view name, absl::string_view value) {
|
||||
CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name);
|
||||
if (flag == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flag->IsRetired()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// No need to lock the flag since we are not mutating it.
|
||||
void* obj = Clone(flag->op, flag->def);
|
||||
std::string ignored_error;
|
||||
const bool result =
|
||||
flags_internal::Parse(flag->marshalling_op, value, obj, &ignored_error) &&
|
||||
Validate(flag, obj);
|
||||
Delete(flag->op, obj);
|
||||
return result;
|
||||
return flag != nullptr &&
|
||||
(flag->IsRetired() || flag->ValidateInputValue(value));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
|
@ -111,7 +97,6 @@ bool IsValidFlagValue(absl::string_view name, absl::string_view value) {
|
|||
bool SpecifiedOnCommandLine(absl::string_view name) {
|
||||
CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name);
|
||||
if (flag != nullptr && !flag->IsRetired()) {
|
||||
absl::MutexLock l(InitFlagIfNecessary(flag));
|
||||
return flag->IsSpecifiedOnCommandLine();
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -21,11 +21,11 @@
|
|||
#include "absl/flags/flag.h"
|
||||
#include "absl/flags/internal/path_util.h"
|
||||
#include "absl/flags/internal/program_name.h"
|
||||
#include "absl/flags/usage.h"
|
||||
#include "absl/flags/usage_config.h"
|
||||
#include "absl/strings/ascii.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
|
||||
ABSL_FLAG(bool, help, false,
|
||||
|
|
@ -185,7 +185,7 @@ void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag,
|
|||
}
|
||||
printer.Write(absl::StrCat("default: ", dflt_val, ";"));
|
||||
|
||||
if (flag.modified) {
|
||||
if (flag.IsModified()) {
|
||||
std::string curr_val = flag.CurrentValue();
|
||||
if (flag.IsOfType<std::string>()) {
|
||||
curr_val = absl::StrCat("\"", curr_val, "\"");
|
||||
|
|
@ -202,10 +202,10 @@ void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag,
|
|||
// STRIP_FLAG_HELP 1' then this flag will not be displayed by '--help'
|
||||
// and its variants.
|
||||
void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
|
||||
HelpFormat format = HelpFormat::kHumanReadable) {
|
||||
HelpFormat format, absl::string_view program_usage_message) {
|
||||
if (format == HelpFormat::kHumanReadable) {
|
||||
out << flags_internal::ShortProgramInvocationName() << ": "
|
||||
<< absl::ProgramUsageMessage() << "\n\n";
|
||||
<< program_usage_message << "\n\n";
|
||||
} else {
|
||||
// XML schema is not a part of our public API for now.
|
||||
out << "<?xml version=\"1.0\"?>\n"
|
||||
|
|
@ -214,7 +214,7 @@ void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
|
|||
// The program name and usage.
|
||||
<< XMLElement("program", flags_internal::ShortProgramInvocationName())
|
||||
<< '\n'
|
||||
<< XMLElement("usage", absl::ProgramUsageMessage()) << '\n';
|
||||
<< XMLElement("usage", program_usage_message) << '\n';
|
||||
}
|
||||
|
||||
// Map of package name to
|
||||
|
|
@ -228,8 +228,6 @@ void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
|
|||
matching_flags;
|
||||
|
||||
flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) {
|
||||
absl::MutexLock l(InitFlagIfNecessary(flag));
|
||||
|
||||
std::string flag_filename = flag->Filename();
|
||||
|
||||
// Ignore retired flags.
|
||||
|
|
@ -292,44 +290,51 @@ void FlagHelp(std::ostream& out, const flags_internal::CommandLineFlag& flag,
|
|||
// --------------------------------------------------------------------
|
||||
// Produces the help messages for all flags matching the filter.
|
||||
// If filter is empty produces help messages for all flags.
|
||||
void FlagsHelp(std::ostream& out, absl::string_view filter, HelpFormat format) {
|
||||
void FlagsHelp(std::ostream& out, absl::string_view filter, HelpFormat format,
|
||||
absl::string_view program_usage_message) {
|
||||
flags_internal::FlagKindFilter filter_cb = [&](absl::string_view filename) {
|
||||
return filter.empty() || filename.find(filter) != absl::string_view::npos;
|
||||
};
|
||||
flags_internal::FlagsHelpImpl(out, filter_cb, format);
|
||||
flags_internal::FlagsHelpImpl(out, filter_cb, format, program_usage_message);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Checks all the 'usage' command line flags to see if any have been set.
|
||||
// If so, handles them appropriately.
|
||||
int HandleUsageFlags(std::ostream& out) {
|
||||
int HandleUsageFlags(std::ostream& out,
|
||||
absl::string_view program_usage_message) {
|
||||
if (absl::GetFlag(FLAGS_helpshort)) {
|
||||
flags_internal::FlagsHelpImpl(
|
||||
out, flags_internal::GetUsageConfig().contains_helpshort_flags,
|
||||
HelpFormat::kHumanReadable);
|
||||
HelpFormat::kHumanReadable, program_usage_message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (absl::GetFlag(FLAGS_helpfull)) {
|
||||
// show all options
|
||||
flags_internal::FlagsHelp(out);
|
||||
flags_internal::FlagsHelp(out, "", HelpFormat::kHumanReadable,
|
||||
program_usage_message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!absl::GetFlag(FLAGS_helpon).empty()) {
|
||||
flags_internal::FlagsHelp(
|
||||
out, absl::StrCat("/", absl::GetFlag(FLAGS_helpon), "."));
|
||||
out, absl::StrCat("/", absl::GetFlag(FLAGS_helpon), "."),
|
||||
HelpFormat::kHumanReadable, program_usage_message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!absl::GetFlag(FLAGS_helpmatch).empty()) {
|
||||
flags_internal::FlagsHelp(out, absl::GetFlag(FLAGS_helpmatch));
|
||||
flags_internal::FlagsHelp(out, absl::GetFlag(FLAGS_helpmatch),
|
||||
HelpFormat::kHumanReadable,
|
||||
program_usage_message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (absl::GetFlag(FLAGS_help)) {
|
||||
flags_internal::FlagsHelpImpl(
|
||||
out, flags_internal::GetUsageConfig().contains_help_flags);
|
||||
out, flags_internal::GetUsageConfig().contains_help_flags,
|
||||
HelpFormat::kHumanReadable, program_usage_message);
|
||||
|
||||
out << "\nTry --helpfull to get a list of all flags.\n";
|
||||
|
||||
|
|
@ -338,7 +343,8 @@ int HandleUsageFlags(std::ostream& out) {
|
|||
|
||||
if (absl::GetFlag(FLAGS_helppackage)) {
|
||||
flags_internal::FlagsHelpImpl(
|
||||
out, flags_internal::GetUsageConfig().contains_helppackage_flags);
|
||||
out, flags_internal::GetUsageConfig().contains_helppackage_flags,
|
||||
HelpFormat::kHumanReadable, program_usage_message);
|
||||
|
||||
out << "\nTry --helpfull to get a list of all flags.\n";
|
||||
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@ void FlagHelp(std::ostream& out, const flags_internal::CommandLineFlag& flag,
|
|||
// .../path/to/file.<ext>
|
||||
// for any extension 'ext'. If the filter is empty this function produces help
|
||||
// messages for all flags.
|
||||
void FlagsHelp(std::ostream& out, absl::string_view filter = {},
|
||||
HelpFormat format = HelpFormat::kHumanReadable);
|
||||
void FlagsHelp(std::ostream& out, absl::string_view filter,
|
||||
HelpFormat format, absl::string_view program_usage_message);
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
|
|
@ -60,7 +60,8 @@ void FlagsHelp(std::ostream& out, absl::string_view filter = {},
|
|||
// -1 - if no usage flags were set on a commmand line.
|
||||
// Non negative return values are expected to be used as an exit code for a
|
||||
// binary.
|
||||
int HandleUsageFlags(std::ostream& out);
|
||||
int HandleUsageFlags(std::ostream& out,
|
||||
absl::string_view program_usage_message);
|
||||
|
||||
} // namespace flags_internal
|
||||
} // namespace absl
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ ABSL_FLAG(double, usage_reporting_test_flag_03, 1.03,
|
|||
ABSL_FLAG(int64_t, usage_reporting_test_flag_04, 1000000000000004L,
|
||||
"usage_reporting_test_flag_04 help message");
|
||||
|
||||
static const char kTestUsageMessage[] = "Custom usage message";
|
||||
|
||||
struct UDT {
|
||||
UDT() = default;
|
||||
UDT(const UDT&) = default;
|
||||
|
|
@ -83,7 +85,7 @@ class UsageReportingTest : public testing::Test {
|
|||
using UsageReportingDeathTest = UsageReportingTest;
|
||||
|
||||
TEST_F(UsageReportingDeathTest, TestSetProgramUsageMessage) {
|
||||
EXPECT_EQ(absl::ProgramUsageMessage(), "Custom usage message");
|
||||
EXPECT_EQ(absl::ProgramUsageMessage(), kTestUsageMessage);
|
||||
|
||||
#ifndef _WIN32
|
||||
// TODO(rogeeff): figure out why this does not work on Windows.
|
||||
|
|
@ -175,22 +177,22 @@ TEST_F(UsageReportingTest, TestFlagsHelpHRF) {
|
|||
|
||||
std::stringstream test_buf_01;
|
||||
flags::FlagsHelp(test_buf_01, "usage_test.cc",
|
||||
flags::HelpFormat::kHumanReadable);
|
||||
flags::HelpFormat::kHumanReadable, kTestUsageMessage);
|
||||
EXPECT_EQ(test_buf_01.str(), usage_test_flags_out);
|
||||
|
||||
std::stringstream test_buf_02;
|
||||
flags::FlagsHelp(test_buf_02, "flags/internal/usage_test.cc",
|
||||
flags::HelpFormat::kHumanReadable);
|
||||
flags::HelpFormat::kHumanReadable, kTestUsageMessage);
|
||||
EXPECT_EQ(test_buf_02.str(), usage_test_flags_out);
|
||||
|
||||
std::stringstream test_buf_03;
|
||||
flags::FlagsHelp(test_buf_03, "usage_test",
|
||||
flags::HelpFormat::kHumanReadable);
|
||||
flags::FlagsHelp(test_buf_03, "usage_test", flags::HelpFormat::kHumanReadable,
|
||||
kTestUsageMessage);
|
||||
EXPECT_EQ(test_buf_03.str(), usage_test_flags_out);
|
||||
|
||||
std::stringstream test_buf_04;
|
||||
flags::FlagsHelp(test_buf_04, "flags/invalid_file_name.cc",
|
||||
flags::HelpFormat::kHumanReadable);
|
||||
flags::HelpFormat::kHumanReadable, kTestUsageMessage);
|
||||
EXPECT_EQ(test_buf_04.str(),
|
||||
R"(usage_test: Custom usage message
|
||||
|
||||
|
|
@ -198,7 +200,8 @@ TEST_F(UsageReportingTest, TestFlagsHelpHRF) {
|
|||
)");
|
||||
|
||||
std::stringstream test_buf_05;
|
||||
flags::FlagsHelp(test_buf_05, "", flags::HelpFormat::kHumanReadable);
|
||||
flags::FlagsHelp(test_buf_05, "", flags::HelpFormat::kHumanReadable,
|
||||
kTestUsageMessage);
|
||||
std::string test_out = test_buf_05.str();
|
||||
absl::string_view test_out_str(test_out);
|
||||
EXPECT_TRUE(
|
||||
|
|
@ -217,7 +220,7 @@ TEST_F(UsageReportingTest, TestFlagsHelpHRF) {
|
|||
|
||||
TEST_F(UsageReportingTest, TestNoUsageFlags) {
|
||||
std::stringstream test_buf;
|
||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf), -1);
|
||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), -1);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
|
@ -226,7 +229,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpshort) {
|
|||
absl::SetFlag(&FLAGS_helpshort, true);
|
||||
|
||||
std::stringstream test_buf;
|
||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf), 1);
|
||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
|
||||
EXPECT_EQ(test_buf.str(),
|
||||
R"(usage_test: Custom usage message
|
||||
|
||||
|
|
@ -250,7 +253,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_help) {
|
|||
absl::SetFlag(&FLAGS_help, true);
|
||||
|
||||
std::stringstream test_buf;
|
||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf), 1);
|
||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
|
||||
EXPECT_EQ(test_buf.str(),
|
||||
R"(usage_test: Custom usage message
|
||||
|
||||
|
|
@ -276,7 +279,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_helppackage) {
|
|||
absl::SetFlag(&FLAGS_helppackage, true);
|
||||
|
||||
std::stringstream test_buf;
|
||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf), 1);
|
||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
|
||||
EXPECT_EQ(test_buf.str(),
|
||||
R"(usage_test: Custom usage message
|
||||
|
||||
|
|
@ -302,10 +305,9 @@ TEST_F(UsageReportingTest, TestUsageFlag_version) {
|
|||
absl::SetFlag(&FLAGS_version, true);
|
||||
|
||||
std::stringstream test_buf;
|
||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf), 0);
|
||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 0);
|
||||
#ifndef NDEBUG
|
||||
EXPECT_EQ(test_buf.str(),
|
||||
"usage_test\nDebug build (NDEBUG not #defined)\n");
|
||||
EXPECT_EQ(test_buf.str(), "usage_test\nDebug build (NDEBUG not #defined)\n");
|
||||
#else
|
||||
EXPECT_EQ(test_buf.str(), "usage_test\n");
|
||||
#endif
|
||||
|
|
@ -317,7 +319,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_only_check_args) {
|
|||
absl::SetFlag(&FLAGS_only_check_args, true);
|
||||
|
||||
std::stringstream test_buf;
|
||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf), 0);
|
||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 0);
|
||||
EXPECT_EQ(test_buf.str(), "");
|
||||
}
|
||||
|
||||
|
|
@ -327,7 +329,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
|
|||
absl::SetFlag(&FLAGS_helpon, "bla-bla");
|
||||
|
||||
std::stringstream test_buf_01;
|
||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf_01), 1);
|
||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf_01, kTestUsageMessage), 1);
|
||||
EXPECT_EQ(test_buf_01.str(),
|
||||
R"(usage_test: Custom usage message
|
||||
|
||||
|
|
@ -337,7 +339,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
|
|||
absl::SetFlag(&FLAGS_helpon, "usage_test");
|
||||
|
||||
std::stringstream test_buf_02;
|
||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf_02), 1);
|
||||
EXPECT_EQ(flags::HandleUsageFlags(test_buf_02, kTestUsageMessage), 1);
|
||||
EXPECT_EQ(test_buf_02.str(),
|
||||
R"(usage_test: Custom usage message
|
||||
|
||||
|
|
@ -362,7 +364,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
|
|||
int main(int argc, char* argv[]) {
|
||||
absl::GetFlag(FLAGS_undefok); // Force linking of parse.cc
|
||||
flags::SetProgramInvocationName("usage_test");
|
||||
absl::SetProgramUsageMessage("Custom usage message");
|
||||
absl::SetProgramUsageMessage(kTestUsageMessage);
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue