From cf1a02e2dc5a1bc9d095f4c996306de448ca200f Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 20 May 2020 00:23:37 -0700 Subject: [PATCH] Export of internal Abseil changes -- 30e5e00a54cabc50118a3e1055826ea02a0d32e4 by Gennadiy Rozental : Move flag help into flag_cold section. To facilitate this we set flag help not in a constructor, but during flag registration. This allows us to place flag name as static variable inside non constexpr lambda and invoke it immediately in FlagRegistrar argument location. PiperOrigin-RevId: 312432625 -- efd91b05152b68df648cff5ba38e9669a817c12f by Gennadiy Rozental : Make CommandLineFlag public. PiperOrigin-RevId: 312404666 -- dc7f5fb73487766fa8a76d6b97d28116e5334445 by Mark Barolak : Internal change. PiperOrigin-RevId: 312297164 GitOrigin-RevId: 30e5e00a54cabc50118a3e1055826ea02a0d32e4 Change-Id: Ic46ab011bb804645264572caddff0becba5f9170 --- absl/flags/BUILD.bazel | 126 ++++++------ absl/flags/CMakeLists.txt | 98 ++++----- absl/flags/{internal => }/commandlineflag.cc | 8 +- absl/flags/commandlineflag.h | 190 ++++++++++++++++++ .../{internal => }/commandlineflag_test.cc | 18 +- absl/flags/flag.cc | 2 - absl/flags/flag.h | 91 ++++----- absl/flags/flag_test.cc | 1 - absl/flags/internal/commandlineflag.h | 112 ----------- absl/flags/internal/flag.cc | 1 - absl/flags/internal/flag.h | 62 +++--- absl/flags/internal/private_handle_accessor.h | 2 +- absl/flags/internal/registry.cc | 9 +- absl/flags/internal/registry.h | 2 + absl/flags/internal/type_erased.cc | 2 +- absl/flags/internal/type_erased.h | 2 +- absl/flags/internal/type_erased_test.cc | 1 - absl/flags/internal/usage.cc | 12 +- absl/flags/internal/usage.h | 4 +- absl/flags/parse.cc | 2 +- 20 files changed, 420 insertions(+), 325 deletions(-) rename absl/flags/{internal => }/commandlineflag.cc (95%) create mode 100644 absl/flags/commandlineflag.h rename absl/flags/{internal => }/commandlineflag_test.cc (93%) diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index 36810825..2d6f799e 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel @@ -27,28 +27,18 @@ package(default_visibility = ["//visibility:public"]) licenses(["notice"]) # Apache 2.0 cc_library( - name = "flag_internal", - srcs = [ - "internal/flag.cc", - ], + name = "path_util", hdrs = [ - "internal/flag.h", + "internal/path_util.h", ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = ["//absl/base:__subpackages__"], + visibility = [ + "//absl/flags:__pkg__", + ], deps = [ - ":config", - ":handle", - ":marshalling", - ":registry", - "//absl/base", "//absl/base:config", - "//absl/base:core_headers", - "//absl/memory", - "//absl/meta:type_traits", "//absl/strings", - "//absl/synchronization", ], ) @@ -74,22 +64,6 @@ cc_library( ], ) -cc_library( - name = "path_util", - hdrs = [ - "internal/path_util.h", - ], - copts = ABSL_DEFAULT_COPTS, - linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = [ - "//absl/flags:__pkg__", - ], - deps = [ - "//absl/base:config", - "//absl/strings", - ], -) - cc_library( name = "config", srcs = [ @@ -131,23 +105,33 @@ cc_library( ) cc_library( - name = "handle", - srcs = [ - "internal/commandlineflag.cc", - ], + name = "commandlineflag_internal", hdrs = [ "internal/commandlineflag.h", ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = [ - "//absl/flags:__pkg__", - ], + visibility = ["//visibility:private"], deps = [ "//absl/base:config", "//absl/base:core_headers", "//absl/base:fast_type_id", "//absl/strings", + ], +) + +cc_library( + name = "commandlineflag", + srcs = [ + "commandlineflag.cc", + ], + hdrs = [ + "commandlineflag.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":commandlineflag_internal", "//absl/types:optional", ], ) @@ -165,7 +149,7 @@ cc_library( visibility = [ "//absl/flags:__pkg__", ], - deps = [":handle"], + deps = [":commandlineflag"], ) cc_library( @@ -184,8 +168,9 @@ cc_library( "//absl/flags:__pkg__", ], deps = [ + ":commandlineflag", + ":commandlineflag_internal", ":config", - ":handle", ":private_handle_accessor", "//absl/base:config", "//absl/base:core_headers", @@ -195,6 +180,32 @@ cc_library( ], ) +cc_library( + name = "flag_internal", + srcs = [ + "internal/flag.cc", + ], + hdrs = [ + "internal/flag.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//absl/base:__subpackages__"], + deps = [ + ":commandlineflag", + ":config", + ":marshalling", + ":registry", + "//absl/base", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/memory", + "//absl/meta:type_traits", + "//absl/strings", + "//absl/synchronization", + ], +) + cc_library( name = "flag", srcs = [ @@ -209,7 +220,6 @@ cc_library( deps = [ ":config", ":flag_internal", - ":handle", ":marshalling", ":registry", "//absl/base", @@ -233,10 +243,10 @@ cc_library( "//absl/flags:__pkg__", ], deps = [ + ":commandlineflag", ":config", ":flag", ":flag_internal", - ":handle", ":path_util", ":private_handle_accessor", ":program_name", @@ -276,10 +286,10 @@ cc_library( copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":commandlineflag", ":config", ":flag", ":flag_internal", - ":handle", ":private_handle_accessor", ":program_name", ":registry", @@ -299,14 +309,14 @@ cc_test( name = "commandlineflag_test", size = "small", srcs = [ - "internal/commandlineflag_test.cc", + "commandlineflag_test.cc", ], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":commandlineflag", ":config", ":flag", - ":handle", ":private_handle_accessor", ":registry", "//absl/memory", @@ -342,7 +352,6 @@ cc_test( ":config", ":flag", ":flag_internal", - ":handle", ":registry", "//absl/base:core_headers", "//absl/base:malloc_internal", @@ -384,35 +393,35 @@ cc_test( ) cc_test( - name = "path_util_test", + name = "parse_test", size = "small", srcs = [ - "internal/path_util_test.cc", + "parse_test.cc", ], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":path_util", + ":flag", + ":parse", + ":registry", + "//absl/base:raw_logging_internal", + "//absl/base:scoped_set_env", + "//absl/strings", + "//absl/types:span", "@com_google_googletest//:gtest_main", ], ) cc_test( - name = "parse_test", + name = "path_util_test", size = "small", srcs = [ - "parse_test.cc", + "internal/path_util_test.cc", ], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":flag", - ":parse", - ":registry", - "//absl/base:raw_logging_internal", - "//absl/base:scoped_set_env", - "//absl/strings", - "//absl/types:span", + ":path_util", "@com_google_googletest//:gtest_main", ], ) @@ -442,7 +451,6 @@ cc_test( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":flag", - ":handle", ":marshalling", ":registry", "//absl/memory", diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt index e6b17c9b..6ecf3b48 100644 --- a/absl/flags/CMakeLists.txt +++ b/absl/flags/CMakeLists.txt @@ -17,24 +17,16 @@ # Internal-only target, do not depend on directly. absl_cc_library( NAME - flags_internal - SRCS - "internal/flag.cc" + flags_path_util HDRS - "internal/flag.h" + "internal/path_util.h" COPTS ${ABSL_DEFAULT_COPTS} LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS - absl::base absl::config - absl::flags_config - absl::flags_handle - absl::flags_marshalling - absl::flags_registry - absl::synchronization - absl::meta + absl::strings PUBLIC ) @@ -59,22 +51,6 @@ absl_cc_library( PUBLIC ) -# Internal-only target, do not depend on directly. -absl_cc_library( - NAME - flags_path_util - HDRS - "internal/path_util.h" - COPTS - ${ABSL_DEFAULT_COPTS} - LINKOPTS - ${ABSL_DEFAULT_LINKOPTS} - DEPS - absl::config - absl::strings - PUBLIC -) - absl_cc_library( NAME flags_config @@ -118,9 +94,7 @@ absl_cc_library( # Internal-only target, do not depend on directly. absl_cc_library( NAME - flags_handle - SRCS - "internal/commandlineflag.cc" + flags_commandlineflag_internal HDRS "internal/commandlineflag.h" COPTS @@ -129,12 +103,25 @@ absl_cc_library( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::config - absl::fast_type_id absl::core_headers - absl::optional - absl::raw_logging_internal + absl::fast_type_id absl::strings - absl::synchronization +) + +absl_cc_library( + NAME + flags_commandlineflag + SRCS + "commandlineflag.cc" + HDRS + "commandlineflag.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::flags_commandlineflag_internal + absl::optional ) # Internal-only target, do not depend on directly. @@ -150,7 +137,7 @@ absl_cc_library( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS - absl::flags_handle + absl::flags_commandlineflag ) # Internal-only target, do not depend on directly. @@ -169,8 +156,8 @@ absl_cc_library( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::config + absl::flags_commandlineflag absl::flags_config - absl::flags_handle absl::flags_private_handle_accessor absl::core_headers absl::raw_logging_internal @@ -178,6 +165,29 @@ absl_cc_library( absl::synchronization ) +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + flags_internal + SRCS + "internal/flag.cc" + HDRS + "internal/flag.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::base + absl::config + absl::flags_config + absl::flags_marshalling + absl::flags_registry + absl::synchronization + absl::meta + PUBLIC +) + absl_cc_library( NAME flags @@ -192,8 +202,8 @@ absl_cc_library( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::config + absl::flags_commandlineflag absl::flags_config - absl::flags_handle absl::flags_internal absl::flags_marshalling absl::flags_registry @@ -218,10 +228,10 @@ absl_cc_library( absl::config absl::flags_config absl::flags - absl::flags_handle - absl::flags_private_handle_accessor + absl::flags_commandlineflag absl::flags_internal absl::flags_path_util + absl::flags_private_handle_accessor absl::flags_program_name absl::flags_registry absl::strings @@ -264,9 +274,9 @@ absl_cc_library( absl::core_headers absl::flags_config absl::flags - absl::flags_handle - absl::flags_private_handle_accessor + absl::flags_commandlineflag absl::flags_internal + absl::flags_private_handle_accessor absl::flags_program_name absl::flags_registry absl::flags_usage @@ -281,13 +291,13 @@ absl_cc_test( NAME flags_commandlineflag_test SRCS - "internal/commandlineflag_test.cc" + "commandlineflag_test.cc" COPTS ${ABSL_TEST_COPTS} DEPS absl::flags + absl::flags_commandlineflag absl::flags_config - absl::flags_handle absl::flags_private_handle_accessor absl::flags_registry absl::memory @@ -319,7 +329,6 @@ absl_cc_test( absl::core_headers absl::flags absl::flags_config - absl::flags_handle absl::flags_internal absl::flags_registry absl::strings @@ -391,7 +400,6 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::flags - absl::flags_handle absl::flags_marshalling absl::flags_registry absl::memory diff --git a/absl/flags/internal/commandlineflag.cc b/absl/flags/commandlineflag.cc similarity index 95% rename from absl/flags/internal/commandlineflag.cc rename to absl/flags/commandlineflag.cc index 84112437..83d14c1b 100644 --- a/absl/flags/internal/commandlineflag.cc +++ b/absl/flags/commandlineflag.cc @@ -13,21 +13,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/commandlineflag.h" namespace absl { ABSL_NAMESPACE_BEGIN -namespace flags_internal { - -FlagStateInterface::~FlagStateInterface() {} bool CommandLineFlag::IsRetired() const { return false; } - bool CommandLineFlag::ParseFrom(absl::string_view value, std::string* error) { return ParseFrom(value, flags_internal::SET_FLAGS_VALUE, flags_internal::kProgrammaticChange, error); } +namespace flags_internal { +FlagStateInterface::~FlagStateInterface() {} } // namespace flags_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/flags/commandlineflag.h b/absl/flags/commandlineflag.h new file mode 100644 index 00000000..f09f1f70 --- /dev/null +++ b/absl/flags/commandlineflag.h @@ -0,0 +1,190 @@ +// +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: commandlineflag.h +// ----------------------------------------------------------------------------- +// +// This header file defines the `CommandLineFlag`, which acts as a type-erased +// handle for accessing metadata about the Abseil Flag in question. +// +// Because an actual Abseil flag is of an unspecified type, you should not +// manipulate or interact directly with objects of that type. Instead, use the +// CommandLineFlag type as an intermediary. +#ifndef ABSL_FLAGS_COMMANDLINEFLAG_H_ +#define ABSL_FLAGS_COMMANDLINEFLAG_H_ + +#include "absl/flags/internal/commandlineflag.h" +#include "absl/types/optional.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace flags_internal { +class PrivateHandleAccessor; +} // namespace flags_internal + +// CommandLineFlag +// +// This type acts as a type-erased handle for an instance of an Abseil Flag and +// holds reflection information pertaining to that flag. Use CommandLineFlag to +// access a flag's name, location, help string etc. +// +// To obtain an absl::CommandLineFlag, invoke `absl::FindCommandLineFlag()` +// passing it the flag name string. +// +// Example: +// +// // Obtain reflection handle for a flag named "flagname". +// const absl::CommandLineFlag* my_flag_data = +// absl::FindCommandLineFlag("flagname"); +// +// // Now you can get flag info from that reflection handle. +// std::string flag_location = my_flag_data->Filename(); +// ... +class CommandLineFlag { + public: + constexpr CommandLineFlag() = default; + + // Not copyable/assignable. + CommandLineFlag(const CommandLineFlag&) = delete; + CommandLineFlag& operator=(const CommandLineFlag&) = delete; + + // absl::CommandLineFlag::IsOfType() + // + // Return true iff flag has type T. + template + inline bool IsOfType() const { + return TypeId() == base_internal::FastTypeId(); + } + + // absl::CommandLineFlag::TryGet() + // + // Attempts to retrieve the flag value. Returns value on success, + // absl::nullopt otherwise. + template + absl::optional TryGet() const { + if (IsRetired() || !IsOfType()) { + return absl::nullopt; + } + + // Implementation notes: + // + // We are wrapping a union around the value of `T` to serve three purposes: + // + // 1. `U.value` has correct size and alignment for a value of type `T` + // 2. The `U.value` constructor is not invoked since U's constructor does + // not do it explicitly. + // 3. The `U.value` destructor is invoked since U's destructor does it + // explicitly. This makes `U` a kind of RAII wrapper around non default + // constructible value of T, which is destructed when we leave the + // scope. We do need to destroy U.value, which is constructed by + // CommandLineFlag::Read even though we left it in a moved-from state + // after std::move. + // + // All of this serves to avoid requiring `T` being default constructible. + union U { + T value; + U() {} + ~U() { value.~T(); } + }; + U u; + + Read(&u.value); + return std::move(u.value); + } + + // absl::CommandLineFlag::Name() + // + // Returns name of this flag. + virtual absl::string_view Name() const = 0; + + // absl::CommandLineFlag::Filename() + // + // Returns name of the file where this flag is defined. + virtual std::string Filename() const = 0; + + // absl::CommandLineFlag::Help() + // + // Returns help message associated with this flag. + virtual std::string Help() const = 0; + + // absl::CommandLineFlag::IsRetired() + // + // Returns true iff this object corresponds to retired flag. + virtual bool IsRetired() const; + + // absl::CommandLineFlag::DefaultValue() + // + // Returns the default value for this flag. + virtual std::string DefaultValue() const = 0; + + // absl::CommandLineFlag::CurrentValue() + // + // Returns the current value for this flag. + virtual std::string CurrentValue() const = 0; + + // absl::CommandLineFlag::ParseFrom() + // + // Sets the value of the flag based on specified string `value`. If the flag + // was successfully set to new value, it returns true. Otherwise, sets `error` + // to indicate the error, leaves the flag unchanged, and returns false. + bool ParseFrom(absl::string_view value, std::string* error); + + protected: + ~CommandLineFlag() = default; + + private: + friend class flags_internal::PrivateHandleAccessor; + + // Sets the value of the flag based on specified string `value`. If the flag + // was successfully set to new value, it returns true. Otherwise, sets `error` + // to indicate the error, leaves the flag unchanged, and returns false. There + // are three ways to set the flag's value: + // * Update the current flag value + // * Update the flag's default value + // * Update the current flag value if it was never set before + // The mode is selected based on `set_mode` parameter. + virtual bool ParseFrom(absl::string_view value, + flags_internal::FlagSettingMode set_mode, + flags_internal::ValueSource source, + std::string* error) = 0; + + // Returns id of the flag's value type. + virtual flags_internal::FlagFastTypeId TypeId() const = 0; + + // Interface to save flag to some persistent state. Returns current flag state + // or nullptr if flag does not support saving and restoring a state. + virtual std::unique_ptr SaveState() = 0; + + // Copy-construct a new value of the flag's type in a memory referenced by + // the dst based on the current flag's value. + virtual void Read(void* dst) const = 0; + + // To be deleted. Used to return true if flag's current value originated from + // command line. + virtual bool IsSpecifiedOnCommandLine() const = 0; + + // Validates supplied value usign validator or parseflag routine + virtual bool ValidateInputValue(absl::string_view value) const = 0; + + // Checks that flags default value can be converted to string and back to the + // flag's value type. + virtual void CheckDefaultValueParsingRoundtrip() const = 0; +}; + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FLAGS_COMMANDLINEFLAG_H_ diff --git a/absl/flags/internal/commandlineflag_test.cc b/absl/flags/commandlineflag_test.cc similarity index 93% rename from absl/flags/internal/commandlineflag_test.cc rename to absl/flags/commandlineflag_test.cc index 0b5aea37..352edccf 100644 --- a/absl/flags/internal/commandlineflag_test.cc +++ b/absl/flags/commandlineflag_test.cc @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/commandlineflag.h" #include #include @@ -70,9 +70,10 @@ TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) { EXPECT_EQ(flag_01->Help(), "int_flag help"); EXPECT_TRUE(!flag_01->IsRetired()); EXPECT_TRUE(flag_01->IsOfType()); - EXPECT_TRUE( - absl::EndsWith(flag_01->Filename(), - "absl/flags/internal/commandlineflag_test.cc")) + EXPECT_TRUE(!flag_01->IsOfType()); + EXPECT_TRUE(!flag_01->IsOfType()); + EXPECT_TRUE(absl::EndsWith(flag_01->Filename(), + "absl/flags/commandlineflag_test.cc")) << flag_01->Filename(); auto* flag_02 = flags::FindCommandLineFlag("string_flag"); @@ -82,9 +83,10 @@ TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) { EXPECT_EQ(flag_02->Help(), "string_flag help"); EXPECT_TRUE(!flag_02->IsRetired()); EXPECT_TRUE(flag_02->IsOfType()); - EXPECT_TRUE( - absl::EndsWith(flag_02->Filename(), - "absl/flags/internal/commandlineflag_test.cc")) + EXPECT_TRUE(!flag_02->IsOfType()); + EXPECT_TRUE(!flag_02->IsOfType()); + EXPECT_TRUE(absl::EndsWith(flag_02->Filename(), + "absl/flags/commandlineflag_test.cc")) << flag_02->Filename(); auto* flag_03 = flags::FindRetiredFlag("bool_retired_flag"); @@ -94,6 +96,8 @@ TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) { EXPECT_EQ(flag_03->Help(), ""); EXPECT_TRUE(flag_03->IsRetired()); EXPECT_TRUE(flag_03->IsOfType()); + EXPECT_TRUE(!flag_03->IsOfType()); + EXPECT_TRUE(!flag_03->IsOfType()); EXPECT_EQ(flag_03->Filename(), "RETIRED"); } diff --git a/absl/flags/flag.cc b/absl/flags/flag.cc index f7a457bf..531df128 100644 --- a/absl/flags/flag.cc +++ b/absl/flags/flag.cc @@ -16,8 +16,6 @@ #include "absl/flags/flag.h" #include "absl/base/config.h" -#include "absl/flags/internal/commandlineflag.h" -#include "absl/flags/internal/flag.h" namespace absl { ABSL_NAMESPACE_BEGIN diff --git a/absl/flags/flag.h b/absl/flags/flag.h index ca7d581f..f84853ea 100644 --- a/absl/flags/flag.h +++ b/absl/flags/flag.h @@ -37,7 +37,6 @@ #include "absl/base/config.h" #include "absl/flags/config.h" #include "absl/flags/declare.h" -#include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/flag.h" #include "absl/flags/internal/registry.h" #include "absl/flags/marshalling.h" @@ -265,27 +264,29 @@ ABSL_NAMESPACE_END // ----------------------------------------------------------------------------- // ABSL_FLAG_IMPL macro definition conditional on ABSL_FLAGS_STRIP_NAMES +#if !defined(_MSC_VER) || defined(__clang__) +#define ABSL_FLAG_IMPL_FLAG_PTR(flag) &flag +#define ABSL_FLAG_IMPL_HELP_ARG(name) \ + absl::flags_internal::HelpArg( \ + FLAGS_help_storage_##name) +#define ABSL_FLAG_IMPL_DEFAULT_ARG(Type, name) \ + absl::flags_internal::DefaultArg(0) +#else +#define ABSL_FLAG_IMPL_FLAG_PTR(flag) flag.GetImpl() +#define ABSL_FLAG_IMPL_HELP_ARG(name) &AbslFlagHelpGenFor##name::NonConst +#define ABSL_FLAG_IMPL_DEFAULT_ARG(Type, name) &AbslFlagDefaultGenFor##name::Gen +#endif #if ABSL_FLAGS_STRIP_NAMES #define ABSL_FLAG_IMPL_FLAGNAME(txt) "" #define ABSL_FLAG_IMPL_FILENAME() "" -#if !defined(_MSC_VER) || defined(__clang__) #define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ - absl::flags_internal::FlagRegistrar(&flag) -#else -#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ - absl::flags_internal::FlagRegistrar(flag.GetImpl()) -#endif + absl::flags_internal::FlagRegistrar(ABSL_FLAG_IMPL_FLAG_PTR(flag)) #else #define ABSL_FLAG_IMPL_FLAGNAME(txt) txt #define ABSL_FLAG_IMPL_FILENAME() __FILE__ -#if !defined(_MSC_VER) || defined(__clang__) -#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ - absl::flags_internal::FlagRegistrar(&flag) -#else #define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ - absl::flags_internal::FlagRegistrar(flag.GetImpl()) -#endif + absl::flags_internal::FlagRegistrar(ABSL_FLAG_IMPL_FLAG_PTR(flag)) #endif // ABSL_FLAG_IMPL macro definition conditional on ABSL_FLAGS_STRIP_HELP @@ -301,15 +302,24 @@ ABSL_NAMESPACE_END // 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 \ - 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); } \ - } +// TODO(rogeeff): place these generated structs into local namespace and apply +// ABSL_INTERNAL_UNIQUE_SHORT_NAME. +// TODO(rogeeff): Apply __attribute__((nodebug)) to FLAGS_help_storage_##name +#define ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, txt) \ + struct AbslFlagHelpGenFor##name { \ + /* The expression is run in the caller as part of the */ \ + /* default value argument. That keeps temporaries alive */ \ + /* long enough for NonConst to work correctly. */ \ + static constexpr absl::string_view Value( \ + absl::string_view v = ABSL_FLAG_IMPL_FLAGHELP(txt)) { \ + return v; \ + } \ + static std::string NonConst() { return std::string(Value()); } \ + }; \ + constexpr auto FLAGS_help_storage_##name ABSL_INTERNAL_UNIQUE_SMALL_NAME() \ + ABSL_ATTRIBUTE_SECTION_VARIABLE(flags_help_cold) = \ + absl::flags_internal::HelpStringAsArray( \ + 0); #define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ struct AbslFlagDefaultGenFor##name { \ @@ -317,40 +327,23 @@ ABSL_NAMESPACE_END static void Gen(void* p) { \ new (p) Type(AbslFlagDefaultGenFor##name{}.value); \ } \ - } + }; // ABSL_FLAG_IMPL // // Note: Name of registrar object is not arbitrary. It is used to "grab" // global name for FLAGS_no symbol, thus preventing the possibility // of defining two flags with names foo and nofoo. -#if !defined(_MSC_VER) || defined(__clang__) - -#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 FLAGS_##name{ \ - ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(), \ - absl::flags_internal::HelpArg(0), \ - absl::flags_internal::DefaultArg(0)}; \ - extern absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name; \ - absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name = \ - ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name) -#else -// 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 FLAGS_##name{ \ - ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(), \ - &AbslFlagHelpGenFor##name::NonConst, &AbslFlagDefaultGenFor##name::Gen}; \ - extern absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name; \ - absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name = \ +#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 FLAGS_##name{ \ + ABSL_FLAG_IMPL_FLAGNAME(#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) -#endif // ABSL_RETIRED_FLAG // diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc index 416a31e5..8d53ecd6 100644 --- a/absl/flags/flag_test.cc +++ b/absl/flags/flag_test.cc @@ -26,7 +26,6 @@ #include "absl/base/attributes.h" #include "absl/flags/config.h" #include "absl/flags/declare.h" -#include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/flag.h" #include "absl/flags/internal/registry.h" #include "absl/flags/usage_config.h" diff --git a/absl/flags/internal/commandlineflag.h b/absl/flags/internal/commandlineflag.h index fa050209..3c149788 100644 --- a/absl/flags/internal/commandlineflag.h +++ b/absl/flags/internal/commandlineflag.h @@ -23,7 +23,6 @@ #include "absl/base/internal/fast_type_id.h" #include "absl/base/macros.h" #include "absl/strings/string_view.h" -#include "absl/types/optional.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -67,117 +66,6 @@ class FlagStateInterface { virtual void Restore() const = 0; }; -// Holds all information for a flag. -class CommandLineFlag { - public: - constexpr CommandLineFlag() = default; - - // Not copyable/assignable. - CommandLineFlag(const CommandLineFlag&) = delete; - CommandLineFlag& operator=(const CommandLineFlag&) = delete; - - // Non-polymorphic access methods. - - // Return true iff flag has type T. - template - inline bool IsOfType() const { - return TypeId() == base_internal::FastTypeId(); - } - - // Attempts to retrieve the flag value. Returns value on success, - // absl::nullopt otherwise. - template - absl::optional TryGet() const { - if (IsRetired() || !IsOfType()) { - return absl::nullopt; - } - - // Implementation notes: - // - // We are wrapping a union around the value of `T` to serve three purposes: - // - // 1. `U.value` has correct size and alignment for a value of type `T` - // 2. The `U.value` constructor is not invoked since U's constructor does - // not do it explicitly. - // 3. The `U.value` destructor is invoked since U's destructor does it - // explicitly. This makes `U` a kind of RAII wrapper around non default - // constructible value of T, which is destructed when we leave the - // scope. We do need to destroy U.value, which is constructed by - // CommandLineFlag::Read even though we left it in a moved-from state - // after std::move. - // - // All of this serves to avoid requiring `T` being default constructible. - union U { - T value; - U() {} - ~U() { value.~T(); } - }; - U u; - - Read(&u.value); - return std::move(u.value); - } - - // Polymorphic access methods - - // Returns name of this flag. - virtual absl::string_view Name() const = 0; - // Returns name of the file where this flag is defined. - virtual std::string Filename() const = 0; - // 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; - virtual std::string DefaultValue() const = 0; - virtual std::string CurrentValue() const = 0; - - // Sets the value of the flag based on specified string `value`. If the flag - // was successfully set to new value, it returns true. Otherwise, sets `error` - // to indicate the error, leaves the flag unchanged, and returns false. - bool ParseFrom(absl::string_view value, std::string* error); - - protected: - ~CommandLineFlag() = default; - - private: - friend class PrivateHandleAccessor; - - // Sets the value of the flag based on specified string `value`. If the flag - // was successfully set to new value, it returns true. Otherwise, sets `error` - // to indicate the error, leaves the flag unchanged, and returns false. There - // are three ways to set the flag's value: - // * Update the current flag value - // * Update the flag's default value - // * Update the current flag value if it was never set before - // The mode is selected based on `set_mode` parameter. - virtual bool ParseFrom(absl::string_view value, - flags_internal::FlagSettingMode set_mode, - flags_internal::ValueSource source, - std::string* error) = 0; - - // Returns id of the flag's value type. - virtual FlagFastTypeId TypeId() const = 0; - - // Interface to save flag to some persistent state. Returns current flag state - // or nullptr if flag does not support saving and restoring a state. - virtual std::unique_ptr SaveState() = 0; - - // Copy-construct a new value of the flag's type in a memory referenced by - // the dst based on the current flag's value. - virtual void Read(void* dst) const = 0; - - // To be deleted. Used to return true if flag's current value originated from - // command line. - virtual bool IsSpecifiedOnCommandLine() const = 0; - - // Validates supplied value usign validator or parseflag routine - virtual bool ValidateInputValue(absl::string_view value) const = 0; - - // Checks that flags default value can be converted to string and back to the - // flag's value type. - virtual void CheckDefaultValueParsingRoundtrip() const = 0; -}; - } // namespace flags_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc index 96c026dc..35ae55fb 100644 --- a/absl/flags/internal/flag.cc +++ b/absl/flags/internal/flag.cc @@ -29,7 +29,6 @@ #include "absl/base/config.h" #include "absl/base/const_init.h" #include "absl/base/optimization.h" -#include "absl/flags/internal/commandlineflag.h" #include "absl/flags/usage_config.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h index e374ecde..97ddb5f9 100644 --- a/absl/flags/internal/flag.h +++ b/absl/flags/internal/flag.h @@ -28,8 +28,8 @@ #include "absl/base/call_once.h" #include "absl/base/config.h" #include "absl/base/thread_annotations.h" +#include "absl/flags/commandlineflag.h" #include "absl/flags/config.h" -#include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/registry.h" #include "absl/flags/marshalling.h" #include "absl/memory/memory.h" @@ -168,6 +168,28 @@ inline const std::type_info* GenRuntimeTypeId() { // cases. using HelpGenFunc = std::string (*)(); +template +struct FixedCharArray { + char value[N]; + + template + static constexpr FixedCharArray FromLiteralString( + absl::string_view str, absl::index_sequence) { + return (void)str, FixedCharArray({{str[I]..., '\0'}}); + } +}; + +template +constexpr FixedCharArray HelpStringAsArray(int) { + return FixedCharArray::FromLiteralString( + Gen::Value(), absl::make_index_sequence{}); +} + +template +constexpr std::false_type HelpStringAsArray(char) { + return std::false_type{}; +} + union FlagHelpMsg { constexpr explicit FlagHelpMsg(const char* help_msg) : literal(help_msg) {} constexpr explicit FlagHelpMsg(HelpGenFunc help_gen) : gen_func(help_gen) {} @@ -185,40 +207,28 @@ struct FlagHelpArg { extern const char kStrippedFlagHelp[]; -// 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 -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 +// AbslFlagHelpGenFor##name as Gen and integer 0 as a single argument to prefer +// first overload if possible. If help message is evaluatable on constexpr +// context We'll be able to make FixedCharArray out of it and 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 -constexpr FlagHelpArg HelpArg(int) { - return {FlagHelpMsg(T::Const()), FlagHelpKind::kLiteral}; +template +constexpr FlagHelpArg HelpArg(const FixedCharArray& value) { + return {FlagHelpMsg(value.value), FlagHelpKind::kLiteral}; } -template -constexpr FlagHelpArg HelpArg(char) { - return {FlagHelpMsg(&T::NonConst), FlagHelpKind::kGenFunc}; +template +constexpr FlagHelpArg HelpArg(std::false_type) { + return {FlagHelpMsg(&Gen::NonConst), FlagHelpKind::kGenFunc}; } /////////////////////////////////////////////////////////////////////////////// @@ -419,7 +429,7 @@ struct DynValueDeleter { class FlagState; -class FlagImpl final : public flags_internal::CommandLineFlag { +class FlagImpl final : public CommandLineFlag { public: constexpr FlagImpl(const char* name, const char* filename, FlagOpFn op, FlagHelpArg help, FlagValueStorageKind value_kind, diff --git a/absl/flags/internal/private_handle_accessor.h b/absl/flags/internal/private_handle_accessor.h index 40591de4..07838600 100644 --- a/absl/flags/internal/private_handle_accessor.h +++ b/absl/flags/internal/private_handle_accessor.h @@ -16,7 +16,7 @@ #ifndef ABSL_FLAGS_INTERNAL_PRIVATE_HANDLE_ACCESSOR_H_ #define ABSL_FLAGS_INTERNAL_PRIVATE_HANDLE_ACCESSOR_H_ -#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/commandlineflag.h" namespace absl { ABSL_NAMESPACE_BEGIN diff --git a/absl/flags/internal/registry.cc b/absl/flags/internal/registry.cc index 3b941f04..70d76557 100644 --- a/absl/flags/internal/registry.cc +++ b/absl/flags/internal/registry.cc @@ -28,7 +28,7 @@ #include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/thread_annotations.h" -#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/commandlineflag.h" #include "absl/flags/internal/private_handle_accessor.h" #include "absl/flags/usage_config.h" #include "absl/strings/str_cat.h" @@ -111,6 +111,7 @@ class FlagRegistryLock { }; void DestroyRetiredFlag(CommandLineFlag* flag); + } // namespace void FlagRegistry::RegisterFlag(CommandLineFlag* flag) { @@ -205,7 +206,7 @@ class FlagSaverImpl { // It's an error to call this more than once. void SaveFromRegistry() { assert(backup_registry_.empty()); // call only once! - flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) { + flags_internal::ForEachFlag([&](CommandLineFlag* flag) { if (auto flag_state = flags_internal::PrivateHandleAccessor::SaveState(flag)) { backup_registry_.emplace_back(std::move(flag_state)); @@ -283,7 +284,7 @@ bool RegisterCommandLineFlag(CommandLineFlag* flag) { namespace { -class RetiredFlagObj final : public flags_internal::CommandLineFlag { +class RetiredFlagObj final : public CommandLineFlag { public: constexpr RetiredFlagObj(const char* name, FlagFastTypeId type_id) : name_(name), type_id_(type_id) {} @@ -319,7 +320,7 @@ class RetiredFlagObj final : public flags_internal::CommandLineFlag { const FlagFastTypeId type_id_; }; -void DestroyRetiredFlag(flags_internal::CommandLineFlag* flag) { +void DestroyRetiredFlag(CommandLineFlag* flag) { assert(flag->IsRetired()); delete static_cast(flag); } diff --git a/absl/flags/internal/registry.h b/absl/flags/internal/registry.h index af8ed6b9..f722fd44 100644 --- a/absl/flags/internal/registry.h +++ b/absl/flags/internal/registry.h @@ -30,6 +30,8 @@ namespace absl { ABSL_NAMESPACE_BEGIN +class CommandLineFlag; + namespace flags_internal { CommandLineFlag* FindCommandLineFlag(absl::string_view name); diff --git a/absl/flags/internal/type_erased.cc b/absl/flags/internal/type_erased.cc index c13fb9b0..35b0d125 100644 --- a/absl/flags/internal/type_erased.cc +++ b/absl/flags/internal/type_erased.cc @@ -21,7 +21,7 @@ #include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" -#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/commandlineflag.h" #include "absl/flags/internal/private_handle_accessor.h" #include "absl/flags/internal/registry.h" #include "absl/flags/usage_config.h" diff --git a/absl/flags/internal/type_erased.h b/absl/flags/internal/type_erased.h index b43a0ff7..fd9663e2 100644 --- a/absl/flags/internal/type_erased.h +++ b/absl/flags/internal/type_erased.h @@ -19,7 +19,7 @@ #include #include "absl/base/config.h" -#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/commandlineflag.h" #include "absl/flags/internal/registry.h" #include "absl/strings/string_view.h" diff --git a/absl/flags/internal/type_erased_test.cc b/absl/flags/internal/type_erased_test.cc index 4ce59810..bb0ff23e 100644 --- a/absl/flags/internal/type_erased_test.cc +++ b/absl/flags/internal/type_erased_test.cc @@ -20,7 +20,6 @@ #include "gtest/gtest.h" #include "absl/flags/flag.h" -#include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/registry.h" #include "absl/flags/marshalling.h" #include "absl/memory/memory.h" diff --git a/absl/flags/internal/usage.cc b/absl/flags/internal/usage.cc index 10accc46..11664e10 100644 --- a/absl/flags/internal/usage.cc +++ b/absl/flags/internal/usage.cc @@ -23,8 +23,8 @@ #include #include "absl/base/config.h" +#include "absl/flags/commandlineflag.h" #include "absl/flags/flag.h" -#include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/flag.h" #include "absl/flags/internal/path_util.h" #include "absl/flags/internal/private_handle_accessor.h" @@ -182,8 +182,7 @@ class FlagHelpPrettyPrinter { bool first_line_; }; -void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag, - std::ostream* out) { +void FlagHelpHumanReadable(const CommandLineFlag& flag, std::ostream* out) { FlagHelpPrettyPrinter printer(80, out); // Max line length is 80. // Flag name. @@ -245,11 +244,10 @@ void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb, // This map is used to output matching flags grouped by package and file // name. std::map>> + std::map>> matching_flags; - flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) { + flags_internal::ForEachFlag([&](CommandLineFlag* flag) { std::string flag_filename = flag->Filename(); // Ignore retired flags. @@ -303,7 +301,7 @@ void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb, // -------------------------------------------------------------------- // Produces the help message describing specific flag. -void FlagHelp(std::ostream& out, const flags_internal::CommandLineFlag& flag, +void FlagHelp(std::ostream& out, const CommandLineFlag& flag, HelpFormat format) { if (format == HelpFormat::kHumanReadable) flags_internal::FlagHelpHumanReadable(flag, &out); diff --git a/absl/flags/internal/usage.h b/absl/flags/internal/usage.h index 6b080fd1..0c62dc4b 100644 --- a/absl/flags/internal/usage.h +++ b/absl/flags/internal/usage.h @@ -20,8 +20,8 @@ #include #include "absl/base/config.h" +#include "absl/flags/commandlineflag.h" #include "absl/flags/declare.h" -#include "absl/flags/internal/commandlineflag.h" #include "absl/strings/string_view.h" // -------------------------------------------------------------------- @@ -37,7 +37,7 @@ enum class HelpFormat { }; // Outputs the help message describing specific flag. -void FlagHelp(std::ostream& out, const flags_internal::CommandLineFlag& flag, +void FlagHelp(std::ostream& out, const CommandLineFlag& flag, HelpFormat format = HelpFormat::kHumanReadable); // Produces the help messages for all flags matching the filter. A flag matches diff --git a/absl/flags/parse.cc b/absl/flags/parse.cc index fbf42675..3f0a7a75 100644 --- a/absl/flags/parse.cc +++ b/absl/flags/parse.cc @@ -34,9 +34,9 @@ #include "absl/base/config.h" #include "absl/base/const_init.h" #include "absl/base/thread_annotations.h" +#include "absl/flags/commandlineflag.h" #include "absl/flags/config.h" #include "absl/flags/flag.h" -#include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/flag.h" #include "absl/flags/internal/parse.h" #include "absl/flags/internal/private_handle_accessor.h"