diff options
Diffstat (limited to 'absl/flags')
-rw-r--r-- | absl/flags/flag.h | 57 | ||||
-rw-r--r-- | absl/flags/flag_test.cc | 17 | ||||
-rw-r--r-- | absl/flags/internal/commandlineflag.cc | 7 | ||||
-rw-r--r-- | absl/flags/internal/commandlineflag.h | 47 | ||||
-rw-r--r-- | absl/flags/internal/flag.cc | 5 | ||||
-rw-r--r-- | absl/flags/internal/flag.h | 89 | ||||
-rw-r--r-- | absl/flags/internal/registry.cc | 6 |
7 files changed, 136 insertions, 92 deletions
diff --git a/absl/flags/flag.h b/absl/flags/flag.h index 356ddb616072..09af47a06d1b 100644 --- a/absl/flags/flag.h +++ b/absl/flags/flag.h @@ -96,8 +96,7 @@ class Flag { constexpr Flag(const char* name, const flags_internal::HelpGenFunc help_gen, const char* filename, const flags_internal::FlagMarshallingOpFn marshalling_op, - const flags_internal::InitialValGenFunc initial_value_gen, - bool, void*) + const flags_internal::InitialValGenFunc initial_value_gen) : name_(name), help_gen_(help_gen), filename_(filename), @@ -115,8 +114,11 @@ class Flag { return impl_; } - impl_ = new flags_internal::Flag<T>(name_, help_gen_, filename_, - marshalling_op_, initial_value_gen_); + impl_ = new flags_internal::Flag<T>( + name_, + {flags_internal::FlagHelpSrc(help_gen_), + flags_internal::FlagHelpSrcKind::kGenFunc}, + filename_, marshalling_op_, initial_value_gen_); inited_.store(true, std::memory_order_release); } @@ -307,9 +309,19 @@ void SetFlag(absl::Flag<T>* flag, const V& v) { #define ABSL_FLAG_IMPL_FLAGHELP(txt) txt #endif -#define ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, txt) \ - static std::string AbslFlagsWrapHelp##name() { \ - return ABSL_FLAG_IMPL_FLAGHELP(txt); \ +// AbslFlagHelpGenFor##name is used to encapsulate both immediate (method Const) +// and lazy (method NonConst) evaluation of help message expression. We choose +// between the two via the call to HelpArg in absl::Flag instantiation below. +// If help message expression is constexpr evaluable compiler will optimize +// away this whole struct. +#define ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, txt) \ + struct AbslFlagHelpGenFor##name { \ + template <typename T = void> \ + static constexpr const char* Const() { \ + return absl::flags_internal::HelpConstexprWrap( \ + ABSL_FLAG_IMPL_FLAGHELP(txt)); \ + } \ + static std::string NonConst() { return ABSL_FLAG_IMPL_FLAGHELP(txt); } \ } #define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ @@ -326,29 +338,28 @@ void SetFlag(absl::Flag<T>* flag, const V& v) { #define ABSL_FLAG_IMPL(Type, name, default_value, help) \ 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_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help); \ ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name{ \ - ABSL_FLAG_IMPL_FLAGNAME(#name), &AbslFlagsWrapHelp##name, \ + ABSL_FLAG_IMPL_FLAGNAME(#name), \ + absl::flags_internal::HelpArg<AbslFlagHelpGenFor##name>(0), \ ABSL_FLAG_IMPL_FILENAME(), \ &absl::flags_internal::FlagMarshallingOps<Type>, \ &AbslFlagsInitFlag##name}; \ extern bool FLAGS_no##name; \ bool FLAGS_no##name = ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name) #else -// MSVC version uses aggregate initialization. -#define ABSL_FLAG_IMPL(Type, name, default_value, help) \ - 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), \ - &AbslFlagsWrapHelp##name, \ - ABSL_FLAG_IMPL_FILENAME(), \ - &absl::flags_internal::FlagMarshallingOps<Type>, \ - &AbslFlagsInitFlag##name, \ - false, \ - nullptr}; \ - extern bool FLAGS_no##name; \ +// MSVC version uses aggregate initialization. We also do not try to +// optimize away help wrapper. +#define ABSL_FLAG_IMPL(Type, name, default_value, help) \ + 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), &AbslFlagHelpGenFor##name::NonConst, \ + ABSL_FLAG_IMPL_FILENAME(), \ + &absl::flags_internal::FlagMarshallingOps<Type>, \ + &AbslFlagsInitFlag##name}; \ + extern bool FLAGS_no##name; \ bool FLAGS_no##name = ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name) #endif diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc index 59dc579ce029..4e08da8589a5 100644 --- a/absl/flags/flag_test.cc +++ b/absl/flags/flag_test.cc @@ -32,7 +32,7 @@ namespace { namespace flags = absl::flags_internal; -std::string TestHelpMsg() { return "help"; } +std::string TestHelpMsg() { return "dynamic help"; } template <typename T> void* TestMakeDflt() { return new T{}; @@ -41,19 +41,22 @@ void TestCallback() {} template <typename T> bool TestConstructionFor() { - constexpr flags::Flag<T> f1("f1", &TestHelpMsg, "file", + constexpr flags::HelpInitArg help_arg{flags::FlagHelpSrc("literal help"), + flags::FlagHelpSrcKind::kLiteral}; + constexpr flags::Flag<T> f1("f1", help_arg, "file", &flags::FlagMarshallingOps<T>, &TestMakeDflt<T>); EXPECT_EQ(f1.Name(), "f1"); - EXPECT_EQ(f1.Help(), "help"); + EXPECT_EQ(f1.Help(), "literal help"); EXPECT_EQ(f1.Filename(), "file"); - ABSL_CONST_INIT static flags::Flag<T> f2("f2", &TestHelpMsg, "file", - &flags::FlagMarshallingOps<T>, - &TestMakeDflt<T>); + ABSL_CONST_INIT static flags::Flag<T> f2( + "f2", + {flags::FlagHelpSrc(&TestHelpMsg), flags::FlagHelpSrcKind::kGenFunc}, + "file", &flags::FlagMarshallingOps<T>, &TestMakeDflt<T>); flags::FlagRegistrar<T, false>(&f2).OnUpdate(TestCallback); EXPECT_EQ(f2.Name(), "f2"); - EXPECT_EQ(f2.Help(), "help"); + EXPECT_EQ(f2.Help(), "dynamic help"); EXPECT_EQ(f2.Filename(), "file"); return true; diff --git a/absl/flags/internal/commandlineflag.cc b/absl/flags/internal/commandlineflag.cc index caf33bc4892b..88f91e16d196 100644 --- a/absl/flags/internal/commandlineflag.cc +++ b/absl/flags/internal/commandlineflag.cc @@ -56,12 +56,5 @@ std::string CommandLineFlag::Filename() const { return flags_internal::GetUsageConfig().normalize_filename(filename_); } -std::string HelpText::GetHelpText() const { - if (help_function_) return help_function_(); - if (help_message_) return help_message_; - - return {}; -} - } // namespace flags_internal } // namespace absl diff --git a/absl/flags/internal/commandlineflag.h b/absl/flags/internal/commandlineflag.h index 13a3352e80f4..7964f1bf8c4a 100644 --- a/absl/flags/internal/commandlineflag.h +++ b/absl/flags/internal/commandlineflag.h @@ -60,14 +60,6 @@ enum ValueSource { kProgrammaticChange, }; -// Signature for the help generation function used as an argument for the -// absl::Flag constructor. -using HelpGenFunc = std::string (*)(); - -// Signature for the function generating the initial flag value based (usually -// based on default value supplied in flag's definition) -using InitialValGenFunc = void* (*)(); - extern const char kStrippedFlagHelp[]; // The per-type function @@ -149,34 +141,6 @@ inline size_t Sizeof(FlagOpFn op) { op(flags_internal::kSizeof, nullptr, nullptr))); } -// Holds either a pointer to help text or a function which produces it. This is -// needed for supporting both static initialization of Flags while supporting -// the legacy registration framework. We can't use absl::variant<const char*, -// const char*(*)()> since anybody passing 0 or nullptr in to a CommandLineFlag -// would find an ambiguity. -class HelpText { - public: - static constexpr HelpText FromFunctionPointer(const HelpGenFunc fn) { - return HelpText(fn, nullptr); - } - static constexpr HelpText FromStaticCString(const char* msg) { - return HelpText(nullptr, msg); - } - - std::string GetHelpText() const; - - HelpText() = delete; - HelpText(const HelpText&) = default; - HelpText(HelpText&&) = default; - - private: - explicit constexpr HelpText(const HelpGenFunc fn, const char* msg) - : help_function_(fn), help_message_(msg) {} - - HelpGenFunc help_function_; - const char* help_message_; -}; - // Handle to FlagState objects. Specific flag state objects will restore state // of a flag produced this flag state from method CommandLineFlag::SaveState(). class FlagStateInterface { @@ -190,9 +154,8 @@ class FlagStateInterface { // Holds all information for a flag. class CommandLineFlag { public: - constexpr CommandLineFlag(const char* name, HelpText help_text, - const char* filename) - : name_(name), help_(help_text), filename_(filename) {} + constexpr CommandLineFlag(const char* name, const char* filename) + : name_(name), filename_(filename) {} // Virtual destructor virtual void Destroy() const = 0; @@ -203,7 +166,6 @@ class CommandLineFlag { // Non-polymorphic access methods. absl::string_view Name() const { return name_; } - std::string Help() const { return help_.GetHelpText(); } absl::string_view Typename() const; std::string Filename() const; @@ -250,6 +212,8 @@ class CommandLineFlag { // Polymorphic access methods + // Returns help message associated with this flag + virtual std::string Help() const = 0; // Returns true iff this object corresponds to retired flag virtual bool IsRetired() const { return false; } // Returns true iff this is a handle to an Abseil Flag. @@ -285,12 +249,11 @@ class CommandLineFlag { // flag's value type. virtual void CheckDefaultValueParsingRoundtrip() const = 0; - // Constant configuration for a particular flag. protected: ~CommandLineFlag() = default; + // Constant configuration for a particular 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. private: diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc index 061113d76557..d087f79e891c 100644 --- a/absl/flags/internal/flag.cc +++ b/absl/flags/internal/flag.cc @@ -85,6 +85,11 @@ void FlagImpl::Destroy() const { delete locks_; } +std::string FlagImpl::Help() const { + return help_source_kind_ == FlagHelpSrcKind::kLiteral ? help_.literal + : help_.gen_func(); +} + bool FlagImpl::IsModified() const { absl::MutexLock l(DataGuard()); return modified_; diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h index ce0ccf2d31d2..d79902543a22 100644 --- a/absl/flags/internal/flag.h +++ b/absl/flags/internal/flag.h @@ -61,6 +61,68 @@ class FlagState : public flags_internal::FlagStateInterface { int64_t counter_; }; +// This is help argument for absl::Flag encapsulating the string literal pointer +// or pointer to function generating it as well as enum descriminating two +// cases. +using HelpGenFunc = std::string (*)(); + +union FlagHelpSrc { + constexpr explicit FlagHelpSrc(const char* help_msg) : literal(help_msg) {} + constexpr explicit FlagHelpSrc(HelpGenFunc help_gen) : gen_func(help_gen) {} + + const char* literal; + HelpGenFunc gen_func; +}; + +enum class FlagHelpSrcKind : int8_t { kLiteral, kGenFunc }; + +struct HelpInitArg { + FlagHelpSrc source; + FlagHelpSrcKind kind; +}; + +// HelpConstexprWrap is used by struct AbslFlagHelpGenFor##name generated by +// ABSL_FLAG macro. It is only used to silence the compiler in the case where +// help message expression is not constexpr and does not have type const char*. +// If help message expression is indeed constexpr const char* HelpConstexprWrap +// is just a trivial identity function. +template <typename T> +const char* HelpConstexprWrap(const T&) { + return nullptr; +} +constexpr const char* HelpConstexprWrap(const char* p) { return p; } +constexpr const char* HelpConstexprWrap(char* p) { return p; } + +// These two HelpArg overloads allows us to select at compile time one of two +// way to pass Help argument to absl::Flag. We'll be passing +// AbslFlagHelpGenFor##name as T and integer 0 as a single argument to prefer +// first overload if possible. If T::Const is evaluatable on constexpr +// context (see non template int parameter below) we'll choose first overload. +// In this case the help message expression is immediately evaluated and is used +// to construct the absl::Flag. No additionl code is generated by ABSL_FLAG. +// Otherwise SFINAE kicks in and first overload is dropped from the +// consideration, in which case the second overload will be used. The second +// overload does not attempt to evaluate the help message expression +// immediately and instead delays the evaluation by returing the function +// pointer (&T::NonConst) genering the help message when necessary. This is +// evaluatable in constexpr context, but the cost is an extra function being +// generated in the ABSL_FLAG code. +template <typename T, int = (T::Const(), 1)> +constexpr flags_internal::HelpInitArg HelpArg(int) { + return {flags_internal::FlagHelpSrc(T::Const()), + flags_internal::FlagHelpSrcKind::kLiteral}; +} + +template <typename T> +constexpr flags_internal::HelpInitArg HelpArg(char) { + return {flags_internal::FlagHelpSrc(&T::NonConst), + flags_internal::FlagHelpSrcKind::kGenFunc}; +} + +// Signature for the function generating the initial flag value based (usually +// based on default value supplied in flag's definition) +using InitialValGenFunc = void* (*)(); + // Signature for the mutation callback used by watched Flags // The callback is noexcept. // TODO(rogeeff): add noexcept after C++17 support is added. @@ -74,15 +136,19 @@ class FlagImpl { public: constexpr FlagImpl(const flags_internal::FlagOpFn op, const flags_internal::FlagMarshallingOpFn marshalling_op, - const flags_internal::InitialValGenFunc initial_value_gen) + const flags_internal::InitialValGenFunc initial_value_gen, + const HelpInitArg help) : op_(op), marshalling_op_(marshalling_op), - initial_value_gen_(initial_value_gen) {} + initial_value_gen_(initial_value_gen), + help_(help.source), + help_source_kind_(help.kind) {} // Forces destruction of the Flag's data. void Destroy() const; // Constant access methods + std::string Help() const; 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); @@ -149,9 +215,12 @@ class FlagImpl { 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 + const FlagOpFn op_; // Type-specific handler. + const FlagMarshallingOpFn marshalling_op_; // Marshalling ops handler. + const InitialValGenFunc initial_value_gen_; // Makes flag's initial value. + const FlagHelpSrc help_; // Help message literal or function to generate it. + // Indicates if help message was supplied as literal or generator func. + const FlagHelpSrcKind help_source_kind_; // Mutable Flag's data. (guarded by locks_->primary_mu). // Indicates that locks_, cur_ and def_ fields have been lazily initialized. @@ -191,14 +260,13 @@ class FlagImpl { template <typename T> class Flag final : public flags_internal::CommandLineFlag { public: - constexpr Flag(const char* name, const flags_internal::HelpGenFunc help_gen, + constexpr Flag(const char* name, const flags_internal::HelpInitArg help, const char* filename, const flags_internal::FlagMarshallingOpFn marshalling_op, const flags_internal::InitialValGenFunc initial_value_gen) - : flags_internal::CommandLineFlag( - name, flags_internal::HelpText::FromFunctionPointer(help_gen), - filename), - impl_(&flags_internal::FlagOps<T>, marshalling_op, initial_value_gen) {} + : flags_internal::CommandLineFlag(name, filename), + impl_(&flags_internal::FlagOps<T>, marshalling_op, initial_value_gen, + help) {} T Get() const { // See implementation notes in CommandLineFlag::Get(). @@ -222,6 +290,7 @@ class Flag final : public flags_internal::CommandLineFlag { } // CommandLineFlag interface + std::string Help() const override { return impl_.Help(); } bool IsModified() const override { return impl_.IsModified(); } bool IsSpecifiedOnCommandLine() const override { return impl_.IsSpecifiedOnCommandLine(); diff --git a/absl/flags/internal/registry.cc b/absl/flags/internal/registry.cc index ae7671a967e8..52d9f3c716f3 100644 --- a/absl/flags/internal/registry.cc +++ b/absl/flags/internal/registry.cc @@ -276,9 +276,8 @@ namespace { class RetiredFlagObj final : public flags_internal::CommandLineFlag { public: constexpr RetiredFlagObj(const char* name, FlagOpFn ops) - : flags_internal::CommandLineFlag( - name, flags_internal::HelpText::FromStaticCString(nullptr), - /*filename=*/"RETIRED"), + : flags_internal::CommandLineFlag(name, + /*filename=*/"RETIRED"), op_(ops) {} private: @@ -288,6 +287,7 @@ class RetiredFlagObj final : public flags_internal::CommandLineFlag { } flags_internal::FlagOpFn TypeId() const override { return op_; } + std::string Help() const override { return ""; } bool IsRetired() const override { return true; } bool IsModified() const override { return false; } bool IsSpecifiedOnCommandLine() const override { return false; } |