Move rare flag callbacks and dynamic defaults into process-wide maps. Few `absl::Flag` instances ever register a mutation callback or override their default value at runtime. Despite that, every `absl::flags_internal::FlagImpl` in writable data reserves an 8-byte callback pointer and mutates its default-value union in place. Store registered callbacks and dynamic default overrides in process-wide `ThreadSafeMap` instances instead, gated by a 1-bit callback indicator and the existing 2-bit default-kind field so flags without overrides never acquire a map lock. Removing the inline callback pointer shrinks `FlagImpl` from 80 bytes to 72 bytes and scalar flags from 88 bytes to 80 bytes, while keeping the initial default-value union immutable for upcoming descriptor splitting. PiperOrigin-RevId: 1006216114
diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index 59771c7..a43151f 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel
@@ -240,6 +240,7 @@ "//absl/base:dynamic_annotations", "//absl/base:fast_type_id", "//absl/base:no_destructor", + "//absl/container:flat_hash_map", "//absl/memory", "//absl/meta:type_traits", "//absl/strings",
diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt index 162b654..788ff0a 100644 --- a/absl/flags/CMakeLists.txt +++ b/absl/flags/CMakeLists.txt
@@ -201,6 +201,7 @@ absl::flags_commandlineflag_internal absl::flags_config absl::flags_marshalling + absl::flat_hash_map absl::meta absl::no_destructor absl::synchronization
diff --git a/absl/flags/commandlineflag_test.cc b/absl/flags/commandlineflag_test.cc index b6d01b2..35ce567 100644 --- a/absl/flags/commandlineflag_test.cc +++ b/absl/flags/commandlineflag_test.cc
@@ -16,7 +16,9 @@ #include "absl/flags/commandlineflag.h" #include <memory> +#include <optional> #include <string> +#include <vector> #include "gtest/gtest.h" #include "absl/flags/config.h" @@ -38,6 +40,21 @@ ABSL_FLAG(int, int_flag2, 201, ""); ABSL_FLAG(std::string, string_flag2, "dflt", ""); +using CustomIntAlias = int; +using CustomStringListAlias = std::vector<std::string>; + +struct CustomFlagUDT {}; +bool AbslParseFlag(absl::string_view, CustomFlagUDT*, std::string*) { + return true; +} +std::string AbslUnparseFlag(const CustomFlagUDT&) { return ""; } + +ABSL_FLAG(CustomIntAlias, int_alias_flag, 7, ""); +ABSL_FLAG(CustomStringListAlias, string_list_alias_flag, {}, ""); +ABSL_FLAG(std::vector<std::string>, string_vec_flag, {}, ""); +ABSL_FLAG(std::optional<int>, optional_int_flag, std::nullopt, ""); +ABSL_FLAG(CustomFlagUDT, custom_udt_flag, {}, ""); + namespace { namespace flags = absl::flags_internal; @@ -233,4 +250,67 @@ // EXPECT_EQ(err, "ERROR: int_flag is already set to 201"); } +void MakeEmptyString(void* dst) { new (dst) std::string(); } + +constexpr flags::FlagHelpArg kCompatHelpArg{flags::FlagHelpMsg("help"), + flags::FlagHelpKind::kLiteral}; +constexpr flags::FlagDefaultArg kCompatStringDefaultArg{ + flags::FlagDefaultSrc(&MakeEmptyString), flags::FlagDefaultKind::kGenFunc}; +constexpr absl::Flag<std::string> kCompat4ArgFlag{"compat4", "file", + kCompatHelpArg, + kCompatStringDefaultArg}; +constexpr absl::Flag<std::string> kCompat5ArgCommonFlag{ + "compat5_common", "std::string", "file", kCompatHelpArg, + kCompatStringDefaultArg}; +constexpr absl::Flag<std::string> kCompat5ArgCustomFlag{ + "compat5_custom", "UnlistedStringAlias", "file", kCompatHelpArg, + kCompatStringDefaultArg}; + +TEST_F(CommandLineFlagTest, TestTypeName) { + EXPECT_EQ(flags::PrivateHandleAccessor::TypeName( + *absl::FindCommandLineFlag("int_flag")), + "int"); + EXPECT_EQ(flags::PrivateHandleAccessor::TypeName( + *absl::FindCommandLineFlag("string_flag")), + "std::string"); + EXPECT_EQ(flags::PrivateHandleAccessor::TypeName( + *absl::FindCommandLineFlag("string_vec_flag")), + "std::vector<std::string>"); + EXPECT_EQ(flags::PrivateHandleAccessor::TypeName( + *absl::FindCommandLineFlag("optional_int_flag")), + "std::optional<int>"); + EXPECT_EQ(flags::PrivateHandleAccessor::TypeName( + *absl::FindCommandLineFlag("int_alias_flag")), + "CustomIntAlias"); + EXPECT_EQ(flags::PrivateHandleAccessor::TypeName( + *absl::FindCommandLineFlag("string_list_alias_flag")), + "CustomStringListAlias"); + EXPECT_EQ(flags::PrivateHandleAccessor::TypeName( + *absl::FindCommandLineFlag("custom_udt_flag")), + "CustomFlagUDT"); + + EXPECT_EQ(flags::PrivateHandleAccessor::TypeName( + absl::GetFlagReflectionHandle(kCompat4ArgFlag)), + ""); + EXPECT_EQ(flags::PrivateHandleAccessor::TypeName( + absl::GetFlagReflectionHandle(kCompat5ArgCommonFlag)), + "std::string"); + EXPECT_EQ(flags::PrivateHandleAccessor::TypeName( + absl::GetFlagReflectionHandle(kCompat5ArgCustomFlag)), + ""); +} + +TEST(CommandLineFlagStrippedTest, TestStrippedTypeName) { +#if ABSL_FLAGS_STRIP_NAMES + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_int_flag).Name(), ""); + EXPECT_EQ(flags::PrivateHandleAccessor::TypeName( + absl::GetFlagReflectionHandle(FLAGS_int_flag)), + ""); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_custom_udt_flag).Name(), ""); + EXPECT_EQ(flags::PrivateHandleAccessor::TypeName( + absl::GetFlagReflectionHandle(FLAGS_custom_udt_flag)), + ""); +#endif +} + } // namespace
diff --git a/absl/flags/flag.h b/absl/flags/flag.h index 2e9a9e6..ea2b889 100644 --- a/absl/flags/flag.h +++ b/absl/flags/flag.h
@@ -207,6 +207,9 @@ #define ABSL_FLAG_IMPL_FLAGNAME(txt) "" #define ABSL_FLAG_IMPL_TYPENAME(txt) "" #define ABSL_FLAG_IMPL_FILENAME() "" +#define ABSL_FLAG_IMPL_NAME_ARG(name) \ + absl::flags_internal::FlagNameArg{"", absl::flags_internal::kEmptyFlagTypeId} +#define ABSL_FLAG_IMPL_DECLARE_NAME_METHODS(name, Type) #define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ absl::flags_internal::FlagRegistrar<T, false>(ABSL_FLAG_IMPL_FLAG_PTR(flag), \ nullptr) @@ -214,6 +217,18 @@ #define ABSL_FLAG_IMPL_FLAGNAME(txt) txt #define ABSL_FLAG_IMPL_TYPENAME(txt) txt #define ABSL_FLAG_IMPL_FILENAME() __FILE__ +#define ABSL_FLAG_IMPL_NAME_ARG(name) \ + absl::flags_internal::MakeFlagNameArg<AbslFlagDefaultGenFor##name>() +#define ABSL_FLAG_IMPL_DECLARE_NAME_METHODS(name, Type) \ + static constexpr const char* AbslFlagName() { \ + return #name; \ + } \ + static constexpr absl::string_view AbslFlagTypeName() { \ + return absl::string_view(#Type, sizeof(#Type) - 1); \ + } \ + static constexpr const char* AbslFlagPackedNameAndType() { \ + return #name "\0" #Type; \ + } #define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ absl::flags_internal::FlagRegistrar<T, true>(ABSL_FLAG_IMPL_FLAG_PTR(flag), \ __FILE__) @@ -241,7 +256,7 @@ /* default value argument. That keeps temporaries alive */ \ /* long enough for NonConst to work correctly. */ \ static constexpr absl::string_view Value( \ - absl::string_view absl_flag_help ABSL_ATTRIBUTE_LIFETIME_BOUND = \ + absl::string_view absl_flag_help ABSL_ATTRIBUTE_LIFETIME_BOUND = \ ABSL_FLAG_IMPL_FLAGHELP(txt)) { \ return absl_flag_help; \ } \ @@ -258,6 +273,7 @@ static void Gen(void* absl_flag_default_loc) { \ new (absl_flag_default_loc) Type(AbslFlagDefaultGenFor##name{}.value); \ } \ + ABSL_FLAG_IMPL_DECLARE_NAME_METHODS(name, Type) \ }; // ABSL_FLAG_IMPL @@ -265,17 +281,16 @@ // Note: Name of registrar object is not arbitrary. It is used to "grab" // global name for FLAGS_no<flag_name> symbol, thus preventing the possibility // of defining two flags with names foo and nofoo. -#define ABSL_FLAG_IMPL(Type, name, default_value, help) \ - extern ::absl::Flag<Type> FLAGS_##name; \ - namespace absl /* block flags in namespaces */ {} \ - ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ - ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help) \ - ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name{ \ - ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_TYPENAME(#Type), \ - ABSL_FLAG_IMPL_FILENAME(), ABSL_FLAG_IMPL_HELP_ARG(name), \ - ABSL_FLAG_IMPL_DEFAULT_ARG(Type, name)}; \ - extern absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name; \ - absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name = \ +#define ABSL_FLAG_IMPL(Type, name, default_value, help) \ + extern ::absl::Flag<Type> FLAGS_##name; \ + namespace absl /* block flags in namespaces */ {} \ + ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ + ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help) \ + ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name{ \ + ABSL_FLAG_IMPL_NAME_ARG(name), ABSL_FLAG_IMPL_FILENAME(), \ + ABSL_FLAG_IMPL_HELP_ARG(name), ABSL_FLAG_IMPL_DEFAULT_ARG(Type, name)}; \ + extern absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name; \ + absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name = \ ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name) // ABSL_RETIRED_FLAG
diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc index 5cacf64..5990e10 100644 --- a/absl/flags/flag_test.cc +++ b/absl/flags/flag_test.cc
@@ -137,6 +137,17 @@ flags::FlagValueStorageKind::kSequenceLocked); EXPECT_EQ(flags::StorageKind<absl::uint128>(), flags::FlagValueStorageKind::kSequenceLocked); + + // These sizes are not hard limits, but we want to keep per-flag size as + // small as possible because production binaries may define thousands of + // flags. + if (sizeof(void*) == 8) { + EXPECT_EQ(sizeof(flags::FlagImpl), 72); + EXPECT_EQ(sizeof(absl::Flag<bool>), 80); + EXPECT_EQ(sizeof(absl::Flag<int>), 80); + EXPECT_EQ(sizeof(absl::Flag<int64_t>), 80); + EXPECT_EQ(sizeof(absl::Flag<double>), 80); + } } // -------------------------------------------------------------------- @@ -151,9 +162,9 @@ #define DEFINE_CONSTRUCTED_FLAG(T, dflt, dflt_kind) \ constexpr flags::FlagDefaultArg f1default##T{ \ flags::FlagDefaultSrc{dflt}, flags::FlagDefaultKind::dflt_kind}; \ - constexpr absl::Flag<T> f1##T{"f1", #T, "file", help_arg, f1default##T}; \ + constexpr absl::Flag<T> f1##T{"f1", "file", help_arg, f1default##T}; \ ABSL_CONST_INIT absl::Flag<T> f2##T { \ - "f2", #T, "file", \ + "f2", "file", \ {flags::FlagHelpMsg(&TestHelpMsg), flags::FlagHelpKind::kGenFunc}, \ flags::FlagDefaultArg { \ flags::FlagDefaultSrc(&TestMakeDflt<T>), \ @@ -175,6 +186,11 @@ DEFINE_CONSTRUCTED_FLAG(int128, 13, kGenFunc); DEFINE_CONSTRUCTED_FLAG(uint128, 14, kGenFunc); +constexpr absl::Flag<String> f_compat_5arg{ + "f_compat", "std::string", "file", help_arg, + flags::FlagDefaultArg{flags::FlagDefaultSrc(&TestMakeDflt<String>), + flags::FlagDefaultKind::kGenFunc}}; + template <typename T> bool TestConstructionFor(const absl::Flag<T>& f1, absl::Flag<T>& f2) { EXPECT_EQ(absl::GetFlagReflectionHandle(f1).Name(), "f1"); @@ -207,6 +223,10 @@ TEST_CONSTRUCTED_FLAG(UDT); TEST_CONSTRUCTED_FLAG(int128); TEST_CONSTRUCTED_FLAG(uint128); + EXPECT_EQ(absl::GetFlagReflectionHandle(f_compat_5arg).Name(), "f_compat"); + EXPECT_EQ(absl::GetFlagReflectionHandle(f_compat_5arg).Help(), + "literal help"); + EXPECT_EQ(absl::GetFlagReflectionHandle(f_compat_5arg).Filename(), "file"); } // --------------------------------------------------------------------
diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc index 662b342..d32992a 100644 --- a/absl/flags/internal/flag.cc +++ b/absl/flags/internal/flag.cc
@@ -39,6 +39,7 @@ #include "absl/base/no_destructor.h" #include "absl/base/optimization.h" #include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_map.h" #include "absl/flags/config.h" #include "absl/flags/internal/commandlineflag.h" #include "absl/flags/usage_config.h" @@ -104,6 +105,55 @@ s_freelist->push_back(p); } +template <typename K, typename V> +class ThreadSafeMap { + public: + V Get(const K& key) { + absl::MutexLock l(mutex_); + auto it = map_.find(key); + return it != map_.end() ? it->second : V{}; + } + + V Set(const K& key, V new_val) { + absl::MutexLock l(mutex_); + return std::exchange(map_[key], new_val); + } + + template <typename Factory> + V GetOrCreate(const K& key, Factory factory) { + absl::MutexLock l(mutex_); + V& val = map_[key]; + if (val == V{}) { + val = factory(); + } + return val; + } + + private: + absl::Mutex mutex_; + absl::flat_hash_map<K, V> map_ ABSL_GUARDED_BY(mutex_); +}; + +FlagCallback* GetOrCreateCallback(const FlagImpl* flag) { + static absl::NoDestructor<ThreadSafeMap<const FlagImpl*, FlagCallback*>> + callbacks; + return callbacks->GetOrCreate(flag, [] { return new FlagCallback; }); +} + +ThreadSafeMap<const FlagImpl*, void*>& DynamicDefaults() { + static absl::NoDestructor<ThreadSafeMap<const FlagImpl*, void*>> + dynamic_defaults; + return *dynamic_defaults; +} + +void* GetDynamicDefault(const FlagImpl* flag) { + return DynamicDefaults().Get(flag); +} + +void* SetDynamicDefault(const FlagImpl* flag, void* new_val) { + return DynamicDefaults().Set(flag, new_val); +} + } // namespace /////////////////////////////////////////////////////////////////////////////// @@ -296,7 +346,7 @@ void* res = nullptr; switch (DefaultKind()) { case FlagDefaultKind::kDynamicValue: - res = flags_internal::Clone(op_, default_value_.dynamic_value); + res = flags_internal::Clone(op_, GetDynamicDefault(this)); break; case FlagDefaultKind::kGenFunc: res = flags_internal::Alloc(op_); @@ -354,7 +404,12 @@ absl::string_view FlagImpl::Name() const { return name_; } -absl::string_view FlagImpl::TypeName() const { return type_name_; } +absl::string_view FlagImpl::TypeName() const { + if (ABSL_PREDICT_TRUE(type_id_ != kCustomFlagTypeId)) { + return kCommonFlagTypeNames[type_id_]; + } + return name_ + std::strlen(name_) + 1; +} std::string FlagImpl::Filename() const { return flags_internal::GetUsageConfig().normalize_filename(filename_); @@ -414,20 +469,20 @@ void FlagImpl::SetCallback(const FlagCallbackFunc mutation_callback) { absl::MutexLock l(DataGuard()); - if (callback_ == nullptr) { - callback_ = new FlagCallback; - } - callback_->func = mutation_callback; + FlagCallback* cb = GetOrCreateCallback(this); + cb->func = mutation_callback; + has_callback_ = true; InvokeCallback(); } void FlagImpl::InvokeCallback() const { - if (!callback_) return; + if (!has_callback_) return; + FlagCallback* cb_entry = GetOrCreateCallback(this); // Make a copy of the C-style function pointer that we are about to invoke // before we release the lock guarding it. - FlagCallbackFunc cb = callback_->func; + FlagCallbackFunc cb = cb_entry->func; // If the flag has a mutation callback this function invokes it. While the // callback is being invoked the primary flag's mutex is unlocked and it is @@ -441,7 +496,7 @@ // completed. Requires that *primary_lock be held in exclusive mode; it may be // released and reacquired by the implementation. MutexRelock relock(DataGuard()); - absl::MutexLock lock(callback_->guard); + absl::MutexLock lock(cb_entry->guard); cb(); } @@ -712,18 +767,17 @@ auto tentative_value = TryParse(value, err); if (!tentative_value) return false; + void* new_default = tentative_value.release(); + void* old_value = SetDynamicDefault(this, new_default); if (DefaultKind() == FlagDefaultKind::kDynamicValue) { - void* old_value = default_value_.dynamic_value; - default_value_.dynamic_value = tentative_value.release(); tentative_value.reset(old_value); } else { - default_value_.dynamic_value = tentative_value.release(); def_kind_ = static_cast<uint8_t>(FlagDefaultKind::kDynamicValue); } if (!modified_) { // Need to set both default value *and* current, in this case. - StoreValue(default_value_.dynamic_value, source); + StoreValue(new_default, source); modified_ = false; } break;
diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h index c12c924..6793202 100644 --- a/absl/flags/internal/flag.h +++ b/absl/flags/internal/flag.h
@@ -21,6 +21,7 @@ #include <atomic> #include <cstring> +#include <iterator> #include <memory> #include <string> #include <type_traits> @@ -166,7 +167,7 @@ } /////////////////////////////////////////////////////////////////////////////// -// Flag help auxiliary structs. +// Flag help and name/type-name auxiliary structs. // This is help argument for absl::Flag encapsulating the string literal pointer // or pointer to function generating it as well as enum discriminating two @@ -184,6 +185,81 @@ } }; +inline constexpr uint8_t kEmptyFlagTypeId = 0; +inline constexpr uint8_t kCustomFlagTypeId = 31; + +// Common macro-stringified `#Type` spellings stored as a 5-bit ID in `FlagImpl` +// so common flags avoid emitting a per-flag type-name string. Entry 0 (`""`) is +// used when flag names are stripped or omitted; ID 31 (`kCustomFlagTypeId`) +// indicates that `name_` points to a packed `"<name>\0<type_name>\0"` array. +inline constexpr absl::string_view kCommonFlagTypeNames[] = { + "", + "bool", + "int", + "int16_t", + "uint16_t", + "int32_t", + "uint32_t", + "int64_t", + "uint64_t", + "size_t", + "float", + "double", + "std::string", + "string", + "std::vector<std::string>", + "absl::Duration", + "absl::Time", + "absl::int128", + "absl::uint128", + "std::int32_t", + "std::int64_t", + "std::optional<bool>", + "std::optional<int>", + "std::optional<int32_t>", + "std::optional<uint32_t>", + "std::optional<int64_t>", + "std::optional<uint64_t>", + "std::optional<size_t>", + "std::optional<double>", + "std::optional<std::string>", + "std::optional<absl::Duration>", +}; +static_assert(std::size(kCommonFlagTypeNames) == kCustomFlagTypeId, + "kCommonFlagTypeNames must have kCustomFlagTypeId entries"); + +constexpr uint8_t LookupCommonFlagTypeId(absl::string_view type_name) { + for (size_t i = 0; i < kCustomFlagTypeId; ++i) { + if (kCommonFlagTypeNames[i] == type_name) { + return static_cast<uint8_t>(i); + } + } + return kCustomFlagTypeId; +} + +constexpr uint8_t LookupCommonOrEmptyFlagTypeId(const char* type_name) { + if (type_name == nullptr) return kEmptyFlagTypeId; + size_t len = 0; + while (type_name[len] != '\0') ++len; + const uint8_t id = LookupCommonFlagTypeId(absl::string_view(type_name, len)); + return id == kCustomFlagTypeId ? kEmptyFlagTypeId : id; +} + +struct FlagNameArg { + const char* name; + uint8_t type_id; +}; + +template <typename Gen> +constexpr FlagNameArg MakeFlagNameArg() { + constexpr uint8_t kTypeId = LookupCommonFlagTypeId(Gen::AbslFlagTypeName()); + if constexpr (kTypeId != kCustomFlagTypeId) { + return {Gen::AbslFlagName(), kTypeId}; + } else { + return {Gen::AbslFlagPackedNameAndType(), kCustomFlagTypeId}; + } +} + template <typename Gen, size_t N = Gen::Value().size()> constexpr FixedCharArray<N + 1> HelpStringAsArray(int) { return FixedCharArray<N + 1>::FromLiteralString( @@ -581,21 +657,20 @@ #endif class FlagImpl final : public CommandLineFlag { public: - constexpr FlagImpl(const char* name, const char* type_name, - const char* filename, FlagOpFn op, FlagHelpArg help, - FlagValueStorageKind value_kind, + constexpr FlagImpl(FlagNameArg name, const char* filename, FlagOpFn op, + FlagHelpArg help, FlagValueStorageKind value_kind, FlagDefaultArg default_arg) - : name_(name), - type_name_(type_name), + : name_(name.name), filename_(filename), op_(op), help_(help.source), help_source_kind_(static_cast<uint8_t>(help.kind)), value_storage_kind_(static_cast<uint8_t>(value_kind)), + type_id_(name.type_id), def_kind_(static_cast<uint8_t>(default_arg.kind)), modified_(false), on_command_line_(false), - callback_(nullptr), + has_callback_(false), default_value_(default_arg.source), data_guard_{} {} @@ -731,12 +806,10 @@ // Immutable flag's state. - // Flags name passed to ABSL_FLAG as second arg. + // Flags name passed to ABSL_FLAG as second arg (followed by NUL-terminated + // type name when `type_id_ == kCustomFlagTypeId`). const char* const name_; - // Flags type passed to ABSL_FLAG as first arg. - const char* const type_name_; - // The file name where ABSL_FLAG resides. const char* const filename_; // Type-specific operations vtable. @@ -747,6 +820,9 @@ const uint8_t help_source_kind_ : 1; // Kind of storage this flag is using for the flag's value. const uint8_t value_storage_kind_ : 2; + // Index into `kCommonFlagTypeNames`, or `kCustomFlagTypeId` when `name_` + // stores `"<name>\0<type_name>\0"`. + const uint8_t type_id_ : 5; uint8_t : 0; // The bytes containing the const bitfields must not be // shared with bytes containing the mutable bitfields. @@ -760,6 +836,8 @@ bool modified_ : 1 ABSL_GUARDED_BY(DataGuard()); // Has this flag been specified on command line. bool on_command_line_ : 1 ABSL_GUARDED_BY(DataGuard()); + // Has a mutation callback been registered on this flag? + bool has_callback_ : 1 ABSL_GUARDED_BY(DataGuard()); // Unique tag for absl::call_once call to initialize this flag. absl::once_flag init_control_; @@ -767,13 +845,9 @@ // Sequence lock / mutation counter. flags_internal::SequenceLock seq_lock_; - // Optional flag's callback and absl::Mutex to guard the invocations. - FlagCallback* callback_ ABSL_GUARDED_BY(DataGuard()); - // Either a pointer to the function generating the default value based on the - // value specified in ABSL_FLAG or pointer to the dynamically set default - // value via SetCommandLineOptionWithMode. def_kind_ is used to distinguish - // these two cases. - FlagDefaultSrc default_value_; + // Initial default value source specified in ABSL_FLAG. Dynamic default + // overrides set via SET_FLAGS_DEFAULT are stored in a side table. + const FlagDefaultSrc default_value_; // This is reserved space for an absl::Mutex to guard flag data. It will be // initialized in FlagImpl::Init via placement new. @@ -803,11 +877,21 @@ template <typename T> class Flag { public: - constexpr Flag(const char* name, const char* type_name, const char* filename, - FlagHelpArg help, const FlagDefaultArg default_arg) - : impl_(name, type_name, filename, &FlagOps<T>, help, + constexpr Flag(FlagNameArg name, const char* filename, FlagHelpArg help, + const FlagDefaultArg default_arg) + : impl_(name, filename, &FlagOps<T>, help, flags_internal::StorageKind<T>(), default_arg), value_() {} + constexpr Flag(const char* name, const char* filename, FlagHelpArg help, + const FlagDefaultArg default_arg) + : Flag(FlagNameArg{name, kEmptyFlagTypeId}, filename, help, default_arg) { + } + // Kept for backwards compatibility. + constexpr Flag(const char* name, const char* type_name, + const char* filename, FlagHelpArg help, + const FlagDefaultArg default_arg) + : Flag(FlagNameArg{name, LookupCommonOrEmptyFlagTypeId(type_name)}, + filename, help, default_arg) {} // CommandLineFlag interface absl::string_view Name() const { return impl_.Name(); }