summaryrefslogtreecommitdiff
path: root/absl/flags
diff options
context:
space:
mode:
Diffstat (limited to 'absl/flags')
-rw-r--r--absl/flags/BUILD.bazel35
-rw-r--r--absl/flags/CMakeLists.txt26
-rw-r--r--absl/flags/config.h8
-rw-r--r--absl/flags/declare.h10
-rw-r--r--absl/flags/flag.h112
-rw-r--r--absl/flags/flag_benchmark.cc138
-rw-r--r--absl/flags/flag_test.cc231
-rw-r--r--absl/flags/internal/flag.cc96
-rw-r--r--absl/flags/internal/flag.h83
-rw-r--r--absl/flags/internal/flag_msvc.inc116
-rw-r--r--absl/flags/internal/sequence_lock.h2
-rw-r--r--absl/flags/internal/usage.cc12
-rw-r--r--absl/flags/internal/usage_test.cc25
-rw-r--r--absl/flags/marshalling.h94
-rw-r--r--absl/flags/marshalling_test.cc166
-rw-r--r--absl/flags/parse_test.cc1
-rw-r--r--absl/flags/reflection.cc8
17 files changed, 949 insertions, 214 deletions
diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel
index 147249ed..4ca687ee 100644
--- a/absl/flags/BUILD.bazel
+++ b/absl/flags/BUILD.bazel
@@ -14,7 +14,6 @@
# limitations under the License.
#
-load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
@@ -101,6 +100,7 @@ cc_library(
"//absl/base:log_severity",
"//absl/strings",
"//absl/strings:str_format",
+ "//absl/types:optional",
],
)
@@ -114,6 +114,9 @@ cc_library(
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
+ visibility = [
+ "//visibility:private",
+ ],
deps = [
"//absl/base:config",
"//absl/base:fast_type_id",
@@ -205,6 +208,7 @@ cc_library(
"//absl/base",
"//absl/base:config",
"//absl/base:core_headers",
+ "//absl/base:dynamic_annotations",
"//absl/memory",
"//absl/meta:type_traits",
"//absl/strings",
@@ -217,6 +221,7 @@ cc_library(
name = "flag",
srcs = [
"flag.cc",
+ "internal/flag_msvc.inc",
],
hdrs = [
"declare.h",
@@ -259,6 +264,7 @@ cc_library(
":reflection",
"//absl/base:config",
"//absl/base:core_headers",
+ "//absl/container:flat_hash_map",
"//absl/strings",
],
)
@@ -320,6 +326,11 @@ cc_test(
],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
+ tags = [
+ "no_test_android",
+ "no_test_ios",
+ "no_test_wasm",
+ ],
deps = [
":commandlineflag",
":commandlineflag_internal",
@@ -356,6 +367,11 @@ cc_test(
],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
+ tags = [
+ "no_test_android",
+ "no_test_ios",
+ "no_test_wasm",
+ ],
deps = [
":config",
":flag",
@@ -417,6 +433,11 @@ cc_test(
],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
+ tags = [
+ "no_test_android",
+ "no_test_ios",
+ "no_test_wasm",
+ ],
deps = [
":flag",
":parse",
@@ -452,6 +473,7 @@ cc_test(
],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
+ tags = ["no_test_wasm"],
deps = [
":program_name",
"//absl/strings",
@@ -467,6 +489,11 @@ cc_test(
],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
+ tags = [
+ "no_test_android",
+ "no_test_ios",
+ "no_test_wasm",
+ ],
deps = [
":commandlineflag_internal",
":flag",
@@ -489,6 +516,7 @@ cc_test(
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
shard_count = 31,
+ tags = ["no_test_wasm"],
deps = [
":flag_internal",
"//absl/base",
@@ -523,6 +551,11 @@ cc_test(
],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
+ tags = [
+ "no_test_android",
+ "no_test_ios",
+ "no_test_wasm",
+ ],
deps = [
":config",
":flag",
diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt
index caac69cf..3e9d5adf 100644
--- a/absl/flags/CMakeLists.txt
+++ b/absl/flags/CMakeLists.txt
@@ -87,6 +87,7 @@ absl_cc_library(
absl::config
absl::core_headers
absl::log_severity
+ absl::optional
absl::strings
absl::str_format
)
@@ -105,6 +106,7 @@ absl_cc_library(
${ABSL_DEFAULT_LINKOPTS}
DEPS
absl::config
+ absl::dynamic_annotations
absl::fast_type_id
)
@@ -202,6 +204,7 @@ absl_cc_library(
HDRS
"declare.h"
"flag.h"
+ "internal/flag_msvc.inc"
COPTS
${ABSL_DEFAULT_COPTS}
LINKOPTS
@@ -239,6 +242,7 @@ absl_cc_library(
absl::flags_private_handle_accessor
absl::flags_program_name
absl::flags_reflection
+ absl::flat_hash_map
absl::strings
absl::synchronization
)
@@ -309,7 +313,7 @@ absl_cc_test(
absl::flags_reflection
absl::memory
absl::strings
- gtest_main
+ GTest::gtest_main
)
absl_cc_test(
@@ -321,7 +325,7 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::flags_config
- gtest_main
+ GTest::gtest_main
)
absl_cc_test(
@@ -341,7 +345,7 @@ absl_cc_test(
absl::flags_reflection
absl::strings
absl::time
- gtest_main
+ GTest::gtest_main
)
absl_cc_test(
@@ -353,7 +357,7 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::flags_marshalling
- gtest_main
+ GTest::gtest_main
)
absl_cc_test(
@@ -372,7 +376,7 @@ absl_cc_test(
absl::scoped_set_env
absl::span
absl::strings
- gmock_main
+ GTest::gmock_main
)
absl_cc_test(
@@ -384,7 +388,7 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::flags_path_util
- gtest_main
+ GTest::gtest_main
)
absl_cc_test(
@@ -397,7 +401,7 @@ absl_cc_test(
DEPS
absl::flags_program_name
absl::strings
- gtest_main
+ GTest::gtest_main
)
absl_cc_test(
@@ -414,7 +418,7 @@ absl_cc_test(
absl::flags_usage
absl::memory
absl::strings
- gmock_main
+ GTest::gmock_main
)
absl_cc_test(
@@ -428,7 +432,7 @@ absl_cc_test(
absl::base
absl::flags_internal
absl::time
- gmock_main
+ GTest::gmock_main
)
absl_cc_test(
@@ -443,7 +447,7 @@ absl_cc_test(
absl::flags_path_util
absl::flags_program_name
absl::strings
- gtest_main
+ GTest::gtest_main
)
absl_cc_test(
@@ -462,5 +466,5 @@ absl_cc_test(
absl::flags_reflection
absl::flags_usage
absl::strings
- gtest
+ GTest::gmock
)
diff --git a/absl/flags/config.h b/absl/flags/config.h
index 5ab1f311..14c4235b 100644
--- a/absl/flags/config.h
+++ b/absl/flags/config.h
@@ -45,14 +45,6 @@
#define ABSL_FLAGS_STRIP_HELP ABSL_FLAGS_STRIP_NAMES
#endif
-// ABSL_FLAGS_INTERNAL_HAS_RTTI macro is used for selecting if we can use RTTI
-// for flag type identification.
-#ifdef ABSL_FLAGS_INTERNAL_HAS_RTTI
-#error ABSL_FLAGS_INTERNAL_HAS_RTTI cannot be directly set
-#elif !defined(__GNUC__) || defined(__GXX_RTTI)
-#define ABSL_FLAGS_INTERNAL_HAS_RTTI 1
-#endif // !defined(__GNUC__) || defined(__GXX_RTTI)
-
// These macros represent the "source of truth" for the list of supported
// built-in types.
#define ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(A) \
diff --git a/absl/flags/declare.h b/absl/flags/declare.h
index b9794d8b..d1437bb9 100644
--- a/absl/flags/declare.h
+++ b/absl/flags/declare.h
@@ -60,6 +60,14 @@ ABSL_NAMESPACE_END
// The ABSL_DECLARE_FLAG(type, name) macro expands to:
//
// extern absl::Flag<type> FLAGS_name;
-#define ABSL_DECLARE_FLAG(type, name) extern ::absl::Flag<type> FLAGS_##name
+#define ABSL_DECLARE_FLAG(type, name) ABSL_DECLARE_FLAG_INTERNAL(type, name)
+
+// Internal implementation of ABSL_DECLARE_FLAG to allow macro expansion of its
+// arguments. Clients must use ABSL_DECLARE_FLAG instead.
+#define ABSL_DECLARE_FLAG_INTERNAL(type, name) \
+ extern absl::Flag<type> FLAGS_##name; \
+ namespace absl /* block flags in namespaces */ {} \
+ /* second redeclaration is to allow applying attributes */ \
+ extern absl::Flag<type> FLAGS_##name
#endif // ABSL_FLAGS_DECLARE_H_
diff --git a/absl/flags/flag.h b/absl/flags/flag.h
index f09580b0..b7f94be7 100644
--- a/absl/flags/flag.h
+++ b/absl/flags/flag.h
@@ -67,105 +67,15 @@ ABSL_NAMESPACE_BEGIN
// ABSL_FLAG(int, count, 0, "Count of items to process");
//
// No public methods of `absl::Flag<T>` are part of the Abseil Flags API.
+//
+// For type support of Abseil Flags, see the marshalling.h header file, which
+// discusses supported standard types, optional flags, and additional Abseil
+// type support.
#if !defined(_MSC_VER) || defined(__clang__)
template <typename T>
using Flag = flags_internal::Flag<T>;
#else
-// MSVC debug builds do not implement initialization with constexpr constructors
-// correctly. To work around this we add a level of indirection, so that the
-// class `absl::Flag` contains an `internal::Flag*` (instead of being an alias
-// to that class) and dynamically allocates an instance when necessary. We also
-// forward all calls to internal::Flag methods via trampoline methods. In this
-// setup the `absl::Flag` class does not have constructor and virtual methods,
-// all the data members are public and thus MSVC is able to initialize it at
-// link time. To deal with multiple threads accessing the flag for the first
-// time concurrently we use an atomic boolean indicating if flag object is
-// initialized. We also employ the double-checked locking pattern where the
-// second level of protection is a global Mutex, so if two threads attempt to
-// construct the flag concurrently only one wins.
-// This solution is based on a recomendation here:
-// https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html?childToView=648454#comment-648454
-
-namespace flags_internal {
-absl::Mutex* GetGlobalConstructionGuard();
-} // namespace flags_internal
-
-template <typename T>
-class Flag {
- public:
- // No constructor and destructor to ensure this is an aggregate type.
- // Visual Studio 2015 still requires the constructor for class to be
- // constexpr initializable.
-#if _MSC_VER <= 1900
- constexpr Flag(const char* name, const char* filename,
- const flags_internal::HelpGenFunc help_gen,
- const flags_internal::FlagDfltGenFunc default_value_gen)
- : name_(name),
- filename_(filename),
- help_gen_(help_gen),
- default_value_gen_(default_value_gen),
- inited_(false),
- impl_(nullptr) {}
-#endif
-
- flags_internal::Flag<T>& GetImpl() const {
- if (!inited_.load(std::memory_order_acquire)) {
- absl::MutexLock l(flags_internal::GetGlobalConstructionGuard());
-
- if (inited_.load(std::memory_order_acquire)) {
- return *impl_;
- }
-
- impl_ = new flags_internal::Flag<T>(
- name_, filename_,
- {flags_internal::FlagHelpMsg(help_gen_),
- flags_internal::FlagHelpKind::kGenFunc},
- {flags_internal::FlagDefaultSrc(default_value_gen_),
- flags_internal::FlagDefaultKind::kGenFunc});
- inited_.store(true, std::memory_order_release);
- }
-
- return *impl_;
- }
-
- // Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API.
- // See https://abseil.io/docs/cpp/guides/flags
- bool IsRetired() const { return GetImpl().IsRetired(); }
- absl::string_view Name() const { return GetImpl().Name(); }
- std::string Help() const { return GetImpl().Help(); }
- bool IsModified() const { return GetImpl().IsModified(); }
- bool IsSpecifiedOnCommandLine() const {
- return GetImpl().IsSpecifiedOnCommandLine();
- }
- std::string Filename() const { return GetImpl().Filename(); }
- std::string DefaultValue() const { return GetImpl().DefaultValue(); }
- std::string CurrentValue() const { return GetImpl().CurrentValue(); }
- template <typename U>
- inline bool IsOfType() const {
- return GetImpl().template IsOfType<U>();
- }
- T Get() const {
- return flags_internal::FlagImplPeer::InvokeGet<T>(GetImpl());
- }
- void Set(const T& v) {
- flags_internal::FlagImplPeer::InvokeSet(GetImpl(), v);
- }
- void InvokeCallback() { GetImpl().InvokeCallback(); }
-
- const CommandLineFlag& Reflect() const {
- return flags_internal::FlagImplPeer::InvokeReflect(GetImpl());
- }
-
- // The data members are logically private, but they need to be public for
- // this to be an aggregate type.
- const char* name_;
- const char* filename_;
- const flags_internal::HelpGenFunc help_gen_;
- const flags_internal::FlagDfltGenFunc default_value_gen_;
-
- mutable std::atomic<bool> inited_;
- mutable flags_internal::Flag<T>* impl_;
-};
+#include "absl/flags/internal/flag_msvc.inc"
#endif
// GetFlag()
@@ -265,6 +175,8 @@ ABSL_NAMESPACE_END
//
// ABSL_FLAG(T, name, default_value, help).OnUpdate(callback);
//
+// `callback` should be convertible to `void (*)()`.
+//
// After any setting of the flag value, the callback will be called at least
// once. A rapid sequence of changes may be merged together into the same
// callback. No concurrent calls to the callback will be made for the same
@@ -279,7 +191,6 @@ ABSL_NAMESPACE_END
// Note: ABSL_FLAG.OnUpdate() does not have a public definition. Hence, this
// comment serves as its API documentation.
-
// -----------------------------------------------------------------------------
// Implementation details below this section
// -----------------------------------------------------------------------------
@@ -334,8 +245,8 @@ ABSL_NAMESPACE_END
/* 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; \
+ absl::string_view absl_flag_help = ABSL_FLAG_IMPL_FLAGHELP(txt)) { \
+ return absl_flag_help; \
} \
static std::string NonConst() { return std::string(Value()); } \
}; \
@@ -347,8 +258,8 @@ ABSL_NAMESPACE_END
#define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \
struct AbslFlagDefaultGenFor##name { \
Type value = absl::flags_internal::InitDefaultValue<Type>(default_value); \
- static void Gen(void* p) { \
- new (p) Type(AbslFlagDefaultGenFor##name{}.value); \
+ static void Gen(void* absl_flag_default_loc) { \
+ new (absl_flag_default_loc) Type(AbslFlagDefaultGenFor##name{}.value); \
} \
};
@@ -358,6 +269,7 @@ ABSL_NAMESPACE_END
// 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) \
diff --git a/absl/flags/flag_benchmark.cc b/absl/flags/flag_benchmark.cc
index 57584f85..fc572d9c 100644
--- a/absl/flags/flag_benchmark.cc
+++ b/absl/flags/flag_benchmark.cc
@@ -101,7 +101,39 @@ std::string AbslUnparseFlag(const UDT&) { return ""; }
A(AbslDuration) \
A(UDT)
-#define FLAG_DEF(T) ABSL_FLAG(T, T##_flag, {}, "");
+#define REPLICATE_0(A, T, name, index) A(T, name, index)
+#define REPLICATE_1(A, T, name, index) \
+ REPLICATE_0(A, T, name, index##0) REPLICATE_0(A, T, name, index##1)
+#define REPLICATE_2(A, T, name, index) \
+ REPLICATE_1(A, T, name, index##0) REPLICATE_1(A, T, name, index##1)
+#define REPLICATE_3(A, T, name, index) \
+ REPLICATE_2(A, T, name, index##0) REPLICATE_2(A, T, name, index##1)
+#define REPLICATE_4(A, T, name, index) \
+ REPLICATE_3(A, T, name, index##0) REPLICATE_3(A, T, name, index##1)
+#define REPLICATE_5(A, T, name, index) \
+ REPLICATE_4(A, T, name, index##0) REPLICATE_4(A, T, name, index##1)
+#define REPLICATE_6(A, T, name, index) \
+ REPLICATE_5(A, T, name, index##0) REPLICATE_5(A, T, name, index##1)
+#define REPLICATE_7(A, T, name, index) \
+ REPLICATE_6(A, T, name, index##0) REPLICATE_6(A, T, name, index##1)
+#define REPLICATE_8(A, T, name, index) \
+ REPLICATE_7(A, T, name, index##0) REPLICATE_7(A, T, name, index##1)
+#define REPLICATE_9(A, T, name, index) \
+ REPLICATE_8(A, T, name, index##0) REPLICATE_8(A, T, name, index##1)
+#if defined(_MSC_VER)
+#define REPLICATE(A, T, name) \
+ REPLICATE_7(A, T, name, 0) REPLICATE_7(A, T, name, 1)
+#define SINGLE_FLAG(T) FLAGS_##T##_flag_00000000
+#else
+#define REPLICATE(A, T, name) \
+ REPLICATE_9(A, T, name, 0) REPLICATE_9(A, T, name, 1)
+#define SINGLE_FLAG(T) FLAGS_##T##_flag_0000000000
+#endif
+#define REPLICATE_ALL(A, T, name) \
+ REPLICATE_9(A, T, name, 0) REPLICATE_9(A, T, name, 1)
+
+#define COUNT(T, name, index) +1
+constexpr size_t kNumFlags = 0 REPLICATE(COUNT, _, _);
#if defined(__clang__) && defined(__linux__)
// Force the flags used for benchmarks into a separate ELF section.
@@ -110,38 +142,87 @@ std::string AbslUnparseFlag(const UDT&) { return ""; }
// benchmark results more reproducible across unrelated code changes.
#pragma clang section data = ".benchmark_flags"
#endif
+#define DEFINE_FLAG(T, name, index) ABSL_FLAG(T, name##_##index, {}, "");
+#define FLAG_DEF(T) REPLICATE(DEFINE_FLAG, T, T##_flag);
BENCHMARKED_TYPES(FLAG_DEF)
#if defined(__clang__) && defined(__linux__)
#pragma clang section data = ""
#endif
// Register thousands of flags to bloat up the size of the registry.
// This mimics real life production binaries.
-#define DEFINE_FLAG_0(name) ABSL_FLAG(int, name, 0, "");
-#define DEFINE_FLAG_1(name) DEFINE_FLAG_0(name##0) DEFINE_FLAG_0(name##1)
-#define DEFINE_FLAG_2(name) DEFINE_FLAG_1(name##0) DEFINE_FLAG_1(name##1)
-#define DEFINE_FLAG_3(name) DEFINE_FLAG_2(name##0) DEFINE_FLAG_2(name##1)
-#define DEFINE_FLAG_4(name) DEFINE_FLAG_3(name##0) DEFINE_FLAG_3(name##1)
-#define DEFINE_FLAG_5(name) DEFINE_FLAG_4(name##0) DEFINE_FLAG_4(name##1)
-#define DEFINE_FLAG_6(name) DEFINE_FLAG_5(name##0) DEFINE_FLAG_5(name##1)
-#define DEFINE_FLAG_7(name) DEFINE_FLAG_6(name##0) DEFINE_FLAG_6(name##1)
-#define DEFINE_FLAG_8(name) DEFINE_FLAG_7(name##0) DEFINE_FLAG_7(name##1)
-#define DEFINE_FLAG_9(name) DEFINE_FLAG_8(name##0) DEFINE_FLAG_8(name##1)
-#define DEFINE_FLAG_10(name) DEFINE_FLAG_9(name##0) DEFINE_FLAG_9(name##1)
-#define DEFINE_FLAG_11(name) DEFINE_FLAG_10(name##0) DEFINE_FLAG_10(name##1)
-#define DEFINE_FLAG_12(name) DEFINE_FLAG_11(name##0) DEFINE_FLAG_11(name##1)
-DEFINE_FLAG_12(bloat_flag_);
+#define BLOAT_FLAG(_unused1, _unused2, index) \
+ ABSL_FLAG(int, bloat_flag_##index, 0, "");
+REPLICATE_ALL(BLOAT_FLAG, _, _)
namespace {
-#define BM_GetFlag(T) \
- void BM_GetFlag_##T(benchmark::State& state) { \
- for (auto _ : state) { \
- benchmark::DoNotOptimize(absl::GetFlag(FLAGS_##T##_flag)); \
- } \
- } \
- BENCHMARK(BM_GetFlag_##T)->ThreadRange(1, 16);
+#define FLAG_PTR(T, name, index) &FLAGS_##name##_##index,
+#define FLAG_PTR_ARR(T) \
+ static constexpr absl::Flag<T>* FlagPtrs_##T[] = { \
+ REPLICATE(FLAG_PTR, T, T##_flag)};
+BENCHMARKED_TYPES(FLAG_PTR_ARR)
+
+#define BM_SingleGetFlag(T) \
+ void BM_SingleGetFlag_##T(benchmark::State& state) { \
+ for (auto _ : state) { \
+ benchmark::DoNotOptimize(absl::GetFlag(SINGLE_FLAG(T))); \
+ } \
+ } \
+ BENCHMARK(BM_SingleGetFlag_##T)->ThreadRange(1, 16);
+
+BENCHMARKED_TYPES(BM_SingleGetFlag)
+
+template <typename T>
+struct Accumulator {
+ using type = T;
+};
+template <>
+struct Accumulator<String> {
+ using type = size_t;
+};
+template <>
+struct Accumulator<VectorOfStrings> {
+ using type = size_t;
+};
+template <>
+struct Accumulator<OptionalInt> {
+ using type = bool;
+};
+template <>
+struct Accumulator<OptionalString> {
+ using type = bool;
+};
+template <>
+struct Accumulator<UDT> {
+ using type = bool;
+};
+
+template <typename T>
+void Accumulate(typename Accumulator<T>::type& a, const T& f) {
+ a += f;
+}
+void Accumulate(bool& a, bool f) { a = a || f; }
+void Accumulate(size_t& a, const std::string& f) { a += f.size(); }
+void Accumulate(size_t& a, const std::vector<std::string>& f) { a += f.size(); }
+void Accumulate(bool& a, const OptionalInt& f) { a |= f.has_value(); }
+void Accumulate(bool& a, const OptionalString& f) { a |= f.has_value(); }
+void Accumulate(bool& a, const UDT& f) {
+ a |= reinterpret_cast<int64_t>(&f) & 0x1;
+}
+
+#define BM_ManyGetFlag(T) \
+ void BM_ManyGetFlag_##T(benchmark::State& state) { \
+ Accumulator<T>::type res = {}; \
+ while (state.KeepRunningBatch(kNumFlags)) { \
+ for (auto* flag_ptr : FlagPtrs_##T) { \
+ Accumulate(res, absl::GetFlag(*flag_ptr)); \
+ } \
+ } \
+ benchmark::DoNotOptimize(res); \
+ } \
+ BENCHMARK(BM_ManyGetFlag_##T)->ThreadRange(1, 8);
-BENCHMARKED_TYPES(BM_GetFlag)
+BENCHMARKED_TYPES(BM_ManyGetFlag)
void BM_ThreadedFindCommandLineFlag(benchmark::State& state) {
char dummy[] = "dummy";
@@ -150,17 +231,18 @@ void BM_ThreadedFindCommandLineFlag(benchmark::State& state) {
// is finalized.
absl::ParseCommandLine(1, argv);
- for (auto s : state) {
- benchmark::DoNotOptimize(
- absl::FindCommandLineFlag("bloat_flag_010101010101"));
+ while (state.KeepRunningBatch(kNumFlags)) {
+ for (auto* flag_ptr : FlagPtrs_bool) {
+ benchmark::DoNotOptimize(absl::FindCommandLineFlag(flag_ptr->Name()));
+ }
}
}
BENCHMARK(BM_ThreadedFindCommandLineFlag)->ThreadRange(1, 16);
} // namespace
-#define InvokeGetFlag(T) \
- T AbslInvokeGetFlag##T() { return absl::GetFlag(FLAGS_##T##_flag); } \
+#define InvokeGetFlag(T) \
+ T AbslInvokeGetFlag##T() { return absl::GetFlag(SINGLE_FLAG(T)); } \
int odr##T = (benchmark::DoNotOptimize(AbslInvokeGetFlag##T), 1);
BENCHMARKED_TYPES(InvokeGetFlag)
diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc
index 6912b546..845b4eba 100644
--- a/absl/flags/flag_test.cc
+++ b/absl/flags/flag_test.cc
@@ -61,6 +61,7 @@ void TestCallback() {}
struct UDT {
UDT() = default;
UDT(const UDT&) = default;
+ UDT& operator=(const UDT&) = default;
};
bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; }
std::string AbslUnparseFlag(const UDT&) { return ""; }
@@ -102,9 +103,9 @@ struct S2 {
TEST_F(FlagTest, Traits) {
EXPECT_EQ(flags::StorageKind<int>(),
- flags::FlagValueStorageKind::kOneWordAtomic);
+ flags::FlagValueStorageKind::kValueAndInitBit);
EXPECT_EQ(flags::StorageKind<bool>(),
- flags::FlagValueStorageKind::kOneWordAtomic);
+ flags::FlagValueStorageKind::kValueAndInitBit);
EXPECT_EQ(flags::StorageKind<double>(),
flags::FlagValueStorageKind::kOneWordAtomic);
EXPECT_EQ(flags::StorageKind<int64_t>(),
@@ -723,6 +724,8 @@ ABSL_FLAG(CustomUDT, test_flag_custom_udt, CustomUDT(), "test flag custom UDT");
namespace {
TEST_F(FlagTest, TestCustomUDT) {
+ EXPECT_EQ(flags::StorageKind<CustomUDT>(),
+ flags::FlagValueStorageKind::kOneWordAtomic);
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_custom_udt), CustomUDT(1, 1));
absl::SetFlag(&FLAGS_test_flag_custom_udt, CustomUDT(2, 3));
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_custom_udt), CustomUDT(2, 3));
@@ -851,7 +854,9 @@ ABSL_RETIRED_FLAG(bool, old_bool_flag, true, "old descr");
ABSL_RETIRED_FLAG(int, old_int_flag, (int)std::sqrt(10), "old descr");
ABSL_RETIRED_FLAG(std::string, old_str_flag, "", absl::StrCat("old ", "descr"));
-bool initializaion_order_fiasco_test = [] {
+namespace {
+
+bool initialization_order_fiasco_test ABSL_ATTRIBUTE_UNUSED = [] {
// Iterate over all the flags during static initialization.
// This should not trigger ASan's initialization-order-fiasco.
auto* handle1 = absl::FindCommandLineFlag("flag_on_separate_file");
@@ -862,8 +867,6 @@ bool initializaion_order_fiasco_test = [] {
return true;
}();
-namespace {
-
TEST_F(FlagTest, TestRetiredFlagRegistration) {
auto* handle = absl::FindCommandLineFlag("old_bool_flag");
EXPECT_TRUE(handle->IsOfType<bool>());
@@ -943,3 +946,221 @@ TEST_F(FlagTest, TestNonTriviallyCopyableUDT) {
}
} // namespace
+
+// --------------------------------------------------------------------
+
+namespace {
+
+enum TestE { A = 1, B = 2, C = 3 };
+
+struct EnumWrapper {
+ EnumWrapper() : e(A) {}
+
+ TestE e;
+};
+
+bool AbslParseFlag(absl::string_view, EnumWrapper*, std::string*) {
+ return true;
+}
+std::string AbslUnparseFlag(const EnumWrapper&) { return ""; }
+
+} // namespace
+
+ABSL_FLAG(EnumWrapper, test_enum_wrapper_flag, {}, "help");
+
+TEST_F(FlagTest, TesTypeWrappingEnum) {
+ EnumWrapper value = absl::GetFlag(FLAGS_test_enum_wrapper_flag);
+ EXPECT_EQ(value.e, A);
+
+ value.e = B;
+ absl::SetFlag(&FLAGS_test_enum_wrapper_flag, value);
+ value = absl::GetFlag(FLAGS_test_enum_wrapper_flag);
+ EXPECT_EQ(value.e, B);
+}
+
+// This is a compile test to ensure macros are expanded within ABSL_FLAG and
+// ABSL_DECLARE_FLAG.
+#define FLAG_NAME_MACRO(name) prefix_ ## name
+ABSL_DECLARE_FLAG(int, FLAG_NAME_MACRO(test_macro_named_flag));
+ABSL_FLAG(int, FLAG_NAME_MACRO(test_macro_named_flag), 0,
+ "Testing macro expansion within ABSL_FLAG");
+
+TEST_F(FlagTest, MacroWithinAbslFlag) {
+ EXPECT_EQ(absl::GetFlag(FLAGS_prefix_test_macro_named_flag), 0);
+ absl::SetFlag(&FLAGS_prefix_test_macro_named_flag, 1);
+ EXPECT_EQ(absl::GetFlag(FLAGS_prefix_test_macro_named_flag), 1);
+}
+
+// --------------------------------------------------------------------
+
+#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 5
+#define ABSL_SKIP_OPTIONAL_BOOL_TEST_DUE_TO_GCC_BUG
+#endif
+
+#ifndef ABSL_SKIP_OPTIONAL_BOOL_TEST_DUE_TO_GCC_BUG
+ABSL_FLAG(absl::optional<bool>, optional_bool, absl::nullopt, "help");
+#endif
+ABSL_FLAG(absl::optional<int>, optional_int, {}, "help");
+ABSL_FLAG(absl::optional<double>, optional_double, 9.3, "help");
+ABSL_FLAG(absl::optional<std::string>, optional_string, absl::nullopt, "help");
+ABSL_FLAG(absl::optional<absl::Duration>, optional_duration, absl::nullopt,
+ "help");
+ABSL_FLAG(absl::optional<absl::optional<int>>, optional_optional_int,
+ absl::nullopt, "help");
+#if defined(ABSL_HAVE_STD_OPTIONAL) && !defined(ABSL_USES_STD_OPTIONAL)
+ABSL_FLAG(std::optional<int64_t>, std_optional_int64, std::nullopt, "help");
+#endif
+
+namespace {
+
+#ifndef ABSL_SKIP_OPTIONAL_BOOL_TEST_DUE_TO_GCC_BUG
+TEST_F(FlagTest, TestOptionalBool) {
+ EXPECT_FALSE(absl::GetFlag(FLAGS_optional_bool).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_bool), absl::nullopt);
+
+ absl::SetFlag(&FLAGS_optional_bool, false);
+ EXPECT_TRUE(absl::GetFlag(FLAGS_optional_bool).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_bool), false);
+
+ absl::SetFlag(&FLAGS_optional_bool, true);
+ EXPECT_TRUE(absl::GetFlag(FLAGS_optional_bool).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_bool), true);
+
+ absl::SetFlag(&FLAGS_optional_bool, absl::nullopt);
+ EXPECT_FALSE(absl::GetFlag(FLAGS_optional_bool).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_bool), absl::nullopt);
+}
+
+// --------------------------------------------------------------------
+#endif
+
+TEST_F(FlagTest, TestOptionalInt) {
+ EXPECT_FALSE(absl::GetFlag(FLAGS_optional_int).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_int), absl::nullopt);
+
+ absl::SetFlag(&FLAGS_optional_int, 0);
+ EXPECT_TRUE(absl::GetFlag(FLAGS_optional_int).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_int), 0);
+
+ absl::SetFlag(&FLAGS_optional_int, 10);
+ EXPECT_TRUE(absl::GetFlag(FLAGS_optional_int).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_int), 10);
+
+ absl::SetFlag(&FLAGS_optional_int, absl::nullopt);
+ EXPECT_FALSE(absl::GetFlag(FLAGS_optional_int).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_int), absl::nullopt);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(FlagTest, TestOptionalDouble) {
+ EXPECT_TRUE(absl::GetFlag(FLAGS_optional_double).has_value());
+ EXPECT_DOUBLE_EQ(*absl::GetFlag(FLAGS_optional_double), 9.3);
+
+ absl::SetFlag(&FLAGS_optional_double, 0.0);
+ EXPECT_TRUE(absl::GetFlag(FLAGS_optional_double).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_double), 0.0);
+
+ absl::SetFlag(&FLAGS_optional_double, 1.234);
+ EXPECT_TRUE(absl::GetFlag(FLAGS_optional_double).has_value());
+ EXPECT_DOUBLE_EQ(*absl::GetFlag(FLAGS_optional_double), 1.234);
+
+ absl::SetFlag(&FLAGS_optional_double, absl::nullopt);
+ EXPECT_FALSE(absl::GetFlag(FLAGS_optional_double).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_double), absl::nullopt);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(FlagTest, TestOptionalString) {
+ EXPECT_FALSE(absl::GetFlag(FLAGS_optional_string).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_string), absl::nullopt);
+
+ // Setting optional string to "" leads to undefined behavior.
+
+ absl::SetFlag(&FLAGS_optional_string, " ");
+ EXPECT_TRUE(absl::GetFlag(FLAGS_optional_string).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_string), " ");
+
+ absl::SetFlag(&FLAGS_optional_string, "QWERTY");
+ EXPECT_TRUE(absl::GetFlag(FLAGS_optional_string).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_string), "QWERTY");
+
+ absl::SetFlag(&FLAGS_optional_string, absl::nullopt);
+ EXPECT_FALSE(absl::GetFlag(FLAGS_optional_string).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_string), absl::nullopt);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(FlagTest, TestOptionalDuration) {
+ EXPECT_FALSE(absl::GetFlag(FLAGS_optional_duration).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_duration), absl::nullopt);
+
+ absl::SetFlag(&FLAGS_optional_duration, absl::ZeroDuration());
+ EXPECT_TRUE(absl::GetFlag(FLAGS_optional_duration).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_duration), absl::Seconds(0));
+
+ absl::SetFlag(&FLAGS_optional_duration, absl::Hours(3));
+ EXPECT_TRUE(absl::GetFlag(FLAGS_optional_duration).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_duration), absl::Hours(3));
+
+ absl::SetFlag(&FLAGS_optional_duration, absl::nullopt);
+ EXPECT_FALSE(absl::GetFlag(FLAGS_optional_duration).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_duration), absl::nullopt);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(FlagTest, TestOptionalOptional) {
+ EXPECT_FALSE(absl::GetFlag(FLAGS_optional_optional_int).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int), absl::nullopt);
+
+ absl::optional<int> nullint{absl::nullopt};
+
+ absl::SetFlag(&FLAGS_optional_optional_int, nullint);
+ EXPECT_TRUE(absl::GetFlag(FLAGS_optional_optional_int).has_value());
+ EXPECT_NE(absl::GetFlag(FLAGS_optional_optional_int), nullint);
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int),
+ absl::optional<absl::optional<int>>{nullint});
+
+ absl::SetFlag(&FLAGS_optional_optional_int, 0);
+ EXPECT_TRUE(absl::GetFlag(FLAGS_optional_optional_int).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int), 0);
+
+ absl::SetFlag(&FLAGS_optional_optional_int, absl::optional<int>{0});
+ EXPECT_TRUE(absl::GetFlag(FLAGS_optional_optional_int).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int), 0);
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int), absl::optional<int>{0});
+
+ absl::SetFlag(&FLAGS_optional_optional_int, absl::nullopt);
+ EXPECT_FALSE(absl::GetFlag(FLAGS_optional_optional_int).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int), absl::nullopt);
+}
+
+// --------------------------------------------------------------------
+
+#if defined(ABSL_HAVE_STD_OPTIONAL) && !defined(ABSL_USES_STD_OPTIONAL)
+
+TEST_F(FlagTest, TestStdOptional) {
+ EXPECT_FALSE(absl::GetFlag(FLAGS_std_optional_int64).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_std_optional_int64), std::nullopt);
+
+ absl::SetFlag(&FLAGS_std_optional_int64, 0);
+ EXPECT_TRUE(absl::GetFlag(FLAGS_std_optional_int64).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_std_optional_int64), 0);
+
+ absl::SetFlag(&FLAGS_std_optional_int64, 0xFFFFFFFFFF16);
+ EXPECT_TRUE(absl::GetFlag(FLAGS_std_optional_int64).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_std_optional_int64), 0xFFFFFFFFFF16);
+
+ absl::SetFlag(&FLAGS_std_optional_int64, std::nullopt);
+ EXPECT_FALSE(absl::GetFlag(FLAGS_std_optional_int64).has_value());
+ EXPECT_EQ(absl::GetFlag(FLAGS_std_optional_int64), std::nullopt);
+}
+
+// --------------------------------------------------------------------
+
+#endif
+
+} // namespace
diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc
index f83c1fe7..55892d77 100644
--- a/absl/flags/internal/flag.cc
+++ b/absl/flags/internal/flag.cc
@@ -30,6 +30,7 @@
#include "absl/base/call_once.h"
#include "absl/base/casts.h"
#include "absl/base/config.h"
+#include "absl/base/dynamic_annotations.h"
#include "absl/base/optimization.h"
#include "absl/flags/config.h"
#include "absl/flags/internal/commandlineflag.h"
@@ -145,12 +146,7 @@ void FlagImpl::Init() {
auto def_kind = static_cast<FlagDefaultKind>(def_kind_);
switch (ValueStorageKind()) {
- case FlagValueStorageKind::kAlignedBuffer:
- // For this storage kind the default_value_ always points to gen_func
- // during initialization.
- assert(def_kind == FlagDefaultKind::kGenFunc);
- (*default_value_.gen_func)(AlignedBufferValue());
- break;
+ case FlagValueStorageKind::kValueAndInitBit:
case FlagValueStorageKind::kOneWordAtomic: {
alignas(int64_t) std::array<char, sizeof(int64_t)> buf{};
if (def_kind == FlagDefaultKind::kGenFunc) {
@@ -159,6 +155,14 @@ void FlagImpl::Init() {
assert(def_kind != FlagDefaultKind::kDynamicValue);
std::memcpy(buf.data(), &default_value_, Sizeof(op_));
}
+ if (ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit) {
+ // We presume here the memory layout of FlagValueAndInitBit struct.
+ uint8_t initialized = 1;
+ std::memcpy(buf.data() + Sizeof(op_), &initialized,
+ sizeof(initialized));
+ }
+ // Type can contain valid uninitialized bits, e.g. padding.
+ ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(buf.data(), buf.size());
OneWordValue().store(absl::bit_cast<int64_t>(buf),
std::memory_order_release);
break;
@@ -170,6 +174,12 @@ void FlagImpl::Init() {
(*default_value_.gen_func)(AtomicBufferValue());
break;
}
+ case FlagValueStorageKind::kAlignedBuffer:
+ // For this storage kind the default_value_ always points to gen_func
+ // during initialization.
+ assert(def_kind == FlagDefaultKind::kGenFunc);
+ (*default_value_.gen_func)(AlignedBufferValue());
+ break;
}
seq_lock_.MarkInitialized();
}
@@ -198,7 +208,7 @@ void FlagImpl::AssertValidType(FlagFastTypeId rhs_type_id,
if (lhs_runtime_type_id == rhs_runtime_type_id) return;
-#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI)
+#ifdef ABSL_INTERNAL_HAS_RTTI
if (*lhs_runtime_type_id == *rhs_runtime_type_id) return;
#endif
@@ -226,12 +236,10 @@ std::unique_ptr<void, DynValueDeleter> FlagImpl::MakeInitValue() const {
void FlagImpl::StoreValue(const void* src) {
switch (ValueStorageKind()) {
- case FlagValueStorageKind::kAlignedBuffer:
- Copy(op_, src, AlignedBufferValue());
- seq_lock_.IncrementModificationCount();
- break;
+ case FlagValueStorageKind::kValueAndInitBit:
case FlagValueStorageKind::kOneWordAtomic: {
- int64_t one_word_val = 0;
+ // Load the current value to avoid setting 'init' bit manualy.
+ int64_t one_word_val = OneWordValue().load(std::memory_order_acquire);
std::memcpy(&one_word_val, src, Sizeof(op_));
OneWordValue().store(one_word_val, std::memory_order_release);
seq_lock_.IncrementModificationCount();
@@ -241,6 +249,10 @@ void FlagImpl::StoreValue(const void* src) {
seq_lock_.Write(AtomicBufferValue(), src, Sizeof(op_));
break;
}
+ case FlagValueStorageKind::kAlignedBuffer:
+ Copy(op_, src, AlignedBufferValue());
+ seq_lock_.IncrementModificationCount();
+ break;
}
modified_ = true;
InvokeCallback();
@@ -280,10 +292,7 @@ std::string FlagImpl::DefaultValue() const {
std::string FlagImpl::CurrentValue() const {
auto* guard = DataGuard(); // Make sure flag initialized
switch (ValueStorageKind()) {
- case FlagValueStorageKind::kAlignedBuffer: {
- absl::MutexLock l(guard);
- return flags_internal::Unparse(op_, AlignedBufferValue());
- }
+ case FlagValueStorageKind::kValueAndInitBit:
case FlagValueStorageKind::kOneWordAtomic: {
const auto one_word_val =
absl::bit_cast<std::array<char, sizeof(int64_t)>>(
@@ -296,6 +305,10 @@ std::string FlagImpl::CurrentValue() const {
ReadSequenceLockedData(cloned.get());
return flags_internal::Unparse(op_, cloned.get());
}
+ case FlagValueStorageKind::kAlignedBuffer: {
+ absl::MutexLock l(guard);
+ return flags_internal::Unparse(op_, AlignedBufferValue());
+ }
}
return "";
@@ -341,11 +354,7 @@ std::unique_ptr<FlagStateInterface> FlagImpl::SaveState() {
bool modified = modified_;
bool on_command_line = on_command_line_;
switch (ValueStorageKind()) {
- case FlagValueStorageKind::kAlignedBuffer: {
- return absl::make_unique<FlagState>(
- *this, flags_internal::Clone(op_, AlignedBufferValue()), modified,
- on_command_line, ModificationCount());
- }
+ case FlagValueStorageKind::kValueAndInitBit:
case FlagValueStorageKind::kOneWordAtomic: {
return absl::make_unique<FlagState>(
*this, OneWordValue().load(std::memory_order_acquire), modified,
@@ -361,6 +370,11 @@ std::unique_ptr<FlagStateInterface> FlagImpl::SaveState() {
return absl::make_unique<FlagState>(*this, cloned, modified,
on_command_line, ModificationCount());
}
+ case FlagValueStorageKind::kAlignedBuffer: {
+ return absl::make_unique<FlagState>(
+ *this, flags_internal::Clone(op_, AlignedBufferValue()), modified,
+ on_command_line, ModificationCount());
+ }
}
return nullptr;
}
@@ -372,13 +386,14 @@ bool FlagImpl::RestoreState(const FlagState& flag_state) {
}
switch (ValueStorageKind()) {
- case FlagValueStorageKind::kAlignedBuffer:
- case FlagValueStorageKind::kSequenceLocked:
- StoreValue(flag_state.value_.heap_allocated);
- break;
+ case FlagValueStorageKind::kValueAndInitBit:
case FlagValueStorageKind::kOneWordAtomic:
StoreValue(&flag_state.value_.one_word);
break;
+ case FlagValueStorageKind::kSequenceLocked:
+ case FlagValueStorageKind::kAlignedBuffer:
+ StoreValue(flag_state.value_.heap_allocated);
+ break;
}
modified_ = flag_state.modified_;
@@ -407,7 +422,8 @@ std::atomic<uint64_t>* FlagImpl::AtomicBufferValue() const {
}
std::atomic<int64_t>& FlagImpl::OneWordValue() const {
- assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic);
+ assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic ||
+ ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit);
return OffsetValue<FlagOneWordValue>()->value;
}
@@ -433,11 +449,7 @@ std::unique_ptr<void, DynValueDeleter> FlagImpl::TryParse(
void FlagImpl::Read(void* dst) const {
auto* guard = DataGuard(); // Make sure flag initialized
switch (ValueStorageKind()) {
- case FlagValueStorageKind::kAlignedBuffer: {
- absl::MutexLock l(guard);
- flags_internal::CopyConstruct(op_, AlignedBufferValue(), dst);
- break;
- }
+ case FlagValueStorageKind::kValueAndInitBit:
case FlagValueStorageKind::kOneWordAtomic: {
const int64_t one_word_val =
OneWordValue().load(std::memory_order_acquire);
@@ -448,9 +460,31 @@ void FlagImpl::Read(void* dst) const {
ReadSequenceLockedData(dst);
break;
}
+ case FlagValueStorageKind::kAlignedBuffer: {
+ absl::MutexLock l(guard);
+ flags_internal::CopyConstruct(op_, AlignedBufferValue(), dst);
+ break;
+ }
}
}
+int64_t FlagImpl::ReadOneWord() const {
+ assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic ||
+ ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit);
+ auto* guard = DataGuard(); // Make sure flag initialized
+ (void)guard;
+ return OneWordValue().load(std::memory_order_acquire);
+}
+
+bool FlagImpl::ReadOneBool() const {
+ assert(ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit);
+ auto* guard = DataGuard(); // Make sure flag initialized
+ (void)guard;
+ return absl::bit_cast<FlagValueAndInitBit<bool>>(
+ OneWordValue().load(std::memory_order_acquire))
+ .value;
+}
+
void FlagImpl::ReadSequenceLockedData(void* dst) const {
int size = Sizeof(op_);
// Attempt to read using the sequence lock.
diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h
index e6bade0a..6154638c 100644
--- a/absl/flags/internal/flag.h
+++ b/absl/flags/internal/flag.h
@@ -29,6 +29,7 @@
#include "absl/base/attributes.h"
#include "absl/base/call_once.h"
+#include "absl/base/casts.h"
#include "absl/base/config.h"
#include "absl/base/optimization.h"
#include "absl/base/thread_annotations.h"
@@ -162,7 +163,7 @@ inline ptrdiff_t ValueOffset(FlagOpFn op) {
// Returns an address of RTTI's typeid(T).
template <typename T>
inline const std::type_info* GenRuntimeTypeId() {
-#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI)
+#ifdef ABSL_INTERNAL_HAS_RTTI
return &typeid(T);
#else
return nullptr;
@@ -289,7 +290,7 @@ constexpr T InitDefaultValue(EmptyBraces) {
template <typename ValueT, typename GenT,
typename std::enable_if<std::is_integral<ValueT>::value, int>::type =
- (GenT{}, 0)>
+ ((void)GenT{}, 0)>
constexpr FlagDefaultArg DefaultArg(int) {
return {FlagDefaultSrc(GenT{}.value), FlagDefaultKind::kOneWord};
}
@@ -302,7 +303,14 @@ constexpr FlagDefaultArg DefaultArg(char) {
///////////////////////////////////////////////////////////////////////////////
// Flag current value auxiliary structs.
-constexpr int64_t UninitializedFlagValue() { return 0xababababababababll; }
+constexpr int64_t UninitializedFlagValue() {
+ return static_cast<int64_t>(0xababababababababll);
+}
+
+template <typename T>
+using FlagUseValueAndInitBitStorage = std::integral_constant<
+ bool, absl::type_traits_internal::is_trivially_copyable<T>::value &&
+ std::is_default_constructible<T>::value && (sizeof(T) < 8)>;
template <typename T>
using FlagUseOneWordStorage = std::integral_constant<
@@ -310,43 +318,61 @@ using FlagUseOneWordStorage = std::integral_constant<
(sizeof(T) <= 8)>;
template <class T>
-using FlagShouldUseSequenceLock = std::integral_constant<
+using FlagUseSequenceLockStorage = std::integral_constant<
bool, absl::type_traits_internal::is_trivially_copyable<T>::value &&
(sizeof(T) > 8)>;
enum class FlagValueStorageKind : uint8_t {
- kAlignedBuffer = 0,
+ kValueAndInitBit = 0,
kOneWordAtomic = 1,
kSequenceLocked = 2,
+ kAlignedBuffer = 3,
};
template <typename T>
static constexpr FlagValueStorageKind StorageKind() {
- return FlagUseOneWordStorage<T>::value ? FlagValueStorageKind::kOneWordAtomic
- : FlagShouldUseSequenceLock<T>::value
+ return FlagUseValueAndInitBitStorage<T>::value
+ ? FlagValueStorageKind::kValueAndInitBit
+ : FlagUseOneWordStorage<T>::value
+ ? FlagValueStorageKind::kOneWordAtomic
+ : FlagUseSequenceLockStorage<T>::value
? FlagValueStorageKind::kSequenceLocked
: FlagValueStorageKind::kAlignedBuffer;
}
struct FlagOneWordValue {
- constexpr FlagOneWordValue() : value(UninitializedFlagValue()) {}
-
+ constexpr explicit FlagOneWordValue(int64_t v) : value(v) {}
std::atomic<int64_t> value;
};
+template <typename T>
+struct alignas(8) FlagValueAndInitBit {
+ T value;
+ // Use an int instead of a bool to guarantee that a non-zero value has
+ // a bit set.
+ uint8_t init;
+};
+
template <typename T,
FlagValueStorageKind Kind = flags_internal::StorageKind<T>()>
struct FlagValue;
template <typename T>
-struct FlagValue<T, FlagValueStorageKind::kAlignedBuffer> {
- bool Get(const SequenceLock&, T&) const { return false; }
-
- alignas(T) char value[sizeof(T)];
+struct FlagValue<T, FlagValueStorageKind::kValueAndInitBit> : FlagOneWordValue {
+ constexpr FlagValue() : FlagOneWordValue(0) {}
+ bool Get(const SequenceLock&, T& dst) const {
+ int64_t storage = value.load(std::memory_order_acquire);
+ if (ABSL_PREDICT_FALSE(storage == 0)) {
+ return false;
+ }
+ dst = absl::bit_cast<FlagValueAndInitBit<T>>(storage).value;
+ return true;
+ }
};
template <typename T>
struct FlagValue<T, FlagValueStorageKind::kOneWordAtomic> : FlagOneWordValue {
+ constexpr FlagValue() : FlagOneWordValue(UninitializedFlagValue()) {}
bool Get(const SequenceLock&, T& dst) const {
int64_t one_word_val = value.load(std::memory_order_acquire);
if (ABSL_PREDICT_FALSE(one_word_val == UninitializedFlagValue())) {
@@ -370,6 +396,13 @@ struct FlagValue<T, FlagValueStorageKind::kSequenceLocked> {
std::atomic<uint64_t>) std::atomic<uint64_t> value_words[kNumWords];
};
+template <typename T>
+struct FlagValue<T, FlagValueStorageKind::kAlignedBuffer> {
+ bool Get(const SequenceLock&, T&) const { return false; }
+
+ alignas(T) char value[sizeof(T)];
+};
+
///////////////////////////////////////////////////////////////////////////////
// Flag callback auxiliary structs.
@@ -415,7 +448,27 @@ class FlagImpl final : public CommandLineFlag {
data_guard_{} {}
// Constant access methods
+ int64_t ReadOneWord() const ABSL_LOCKS_EXCLUDED(*DataGuard());
+ bool ReadOneBool() const ABSL_LOCKS_EXCLUDED(*DataGuard());
void Read(void* dst) const override ABSL_LOCKS_EXCLUDED(*DataGuard());
+ void Read(bool* value) const ABSL_LOCKS_EXCLUDED(*DataGuard()) {
+ *value = ReadOneBool();
+ }
+ template <typename T,
+ absl::enable_if_t<flags_internal::StorageKind<T>() ==
+ FlagValueStorageKind::kOneWordAtomic,
+ int> = 0>
+ void Read(T* value) const ABSL_LOCKS_EXCLUDED(*DataGuard()) {
+ int64_t v = ReadOneWord();
+ std::memcpy(value, static_cast<const void*>(&v), sizeof(T));
+ }
+ template <typename T,
+ typename std::enable_if<flags_internal::StorageKind<T>() ==
+ FlagValueStorageKind::kValueAndInitBit,
+ int>::type = 0>
+ void Read(T* value) const ABSL_LOCKS_EXCLUDED(*DataGuard()) {
+ *value = absl::bit_cast<FlagValueAndInitBit<T>>(ReadOneWord()).value;
+ }
// Mutating access methods
void Write(const void* src) ABSL_LOCKS_EXCLUDED(*DataGuard());
@@ -704,8 +757,8 @@ void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3) {
case FlagOp::kValueOffset: {
// Round sizeof(FlagImp) to a multiple of alignof(FlagValue<T>) to get the
// offset of the data.
- ptrdiff_t round_to = alignof(FlagValue<T>);
- ptrdiff_t offset =
+ size_t round_to = alignof(FlagValue<T>);
+ size_t offset =
(sizeof(FlagImpl) + round_to - 1) / round_to * round_to;
return reinterpret_cast<void*>(offset);
}
diff --git a/absl/flags/internal/flag_msvc.inc b/absl/flags/internal/flag_msvc.inc
new file mode 100644
index 00000000..c31bd27f
--- /dev/null
+++ b/absl/flags/internal/flag_msvc.inc
@@ -0,0 +1,116 @@
+//
+// Copyright 2021 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.
+
+// Do not include this file directly.
+// Include absl/flags/flag.h instead.
+
+// MSVC debug builds do not implement initialization with constexpr constructors
+// correctly. To work around this we add a level of indirection, so that the
+// class `absl::Flag` contains an `internal::Flag*` (instead of being an alias
+// to that class) and dynamically allocates an instance when necessary. We also
+// forward all calls to internal::Flag methods via trampoline methods. In this
+// setup the `absl::Flag` class does not have constructor and virtual methods,
+// all the data members are public and thus MSVC is able to initialize it at
+// link time. To deal with multiple threads accessing the flag for the first
+// time concurrently we use an atomic boolean indicating if flag object is
+// initialized. We also employ the double-checked locking pattern where the
+// second level of protection is a global Mutex, so if two threads attempt to
+// construct the flag concurrently only one wins.
+//
+// This solution is based on a recomendation here:
+// https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html?childToView=648454#comment-648454
+
+namespace flags_internal {
+absl::Mutex* GetGlobalConstructionGuard();
+} // namespace flags_internal
+
+// Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API.
+// See https://abseil.io/docs/cpp/guides/flags
+template <typename T>
+class Flag {
+ public:
+ // No constructor and destructor to ensure this is an aggregate type.
+ // Visual Studio 2015 still requires the constructor for class to be
+ // constexpr initializable.
+#if _MSC_VER <= 1900
+ constexpr Flag(const char* name, const char* filename,
+ const flags_internal::HelpGenFunc help_gen,
+ const flags_internal::FlagDfltGenFunc default_value_gen)
+ : name_(name),
+ filename_(filename),
+ help_gen_(help_gen),
+ default_value_gen_(default_value_gen),
+ inited_(false),
+ impl_(nullptr) {}
+#endif
+
+ flags_internal::Flag<T>& GetImpl() const {
+ if (!inited_.load(std::memory_order_acquire)) {
+ absl::MutexLock l(flags_internal::GetGlobalConstructionGuard());
+
+ if (inited_.load(std::memory_order_acquire)) {
+ return *impl_;
+ }
+
+ impl_ = new flags_internal::Flag<T>(
+ name_, filename_,
+ {flags_internal::FlagHelpMsg(help_gen_),
+ flags_internal::FlagHelpKind::kGenFunc},
+ {flags_internal::FlagDefaultSrc(default_value_gen_),
+ flags_internal::FlagDefaultKind::kGenFunc});
+ inited_.store(true, std::memory_order_release);
+ }
+
+ return *impl_;
+ }
+
+ // Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API.
+ // See https://abseil.io/docs/cpp/guides/flags
+ bool IsRetired() const { return GetImpl().IsRetired(); }
+ absl::string_view Name() const { return GetImpl().Name(); }
+ std::string Help() const { return GetImpl().Help(); }
+ bool IsModified() const { return GetImpl().IsModified(); }
+ bool IsSpecifiedOnCommandLine() const {
+ return GetImpl().IsSpecifiedOnCommandLine();
+ }
+ std::string Filename() const { return GetImpl().Filename(); }
+ std::string DefaultValue() const { return GetImpl().DefaultValue(); }
+ std::string CurrentValue() const { return GetImpl().CurrentValue(); }
+ template <typename U>
+ inline bool IsOfType() const {
+ return GetImpl().template IsOfType<U>();
+ }
+ T Get() const {
+ return flags_internal::FlagImplPeer::InvokeGet<T>(GetImpl());
+ }
+ void Set(const T& v) {
+ flags_internal::FlagImplPeer::InvokeSet(GetImpl(), v);
+ }
+ void InvokeCallback() { GetImpl().InvokeCallback(); }
+
+ const CommandLineFlag& Reflect() const {
+ return flags_internal::FlagImplPeer::InvokeReflect(GetImpl());
+ }
+
+ // The data members are logically private, but they need to be public for
+ // this to be an aggregate type.
+ const char* name_;
+ const char* filename_;
+ const flags_internal::HelpGenFunc help_gen_;
+ const flags_internal::FlagDfltGenFunc default_value_gen_;
+
+ mutable std::atomic<bool> inited_;
+ mutable flags_internal::Flag<T>* impl_;
+};
diff --git a/absl/flags/internal/sequence_lock.h b/absl/flags/internal/sequence_lock.h
index 807b2a73..36318ab9 100644
--- a/absl/flags/internal/sequence_lock.h
+++ b/absl/flags/internal/sequence_lock.h
@@ -49,7 +49,7 @@ inline constexpr size_t AlignUp(size_t x, size_t align) {
// The memory reads and writes protected by this lock must use the provided
// `TryRead()` and `Write()` functions. These functions behave similarly to
// `memcpy()`, with one oddity: the protected data must be an array of
-// `std::atomic<int64>`. This is to comply with the C++ standard, which
+// `std::atomic<uint64>`. This is to comply with the C++ standard, which
// considers data races on non-atomic objects to be undefined behavior. See "Can
// Seqlocks Get Along With Programming Language Memory Models?"[1] by Hans J.
// Boehm for more details.
diff --git a/absl/flags/internal/usage.cc b/absl/flags/internal/usage.cc
index a588c7f7..949709e8 100644
--- a/absl/flags/internal/usage.cc
+++ b/absl/flags/internal/usage.cc
@@ -245,7 +245,7 @@ void FlagsHelpImpl(std::ostream& out, PerFlagFilter filter_cb,
<< XMLElement("usage", program_usage_message) << '\n';
}
- // Map of package name to
+ // Ordered map of package name to
// map of file name to
// vector of flags in the file.
// This map is used to output matching flags grouped by package and file
@@ -273,20 +273,26 @@ void FlagsHelpImpl(std::ostream& out, PerFlagFilter filter_cb,
absl::string_view package_separator; // controls blank lines between packages
absl::string_view file_separator; // controls blank lines between files
- for (const auto& package : matching_flags) {
+ for (auto& package : matching_flags) {
if (format == HelpFormat::kHumanReadable) {
out << package_separator;
package_separator = "\n\n";
}
file_separator = "";
- for (const auto& flags_in_file : package.second) {
+ for (auto& flags_in_file : package.second) {
if (format == HelpFormat::kHumanReadable) {
out << file_separator << " Flags from " << flags_in_file.first
<< ":\n";
file_separator = "\n";
}
+ std::sort(std::begin(flags_in_file.second),
+ std::end(flags_in_file.second),
+ [](const CommandLineFlag* lhs, const CommandLineFlag* rhs) {
+ return lhs->Name() < rhs->Name();
+ });
+
for (const auto* flag : flags_in_file.second) {
flags_internal::FlagHelp(out, *flag, format);
}
diff --git a/absl/flags/internal/usage_test.cc b/absl/flags/internal/usage_test.cc
index b5c2487d..209a7be9 100644
--- a/absl/flags/internal/usage_test.cc
+++ b/absl/flags/internal/usage_test.cc
@@ -20,6 +20,7 @@
#include <sstream>
#include <string>
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/flags/flag.h"
#include "absl/flags/internal/parse.h"
@@ -45,9 +46,12 @@ static const char kTestUsageMessage[] = "Custom usage message";
struct UDT {
UDT() = default;
UDT(const UDT&) = default;
+ UDT& operator=(const UDT&) = default;
};
-bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; }
-std::string AbslUnparseFlag(const UDT&) { return "UDT{}"; }
+static bool AbslParseFlag(absl::string_view, UDT*, std::string*) {
+ return true;
+}
+static std::string AbslUnparseFlag(const UDT&) { return "UDT{}"; }
ABSL_FLAG(UDT, usage_reporting_test_flag_05, {},
"usage_reporting_test_flag_05 help message");
@@ -102,14 +106,19 @@ class UsageReportingTest : public testing::Test {
using UsageReportingDeathTest = UsageReportingTest;
TEST_F(UsageReportingDeathTest, TestSetProgramUsageMessage) {
+#if !defined(GTEST_HAS_ABSL) || !GTEST_HAS_ABSL
+ // Check for kTestUsageMessage set in main() below.
EXPECT_EQ(absl::ProgramUsageMessage(), kTestUsageMessage);
+#else
+ // Check for part of the usage message set by GoogleTest.
+ EXPECT_THAT(absl::ProgramUsageMessage(),
+ ::testing::HasSubstr(
+ "This program contains tests written using Google Test"));
+#endif
-#ifndef _WIN32
- // TODO(rogeeff): figure out why this does not work on Windows.
EXPECT_DEATH_IF_SUPPORTED(
absl::SetProgramUsageMessage("custom usage message"),
- ".*SetProgramUsageMessage\\(\\) called twice.*");
-#endif
+ ::testing::HasSubstr("SetProgramUsageMessage() called twice"));
}
// --------------------------------------------------------------------
@@ -486,8 +495,10 @@ path.
int main(int argc, char* argv[]) {
(void)absl::GetFlag(FLAGS_undefok); // Force linking of parse.cc
flags::SetProgramInvocationName("usage_test");
+#if !defined(GTEST_HAS_ABSL) || !GTEST_HAS_ABSL
+ // GoogleTest calls absl::SetProgramUsageMessage() already.
absl::SetProgramUsageMessage(kTestUsageMessage);
+#endif
::testing::InitGoogleTest(&argc, argv);
-
return RUN_ALL_TESTS();
}
diff --git a/absl/flags/marshalling.h b/absl/flags/marshalling.h
index 7cbc136d..b1e2ffa5 100644
--- a/absl/flags/marshalling.h
+++ b/absl/flags/marshalling.h
@@ -33,6 +33,7 @@
// * `double`
// * `std::string`
// * `std::vector<std::string>`
+// * `std::optional<T>`
// * `absl::LogSeverity` (provided natively for layering reasons)
//
// Note that support for integral types is implemented using overloads for
@@ -65,6 +66,42 @@
// below.)
//
// -----------------------------------------------------------------------------
+// Optional Flags
+// -----------------------------------------------------------------------------
+//
+// The Abseil flags library supports flags of type `std::optional<T>` where
+// `T` is a type of one of the supported flags. We refer to this flag type as
+// an "optional flag." An optional flag is either "valueless", holding no value
+// of type `T` (indicating that the flag has not been set) or a value of type
+// `T`. The valueless state in C++ code is represented by a value of
+// `std::nullopt` for the optional flag.
+//
+// Using `std::nullopt` as an optional flag's default value allows you to check
+// whether such a flag was ever specified on the command line:
+//
+// if (absl::GetFlag(FLAGS_foo).has_value()) {
+// // flag was set on command line
+// } else {
+// // flag was not passed on command line
+// }
+//
+// Using an optional flag in this manner avoids common workarounds for
+// indicating such an unset flag (such as using sentinal values to indicate this
+// state).
+//
+// An optional flag also allows a developer to pass a flag in an "unset"
+// valueless state on the command line, allowing the flag to later be set in
+// binary logic. An optional flag's valueless state is indicated by the special
+// notation of passing the value as an empty string through the syntax `--flag=`
+// or `--flag ""`.
+//
+// $ binary_with_optional --flag_in_unset_state=
+// $ binary_with_optional --flag_in_unset_state ""
+//
+// Note: as a result of the above syntax requirements, an optional flag cannot
+// be set to a `T` of any value which unparses to the empty string.
+//
+// -----------------------------------------------------------------------------
// Adding Type Support for Abseil Flags
// -----------------------------------------------------------------------------
//
@@ -162,14 +199,27 @@
#ifndef ABSL_FLAGS_MARSHALLING_H_
#define ABSL_FLAGS_MARSHALLING_H_
+#include "absl/base/config.h"
+
+#if defined(ABSL_HAVE_STD_OPTIONAL) && !defined(ABSL_USES_STD_OPTIONAL)
+#include <optional>
+#endif
#include <string>
#include <vector>
-#include "absl/base/config.h"
#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
+
+// Forward declaration to be used inside composable flag parse/unparse
+// implementations
+template <typename T>
+inline bool ParseFlag(absl::string_view input, T* dst, std::string* error);
+template <typename T>
+inline std::string UnparseFlag(const T& v);
+
namespace flags_internal {
// Overloads of `AbslParseFlag()` and `AbslUnparseFlag()` for fundamental types.
@@ -189,6 +239,36 @@ bool AbslParseFlag(absl::string_view, std::string*, std::string*);
bool AbslParseFlag(absl::string_view, std::vector<std::string>*, std::string*);
template <typename T>
+bool AbslParseFlag(absl::string_view text, absl::optional<T>* f,
+ std::string* err) {
+ if (text.empty()) {
+ *f = absl::nullopt;
+ return true;
+ }
+ T value;
+ if (!absl::ParseFlag(text, &value, err)) return false;
+
+ *f = std::move(value);
+ return true;
+}
+
+#if defined(ABSL_HAVE_STD_OPTIONAL) && !defined(ABSL_USES_STD_OPTIONAL)
+template <typename T>
+bool AbslParseFlag(absl::string_view text, std::optional<T>* f,
+ std::string* err) {
+ if (text.empty()) {
+ *f = std::nullopt;
+ return true;
+ }
+ T value;
+ if (!absl::ParseFlag(text, &value, err)) return false;
+
+ *f = std::move(value);
+ return true;
+}
+#endif
+
+template <typename T>
bool InvokeParseFlag(absl::string_view input, T* dst, std::string* err) {
// Comment on next line provides a good compiler error message if T
// does not have AbslParseFlag(absl::string_view, T*, std::string*).
@@ -202,6 +282,18 @@ std::string AbslUnparseFlag(absl::string_view v);
std::string AbslUnparseFlag(const std::vector<std::string>&);
template <typename T>
+std::string AbslUnparseFlag(const absl::optional<T>& f) {
+ return f.has_value() ? absl::UnparseFlag(*f) : "";
+}
+
+#if defined(ABSL_HAVE_STD_OPTIONAL) && !defined(ABSL_USES_STD_OPTIONAL)
+template <typename T>
+std::string AbslUnparseFlag(const std::optional<T>& f) {
+ return f.has_value() ? absl::UnparseFlag(*f) : "";
+}
+#endif
+
+template <typename T>
std::string Unparse(const T& v) {
// Comment on next line provides a good compiler error message if T does not
// have UnparseFlag.
diff --git a/absl/flags/marshalling_test.cc b/absl/flags/marshalling_test.cc
index 4a64ce11..7b6d2ad5 100644
--- a/absl/flags/marshalling_test.cc
+++ b/absl/flags/marshalling_test.cc
@@ -659,6 +659,88 @@ TEST(MarshallingTest, TestVectorOfStringParsing) {
// --------------------------------------------------------------------
+TEST(MarshallingTest, TestOptionalBoolParsing) {
+ std::string err;
+ absl::optional<bool> value;
+
+ EXPECT_TRUE(absl::ParseFlag("", &value, &err));
+ EXPECT_FALSE(value.has_value());
+
+ EXPECT_TRUE(absl::ParseFlag("true", &value, &err));
+ EXPECT_TRUE(value.has_value());
+ EXPECT_TRUE(*value);
+
+ EXPECT_TRUE(absl::ParseFlag("false", &value, &err));
+ EXPECT_TRUE(value.has_value());
+ EXPECT_FALSE(*value);
+
+ EXPECT_FALSE(absl::ParseFlag("nullopt", &value, &err));
+}
+
+// --------------------------------------------------------------------
+
+TEST(MarshallingTest, TestOptionalIntParsing) {
+ std::string err;
+ absl::optional<int> value;
+
+ EXPECT_TRUE(absl::ParseFlag("", &value, &err));
+ EXPECT_FALSE(value.has_value());
+
+ EXPECT_TRUE(absl::ParseFlag("10", &value, &err));
+ EXPECT_TRUE(value.has_value());
+ EXPECT_EQ(*value, 10);
+
+ EXPECT_TRUE(absl::ParseFlag("0x1F", &value, &err));
+ EXPECT_TRUE(value.has_value());
+ EXPECT_EQ(*value, 31);
+
+ EXPECT_FALSE(absl::ParseFlag("nullopt", &value, &err));
+}
+
+// --------------------------------------------------------------------
+
+TEST(MarshallingTest, TestOptionalDoubleParsing) {
+ std::string err;
+ absl::optional<double> value;
+
+ EXPECT_TRUE(absl::ParseFlag("", &value, &err));
+ EXPECT_FALSE(value.has_value());
+
+ EXPECT_TRUE(absl::ParseFlag("1.11", &value, &err));
+ EXPECT_TRUE(value.has_value());
+ EXPECT_EQ(*value, 1.11);
+
+ EXPECT_TRUE(absl::ParseFlag("-0.12", &value, &err));
+ EXPECT_TRUE(value.has_value());
+ EXPECT_EQ(*value, -0.12);
+
+ EXPECT_FALSE(absl::ParseFlag("nullopt", &value, &err));
+}
+
+// --------------------------------------------------------------------
+
+TEST(MarshallingTest, TestOptionalStringParsing) {
+ std::string err;
+ absl::optional<std::string> value;
+
+ EXPECT_TRUE(absl::ParseFlag("", &value, &err));
+ EXPECT_FALSE(value.has_value());
+
+ EXPECT_TRUE(absl::ParseFlag(" ", &value, &err));
+ EXPECT_TRUE(value.has_value());
+ EXPECT_EQ(*value, " ");
+
+ EXPECT_TRUE(absl::ParseFlag("aqswde", &value, &err));
+ EXPECT_TRUE(value.has_value());
+ EXPECT_EQ(*value, "aqswde");
+
+ EXPECT_TRUE(absl::ParseFlag("nullopt", &value, &err));
+ EXPECT_TRUE(value.has_value());
+ EXPECT_EQ(*value, "nullopt");
+}
+
+// --------------------------------------------------------------------
+
TEST(MarshallingTest, TestBoolUnparsing) {
EXPECT_EQ(absl::UnparseFlag(true), "true");
EXPECT_EQ(absl::UnparseFlag(false), "false");
@@ -808,6 +890,90 @@ TEST(MarshallingTest, TestStringUnparsing) {
// --------------------------------------------------------------------
+TEST(MarshallingTest, TestOptionalBoolUnparsing) {
+ absl::optional<bool> value;
+
+ EXPECT_EQ(absl::UnparseFlag(value), "");
+ value = true;
+ EXPECT_EQ(absl::UnparseFlag(value), "true");
+ value = false;
+ EXPECT_EQ(absl::UnparseFlag(value), "false");
+ value = absl::nullopt;
+ EXPECT_EQ(absl::UnparseFlag(value), "");
+}
+
+// --------------------------------------------------------------------
+
+TEST(MarshallingTest, TestOptionalIntUnparsing) {
+ absl::optional<int> value;
+
+ EXPECT_EQ(absl::UnparseFlag(value), "");
+ value = 0;
+ EXPECT_EQ(absl::UnparseFlag(value), "0");
+ value = -12;
+ EXPECT_EQ(absl::UnparseFlag(value), "-12");
+ value = absl::nullopt;
+ EXPECT_EQ(absl::UnparseFlag(value), "");
+}
+
+// --------------------------------------------------------------------
+
+TEST(MarshallingTest, TestOptionalDoubleUnparsing) {
+ absl::optional<double> value;
+
+ EXPECT_EQ(absl::UnparseFlag(value), "");
+ value = 1.;
+ EXPECT_EQ(absl::UnparseFlag(value), "1");
+ value = -1.23;
+ EXPECT_EQ(absl::UnparseFlag(value), "-1.23");
+ value = absl::nullopt;
+ EXPECT_EQ(absl::UnparseFlag(value), "");
+}
+
+// --------------------------------------------------------------------
+
+TEST(MarshallingTest, TestOptionalStringUnparsing) {
+ absl::optional<std::string> strvalue;
+ EXPECT_EQ(absl::UnparseFlag(strvalue), "");
+
+ strvalue = "asdfg";
+ EXPECT_EQ(absl::UnparseFlag(strvalue), "asdfg");
+
+ strvalue = " ";
+ EXPECT_EQ(absl::UnparseFlag(strvalue), " ");
+
+ strvalue = ""; // It is UB to set an optional string flag to ""
+ EXPECT_EQ(absl::UnparseFlag(strvalue), "");
+}
+
+// --------------------------------------------------------------------
+
+#if defined(ABSL_HAVE_STD_OPTIONAL) && !defined(ABSL_USES_STD_OPTIONAL)
+
+TEST(MarshallingTest, TestStdOptionalUnparsing) {
+ std::optional<std::string> strvalue;
+ EXPECT_EQ(absl::UnparseFlag(strvalue), "");
+
+ strvalue = "asdfg";
+ EXPECT_EQ(absl::UnparseFlag(strvalue), "asdfg");
+
+ strvalue = " ";
+ EXPECT_EQ(absl::UnparseFlag(strvalue), " ");
+
+ strvalue = ""; // It is UB to set an optional string flag to ""
+ EXPECT_EQ(absl::UnparseFlag(strvalue), "");
+
+ std::optional<int> intvalue;
+ EXPECT_EQ(absl::UnparseFlag(intvalue), "");
+
+ intvalue = 10;
+ EXPECT_EQ(absl::UnparseFlag(intvalue), "10");
+}
+
+// --------------------------------------------------------------------
+
+#endif
+
template <typename T>
void TestRoundtrip(T v) {
T new_v;
diff --git a/absl/flags/parse_test.cc b/absl/flags/parse_test.cc
index 41bc0bc6..8dc91db2 100644
--- a/absl/flags/parse_test.cc
+++ b/absl/flags/parse_test.cc
@@ -46,6 +46,7 @@ using absl::base_internal::ScopedSetEnv;
struct UDT {
UDT() = default;
UDT(const UDT&) = default;
+ UDT& operator=(const UDT&) = default;
UDT(int v) : value(v) {} // NOLINT
int value;
diff --git a/absl/flags/reflection.cc b/absl/flags/reflection.cc
index 0c761101..dbce4032 100644
--- a/absl/flags/reflection.cc
+++ b/absl/flags/reflection.cc
@@ -18,11 +18,11 @@
#include <assert.h>
#include <atomic>
-#include <map>
#include <string>
#include "absl/base/config.h"
#include "absl/base/thread_annotations.h"
+#include "absl/container/flat_hash_map.h"
#include "absl/flags/commandlineflag.h"
#include "absl/flags/internal/private_handle_accessor.h"
#include "absl/flags/internal/registry.h"
@@ -68,7 +68,7 @@ class FlagRegistry {
friend void FinalizeRegistry();
// The map from name to flag, for FindFlag().
- using FlagMap = std::map<absl::string_view, CommandLineFlag*>;
+ using FlagMap = absl::flat_hash_map<absl::string_view, CommandLineFlag*>;
using FlagIterator = FlagMap::iterator;
using FlagConstIterator = FlagMap::const_iterator;
FlagMap flags_;
@@ -204,6 +204,10 @@ void FinalizeRegistry() {
for (const auto& f : registry.flags_) {
registry.flat_flags_.push_back(f.second);
}
+ std::sort(std::begin(registry.flat_flags_), std::end(registry.flat_flags_),
+ [](const CommandLineFlag* lhs, const CommandLineFlag* rhs) {
+ return lhs->Name() < rhs->Name();
+ });
registry.flags_.clear();
registry.finalized_flags_.store(true, std::memory_order_release);
}