summaryrefslogtreecommitdiff
path: root/absl/flags
diff options
context:
space:
mode:
Diffstat (limited to 'absl/flags')
-rw-r--r--absl/flags/BUILD.bazel125
-rw-r--r--absl/flags/CMakeLists.txt93
-rw-r--r--absl/flags/config.h19
-rw-r--r--absl/flags/declare.h10
-rw-r--r--absl/flags/flag.cc40
-rw-r--r--absl/flags/flag.h170
-rw-r--r--absl/flags/flag_benchmark.cc111
-rw-r--r--absl/flags/flag_test.cc130
-rw-r--r--absl/flags/flag_test_defs.cc2
-rw-r--r--absl/flags/internal/commandlineflag.cc496
-rw-r--r--absl/flags/internal/commandlineflag.h414
-rw-r--r--absl/flags/internal/commandlineflag_test.cc35
-rw-r--r--absl/flags/internal/flag.cc382
-rw-r--r--absl/flags/internal/flag.h628
-rw-r--r--absl/flags/internal/parse.h5
-rw-r--r--absl/flags/internal/path_util.h5
-rw-r--r--absl/flags/internal/program_name.cc11
-rw-r--r--absl/flags/internal/program_name.h5
-rw-r--r--absl/flags/internal/program_name_test.cc3
-rw-r--r--absl/flags/internal/registry.cc258
-rw-r--r--absl/flags/internal/registry.h57
-rw-r--r--absl/flags/internal/type_erased.cc38
-rw-r--r--absl/flags/internal/type_erased.h17
-rw-r--r--absl/flags/internal/type_erased_test.cc14
-rw-r--r--absl/flags/internal/usage.cc42
-rw-r--r--absl/flags/internal/usage.h5
-rw-r--r--absl/flags/internal/usage_test.cc84
-rw-r--r--absl/flags/marshalling.cc53
-rw-r--r--absl/flags/marshalling.h21
-rw-r--r--absl/flags/marshalling_test.cc5
-rw-r--r--absl/flags/parse.cc35
-rw-r--r--absl/flags/parse.h5
-rw-r--r--absl/flags/parse_test.cc23
-rw-r--r--absl/flags/usage.cc13
-rw-r--r--absl/flags/usage.h5
-rw-r--r--absl/flags/usage_config.cc14
-rw-r--r--absl/flags/usage_config.h7
-rw-r--r--absl/flags/usage_config_test.cc3
38 files changed, 2049 insertions, 1334 deletions
diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel
index 2fe61eaa..cdb4e7e8 100644
--- a/absl/flags/BUILD.bazel
+++ b/absl/flags/BUILD.bazel
@@ -1,5 +1,5 @@
#
-# Copyright 2019 The Abseil Authors.
+# Copyright 2019 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.
@@ -14,6 +14,7 @@
# limitations under the License.
#
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
@@ -26,12 +27,35 @@ package(default_visibility = ["//visibility:public"])
licenses(["notice"]) # Apache 2.0
cc_library(
- name = "internal",
+ name = "flag_internal",
+ srcs = [
+ "internal/flag.cc",
+ ],
+ hdrs = [
+ "internal/flag.h",
+ ],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ visibility = ["//absl/base:__subpackages__"],
+ deps = [
+ ":config",
+ ":handle",
+ ":registry",
+ "//absl/base",
+ "//absl/base:config",
+ "//absl/base:core_headers",
+ "//absl/memory",
+ "//absl/strings",
+ "//absl/synchronization",
+ ],
+)
+
+cc_library(
+ name = "program_name",
srcs = [
"internal/program_name.cc",
],
hdrs = [
- "internal/path_util.h",
"internal/program_name.h",
],
copts = ABSL_DEFAULT_COPTS,
@@ -40,12 +64,31 @@ cc_library(
"//absl/flags:__pkg__",
],
deps = [
+ ":path_util",
+ "//absl/base:config",
+ "//absl/base:core_headers",
"//absl/strings",
"//absl/synchronization",
],
)
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 = [
"usage_config.cc",
@@ -57,7 +100,9 @@ cc_library(
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
- ":internal",
+ ":path_util",
+ ":program_name",
+ "//absl/base:config",
"//absl/base:core_headers",
"//absl/strings",
"//absl/synchronization",
@@ -75,7 +120,9 @@ cc_library(
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
+ "//absl/base:config",
"//absl/base:core_headers",
+ "//absl/base:log_severity",
"//absl/strings",
"//absl/strings:str_format",
],
@@ -83,9 +130,6 @@ cc_library(
cc_library(
name = "handle",
- srcs = [
- "internal/commandlineflag.cc",
- ],
hdrs = [
"internal/commandlineflag.h",
],
@@ -97,10 +141,9 @@ cc_library(
deps = [
":config",
":marshalling",
- "//absl/base",
+ "//absl/base:config",
"//absl/base:core_headers",
"//absl/strings",
- "//absl/synchronization",
"//absl/types:optional",
],
)
@@ -123,9 +166,9 @@ cc_library(
deps = [
":config",
":handle",
- "//absl/base",
+ "//absl/base:config",
"//absl/base:core_headers",
- "//absl/base:dynamic_annotations",
+ "//absl/base:raw_logging_internal",
"//absl/strings",
"//absl/synchronization",
],
@@ -139,16 +182,17 @@ cc_library(
hdrs = [
"declare.h",
"flag.h",
- "internal/flag.h",
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":config",
+ ":flag_internal",
":handle",
":marshalling",
":registry",
"//absl/base",
+ "//absl/base:config",
"//absl/base:core_headers",
"//absl/strings",
],
@@ -170,10 +214,14 @@ cc_library(
deps = [
":config",
":flag",
+ ":flag_internal",
":handle",
- ":internal",
+ ":path_util",
+ ":program_name",
+ ":registry",
+ "//absl/base:config",
+ "//absl/base:core_headers",
"//absl/strings",
- "//absl/synchronization",
],
)
@@ -189,6 +237,8 @@ cc_library(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":usage_internal",
+ "//absl/base:config",
+ "//absl/base:core_headers",
"//absl/strings",
"//absl/synchronization",
],
@@ -206,18 +256,21 @@ cc_library(
deps = [
":config",
":flag",
+ ":flag_internal",
":handle",
- ":internal",
+ ":program_name",
":registry",
":usage",
":usage_internal",
+ "//absl/base:config",
+ "//absl/base:core_headers",
"//absl/strings",
"//absl/synchronization",
],
)
############################################################################
-# Unit tests in alpahabetical order.
+# Unit tests in alphabetical order.
cc_test(
name = "commandlineflag_test",
@@ -228,6 +281,7 @@ cc_test(
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
+ ":config",
":flag",
":handle",
":registry",
@@ -261,12 +315,34 @@ cc_test(
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
+ ":config",
":flag",
+ ":flag_internal",
+ ":handle",
+ ":registry",
+ "//absl/base:core_headers",
"//absl/strings",
"@com_google_googletest//:gtest_main",
],
)
+cc_binary(
+ name = "flag_benchmark",
+ testonly = 1,
+ srcs = [
+ "flag_benchmark.cc",
+ ],
+ copts = ABSL_TEST_COPTS,
+ tags = ["benchmark"],
+ visibility = ["//visibility:private"],
+ deps = [
+ ":flag",
+ "//absl/time",
+ "//absl/types:optional",
+ "@com_github_google_benchmark//:benchmark_main",
+ ],
+)
+
cc_test(
name = "marshalling_test",
size = "small",
@@ -290,7 +366,7 @@ cc_test(
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
- ":internal",
+ ":path_util",
"@com_google_googletest//:gtest_main",
],
)
@@ -306,7 +382,8 @@ cc_test(
deps = [
":flag",
":parse",
- "//absl/base",
+ ":registry",
+ "//absl/base:raw_logging_internal",
"//absl/base:scoped_set_env",
"//absl/strings",
"//absl/types:span",
@@ -323,7 +400,7 @@ cc_test(
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
- ":internal",
+ ":program_name",
"//absl/strings",
"@com_google_googletest//:gtest_main",
],
@@ -339,9 +416,10 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":flag",
+ ":handle",
+ ":marshalling",
":registry",
"//absl/memory",
- "//absl/strings",
"@com_google_googletest//:gtest_main",
],
)
@@ -356,7 +434,8 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":config",
- ":internal",
+ ":path_util",
+ ":program_name",
"//absl/strings",
"@com_google_googletest//:gtest_main",
],
@@ -373,8 +452,10 @@ cc_test(
deps = [
":config",
":flag",
- ":internal",
":parse",
+ ":path_util",
+ ":program_name",
+ ":registry",
":usage",
":usage_internal",
"//absl/memory",
diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt
index fa1d4e17..1d25f0de 100644
--- a/absl/flags/CMakeLists.txt
+++ b/absl/flags/CMakeLists.txt
@@ -19,20 +19,60 @@ 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_handle
+ absl::flags_registry
+ absl::synchronization
+ PUBLIC
+)
+
+# Internal-only target, do not depend on directly.
+absl_cc_library(
+ NAME
+ flags_program_name
+ SRCS
"internal/program_name.cc"
HDRS
- "internal/path_util.h"
"internal/program_name.h"
COPTS
${ABSL_DEFAULT_COPTS}
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
+ absl::config
+ absl::core_headers
+ absl::flags_path_util
absl::strings
absl::synchronization
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
@@ -46,7 +86,9 @@ absl_cc_library(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
- absl::flags_internal
+ absl::config
+ absl::flags_path_util
+ absl::flags_program_name
absl::core_headers
absl::strings
absl::synchronization
@@ -64,7 +106,9 @@ absl_cc_library(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
+ absl::config
absl::core_headers
+ absl::log_severity
absl::strings
absl::str_format
)
@@ -73,8 +117,6 @@ absl_cc_library(
absl_cc_library(
NAME
flags_handle
- SRCS
- "internal/commandlineflag.cc"
HDRS
"internal/commandlineflag.h"
COPTS
@@ -82,13 +124,14 @@ absl_cc_library(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
+ absl::config
absl::flags_config
absl::flags_marshalling
- absl::base
absl::core_headers
+ absl::optional
+ absl::raw_logging_internal
absl::strings
absl::synchronization
- absl::optional
)
# Internal-only target, do not depend on directly.
@@ -106,11 +149,11 @@ absl_cc_library(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
+ absl::config
absl::flags_config
absl::flags_handle
- absl::base
absl::core_headers
- absl::dynamic_annotations
+ absl::raw_logging_internal
absl::strings
absl::synchronization
)
@@ -123,14 +166,15 @@ absl_cc_library(
HDRS
"declare.h"
"flag.h"
- "internal/flag.h"
COPTS
${ABSL_DEFAULT_COPTS}
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
+ absl::config
absl::flags_config
absl::flags_handle
+ absl::flags_internal
absl::flags_marshalling
absl::flags_registry
absl::base
@@ -151,10 +195,14 @@ absl_cc_library(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
+ absl::config
absl::flags_config
absl::flags
absl::flags_handle
absl::flags_internal
+ absl::flags_path_util
+ absl::flags_program_name
+ absl::flags_registry
absl::strings
absl::synchronization
)
@@ -171,6 +219,8 @@ absl_cc_library(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
+ absl::config
+ absl::core_headers
absl::flags_usage_internal
absl::strings
absl::synchronization
@@ -189,10 +239,13 @@ absl_cc_library(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
+ absl::config
+ absl::core_headers
absl::flags_config
absl::flags
absl::flags_handle
absl::flags_internal
+ absl::flags_program_name
absl::flags_registry
absl::flags_usage
absl::strings
@@ -211,6 +264,7 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::flags
+ absl::flags_config
absl::flags_handle
absl::flags_registry
absl::memory
@@ -239,7 +293,12 @@ absl_cc_test(
COPTS
${ABSL_TEST_COPTS}
DEPS
+ absl::core_headers
absl::flags
+ absl::flags_config
+ absl::flags_handle
+ absl::flags_internal
+ absl::flags_registry
absl::strings
gtest_main
)
@@ -265,8 +324,9 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::flags
- absl::base
absl::flags_parse
+ absl::flags_registry
+ absl::raw_logging_internal
absl::scoped_set_env
absl::span
absl::strings
@@ -281,7 +341,7 @@ absl_cc_test(
COPTS
${ABSL_TEST_COPTS}
DEPS
- absl::flags_internal
+ absl::flags_path_util
gtest_main
)
@@ -293,7 +353,7 @@ absl_cc_test(
COPTS
${ABSL_TEST_COPTS}
DEPS
- absl::flags_internal
+ absl::flags_program_name
absl::strings
gtest_main
)
@@ -307,6 +367,8 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::flags
+ absl::flags_handle
+ absl::flags_marshalling
absl::flags_registry
absl::memory
absl::strings
@@ -322,7 +384,8 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::flags_config
- absl::flags_internal
+ absl::flags_path_util
+ absl::flags_program_name
absl::strings
gtest_main
)
@@ -337,8 +400,10 @@ absl_cc_test(
DEPS
absl::flags_config
absl::flags
- absl::flags_internal
+ absl::flags_path_util
+ absl::flags_program_name
absl::flags_parse
+ absl::flags_registry
absl::flags_usage
absl::memory
absl::strings
diff --git a/absl/flags/config.h b/absl/flags/config.h
index a9fd97ad..001f8fea 100644
--- a/absl/flags/config.h
+++ b/absl/flags/config.h
@@ -45,4 +45,23 @@
#define ABSL_FLAGS_STRIP_HELP ABSL_FLAGS_STRIP_NAMES
#endif
+// ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD macro is used for using atomics with
+// double words, e.g. absl::Duration.
+// For reasons in bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80878, modern
+// versions of GCC do not support cmpxchg16b instruction in standard atomics.
+#ifdef ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD
+#error "ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD should not be defined."
+#elif defined(__clang__) && defined(__x86_64__) && \
+ defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16)
+#define ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD 1
+#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)
+
#endif // ABSL_FLAGS_CONFIG_H_
diff --git a/absl/flags/declare.h b/absl/flags/declare.h
index 7ef1d432..0f8cc6a5 100644
--- a/absl/flags/declare.h
+++ b/absl/flags/declare.h
@@ -25,10 +25,11 @@
#ifndef ABSL_FLAGS_DECLARE_H_
#define ABSL_FLAGS_DECLARE_H_
+#include "absl/base/config.h"
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
// absl::Flag<T> represents a flag of type 'T' created by ABSL_FLAG.
@@ -40,10 +41,15 @@ class Flag;
// Flag
//
// Forward declaration of the `absl::Flag` type for use in defining the macro.
+#if defined(_MSC_VER) && !defined(__clang__)
+template <typename T>
+class Flag;
+#else
template <typename T>
using Flag = flags_internal::Flag<T>;
+#endif
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
// ABSL_DECLARE_FLAG()
diff --git a/absl/flags/flag.cc b/absl/flags/flag.cc
index 0858259b..f7a457bf 100644
--- a/absl/flags/flag.cc
+++ b/absl/flags/flag.cc
@@ -15,32 +15,26 @@
#include "absl/flags/flag.h"
-#include <cstring>
+#include "absl/base/config.h"
+#include "absl/flags/internal/commandlineflag.h"
+#include "absl/flags/internal/flag.h"
namespace absl {
-inline namespace lts_2019_08_08 {
-
-// We want to validate the type mismatch between type definition and
-// declaration. The lock-free implementation does not allow us to do it,
-// so in debug builds we always use the slower implementation, which always
-// validates the type.
-#ifndef NDEBUG
-#define ABSL_FLAGS_ATOMIC_GET(T) \
- T GetFlag(const absl::Flag<T>& flag) { return flag.Get(); }
-#else
-#define ABSL_FLAGS_ATOMIC_GET(T) \
- T GetFlag(const absl::Flag<T>& flag) { \
- T result; \
- if (flag.AtomicGet(&result)) { \
- return result; \
- } \
- return flag.Get(); \
- }
-#endif
+ABSL_NAMESPACE_BEGIN
+
+// This global mutex protects on-demand construction of flag objects in MSVC
+// builds.
+#if defined(_MSC_VER) && !defined(__clang__)
+
+namespace flags_internal {
-ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_ATOMIC_GET)
+ABSL_CONST_INIT static absl::Mutex construction_guard(absl::kConstInit);
-#undef ABSL_FLAGS_ATOMIC_GET
+absl::Mutex* GetGlobalConstructionGuard() { return &construction_guard; }
+
+} // namespace flags_internal
+
+#endif
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/flag.h b/absl/flags/flag.h
index 28925927..cff02c1f 100644
--- a/absl/flags/flag.h
+++ b/absl/flags/flag.h
@@ -29,16 +29,21 @@
#ifndef ABSL_FLAGS_FLAG_H_
#define ABSL_FLAGS_FLAG_H_
+#include <string>
+#include <type_traits>
+
#include "absl/base/attributes.h"
#include "absl/base/casts.h"
+#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"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// Flag
//
@@ -64,8 +69,103 @@ inline namespace lts_2019_08_08 {
// ABSL_FLAG(int, count, 0, "Count of items to process");
//
// No public methods of `absl::Flag<T>` are part of the Abseil Flags API.
+#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},
+ default_value_gen_);
+ 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(); }
+ bool IsAbseilFlag() const { return GetImpl()->IsAbseilFlag(); }
+ 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();
+ }
+ absl::string_view Typename() const { return GetImpl()->Typename(); }
+ 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 GetImpl()->Get(); }
+ bool AtomicGet(T* v) const { return GetImpl()->AtomicGet(v); }
+ void Set(const T& v) { GetImpl()->Set(v); }
+ void SetCallback(const flags_internal::FlagCallbackFunc mutation_callback) {
+ GetImpl()->SetCallback(mutation_callback);
+ }
+ void InvokeCallback() { GetImpl()->InvokeCallback(); }
+
+ // 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_;
+};
+#endif
// GetFlag()
//
@@ -84,23 +184,10 @@ using Flag = flags_internal::Flag<T>;
// // FLAGS_firstname is a Flag of type `std::string`
// std::string first_name = absl::GetFlag(FLAGS_firstname);
template <typename T>
-T GetFlag(const absl::Flag<T>& flag) {
-#define ABSL_FLAGS_INTERNAL_LOCK_FREE_VALIDATE(BIT) \
- static_assert( \
- !std::is_same<T, BIT>::value, \
- "Do not specify explicit template parameters to absl::GetFlag");
- ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_INTERNAL_LOCK_FREE_VALIDATE)
-#undef ABSL_FLAGS_INTERNAL_LOCK_FREE_VALIDATE
-
+ABSL_MUST_USE_RESULT T GetFlag(const absl::Flag<T>& flag) {
return flag.Get();
}
-// Overload for `GetFlag()` for types that support lock-free reads.
-#define ABSL_FLAGS_INTERNAL_LOCK_FREE_EXPORT(T) \
- extern T GetFlag(const absl::Flag<T>& flag);
-ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_INTERNAL_LOCK_FREE_EXPORT)
-#undef ABSL_FLAGS_INTERNAL_LOCK_FREE_EXPORT
-
// SetFlag()
//
// Sets the value of an `absl::Flag` to the value `v`. Do not construct an
@@ -122,7 +209,7 @@ void SetFlag(absl::Flag<T>* flag, const V& v) {
flag->Set(value);
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
@@ -186,13 +273,23 @@ void SetFlag(absl::Flag<T>* flag, const V& v) {
#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<T, false>(&flag)
#else
+#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
+ absl::flags_internal::FlagRegistrar<T, false>(flag.GetImpl())
+#endif
+#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<T, true>(&flag)
+#else
+#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
+ absl::flags_internal::FlagRegistrar<T, true>(flag.GetImpl())
+#endif
#endif
// ABSL_FLAG_IMPL macro definition conditional on ABSL_FLAGS_STRIP_HELP
@@ -203,9 +300,19 @@ void SetFlag(absl::Flag<T>* flag, const V& v) {
#define ABSL_FLAG_IMPL_FLAGHELP(txt) txt
#endif
-#define ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, txt) \
- static std::string AbslFlagsWrapHelp##name() { \
- return ABSL_FLAG_IMPL_FLAGHELP(txt); \
+// AbslFlagHelpGenFor##name is used to encapsulate both immediate (method Const)
+// and lazy (method NonConst) evaluation of help message expression. We choose
+// between the two via the call to HelpArg in absl::Flag instantiation below.
+// If help message expression is constexpr evaluable compiler will optimize
+// away this whole struct.
+#define ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, txt) \
+ struct AbslFlagHelpGenFor##name { \
+ template <typename T = void> \
+ static constexpr const char* Const() { \
+ return absl::flags_internal::HelpConstexprWrap( \
+ ABSL_FLAG_IMPL_FLAGHELP(txt)); \
+ } \
+ static std::string NonConst() { return ABSL_FLAG_IMPL_FLAGHELP(txt); } \
}
#define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \
@@ -218,17 +325,30 @@ void SetFlag(absl::Flag<T>* flag, const V& v) {
// Note: Name of registrar object is not arbitrary. It is used to "grab"
// global name for FLAGS_no<flag_name> symbol, thus preventing the possibility
// of defining two flags with names foo and nofoo.
+#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<Type> FLAGS_##name( \
- ABSL_FLAG_IMPL_FLAGNAME(#name), &AbslFlagsWrapHelp##name, \
- ABSL_FLAG_IMPL_FILENAME(), \
- &absl::flags_internal::FlagMarshallingOps<Type>, \
- &AbslFlagsInitFlag##name); \
+ ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help); \
+ ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name{ \
+ ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(), \
+ absl::flags_internal::HelpArg<AbslFlagHelpGenFor##name>(0), \
+ &AbslFlagsInitFlag##name}; \
extern bool FLAGS_no##name; \
bool 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<Type> FLAGS_##name{ \
+ ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(), \
+ &AbslFlagHelpGenFor##name::NonConst, &AbslFlagsInitFlag##name}; \
+ extern bool FLAGS_no##name; \
+ bool FLAGS_no##name = ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name)
+#endif
// ABSL_RETIRED_FLAG
//
diff --git a/absl/flags/flag_benchmark.cc b/absl/flags/flag_benchmark.cc
new file mode 100644
index 00000000..87f73170
--- /dev/null
+++ b/absl/flags/flag_benchmark.cc
@@ -0,0 +1,111 @@
+//
+// 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.
+
+#include "absl/flags/flag.h"
+#include "absl/time/time.h"
+#include "absl/types/optional.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+using String = std::string;
+using VectorOfStrings = std::vector<std::string>;
+using AbslDuration = absl::Duration;
+
+// We do not want to take over marshalling for the types absl::optional<int>,
+// absl::optional<std::string> which we do not own. Instead we introduce unique
+// "aliases" to these types, which we do.
+using AbslOptionalInt = absl::optional<int>;
+struct OptionalInt : AbslOptionalInt {
+ using AbslOptionalInt::AbslOptionalInt;
+};
+// Next two functions represent Abseil Flags marshalling for OptionalInt.
+bool AbslParseFlag(absl::string_view src, OptionalInt* flag,
+ std::string* error) {
+ int val;
+ if (src.empty())
+ flag->reset();
+ else if (!absl::ParseFlag(src, &val, error))
+ return false;
+ *flag = val;
+ return true;
+}
+std::string AbslUnparseFlag(const OptionalInt& flag) {
+ return !flag ? "" : absl::UnparseFlag(*flag);
+}
+
+using AbslOptionalString = absl::optional<std::string>;
+struct OptionalString : AbslOptionalString {
+ using AbslOptionalString::AbslOptionalString;
+};
+// Next two functions represent Abseil Flags marshalling for OptionalString.
+bool AbslParseFlag(absl::string_view src, OptionalString* flag,
+ std::string* error) {
+ std::string val;
+ if (src.empty())
+ flag->reset();
+ else if (!absl::ParseFlag(src, &val, error))
+ return false;
+ *flag = val;
+ return true;
+}
+std::string AbslUnparseFlag(const OptionalString& flag) {
+ return !flag ? "" : absl::UnparseFlag(*flag);
+}
+
+struct UDT {
+ UDT() = default;
+ UDT(const UDT&) {}
+ UDT& operator=(const UDT&) { return *this; }
+};
+// Next two functions represent Abseil Flags marshalling for UDT.
+bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; }
+std::string AbslUnparseFlag(const UDT&) { return ""; }
+
+} // namespace
+
+#define BENCHMARKED_TYPES(A) \
+ A(bool) \
+ A(int16_t) \
+ A(uint16_t) \
+ A(int32_t) \
+ A(uint32_t) \
+ A(int64_t) \
+ A(uint64_t) \
+ A(double) \
+ A(float) \
+ A(String) \
+ A(VectorOfStrings) \
+ A(OptionalInt) \
+ A(OptionalString) \
+ A(AbslDuration) \
+ A(UDT)
+
+#define FLAG_DEF(T) ABSL_FLAG(T, T##_flag, {}, "");
+
+BENCHMARKED_TYPES(FLAG_DEF)
+
+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);
+
+BENCHMARKED_TYPES(BM_GetFlag)
+
+} // namespace
diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc
index 1bcd7e96..4984d284 100644
--- a/absl/flags/flag_test.cc
+++ b/absl/flags/flag_test.cc
@@ -15,11 +15,25 @@
#include "absl/flags/flag.h"
+#include <stdint.h>
+
+#include <cmath>
+#include <string>
+#include <vector>
+
#include "gtest/gtest.h"
+#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"
#include "absl/strings/match.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
+#include "absl/strings/string_view.h"
ABSL_DECLARE_FLAG(int64_t, mistyped_int_flag);
ABSL_DECLARE_FLAG(std::vector<std::string>, mistyped_string_flag);
@@ -28,7 +42,7 @@ namespace {
namespace flags = absl::flags_internal;
-std::string TestHelpMsg() { return "help"; }
+std::string TestHelpMsg() { return "dynamic help"; }
template <typename T>
void* TestMakeDflt() {
return new T{};
@@ -37,20 +51,21 @@ void TestCallback() {}
template <typename T>
bool TestConstructionFor() {
- constexpr flags::Flag<T> f1("f1", &TestHelpMsg, "file",
- &absl::flags_internal::FlagMarshallingOps<T>,
- &TestMakeDflt<T>);
+ constexpr flags::FlagHelpArg help_arg{flags::FlagHelpMsg("literal help"),
+ flags::FlagHelpKind::kLiteral};
+ constexpr flags::Flag<T> f1("f1", "file", help_arg, &TestMakeDflt<T>);
EXPECT_EQ(f1.Name(), "f1");
- EXPECT_EQ(f1.Help(), "help");
+ EXPECT_EQ(f1.Help(), "literal help");
EXPECT_EQ(f1.Filename(), "file");
ABSL_CONST_INIT static flags::Flag<T> f2(
- "f2", &TestHelpMsg, "file", &absl::flags_internal::FlagMarshallingOps<T>,
+ "f2", "file",
+ {flags::FlagHelpMsg(&TestHelpMsg), flags::FlagHelpKind::kGenFunc},
&TestMakeDflt<T>);
flags::FlagRegistrar<T, false>(&f2).OnUpdate(TestCallback);
EXPECT_EQ(f2.Name(), "f2");
- EXPECT_EQ(f2.Help(), "help");
+ EXPECT_EQ(f2.Help(), "dynamic help");
EXPECT_EQ(f2.Filename(), "file");
return true;
@@ -63,7 +78,27 @@ struct UDT {
bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; }
std::string AbslUnparseFlag(const UDT&) { return ""; }
-TEST(FlagTest, TestConstruction) {
+class FlagTest : public testing::Test {
+ protected:
+ static void SetUpTestSuite() {
+ // Install a function to normalize filenames before this test is run.
+ absl::FlagsUsageConfig default_config;
+ default_config.normalize_filename = &FlagTest::NormalizeFileName;
+ absl::SetFlagsUsageConfig(default_config);
+ }
+
+ private:
+ static std::string NormalizeFileName(absl::string_view fname) {
+#ifdef _WIN32
+ std::string normalized(fname);
+ std::replace(normalized.begin(), normalized.end(), '\\', '/');
+ fname = normalized;
+#endif
+ return std::string(fname);
+ }
+};
+
+TEST_F(FlagTest, TestConstruction) {
TestConstructionFor<bool>();
TestConstructionFor<int16_t>();
TestConstructionFor<uint16_t>();
@@ -98,7 +133,7 @@ namespace {
#if !ABSL_FLAGS_STRIP_NAMES
-TEST(FlagTest, TestFlagDeclaration) {
+TEST_F(FlagTest, TestFlagDeclaration) {
// test that we can access flag objects.
EXPECT_EQ(FLAGS_test_flag_01.Name(), "test_flag_01");
EXPECT_EQ(FLAGS_test_flag_02.Name(), "test_flag_02");
@@ -133,69 +168,69 @@ ABSL_FLAG(std::string, test_flag_11, "", "test flag 11");
namespace {
#if !ABSL_FLAGS_STRIP_NAMES
-TEST(FlagTest, TestFlagDefinition) {
+TEST_F(FlagTest, TestFlagDefinition) {
absl::string_view expected_file_name = "absl/flags/flag_test.cc";
EXPECT_EQ(FLAGS_test_flag_01.Name(), "test_flag_01");
EXPECT_EQ(FLAGS_test_flag_01.Help(), "test flag 01");
- EXPECT_TRUE(
- absl::EndsWith(FLAGS_test_flag_01.Filename(), expected_file_name));
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_01.Filename(), expected_file_name))
+ << FLAGS_test_flag_01.Filename();
EXPECT_EQ(FLAGS_test_flag_02.Name(), "test_flag_02");
EXPECT_EQ(FLAGS_test_flag_02.Help(), "test flag 02");
- EXPECT_TRUE(
- absl::EndsWith(FLAGS_test_flag_02.Filename(), expected_file_name));
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_02.Filename(), expected_file_name))
+ << FLAGS_test_flag_02.Filename();
EXPECT_EQ(FLAGS_test_flag_03.Name(), "test_flag_03");
EXPECT_EQ(FLAGS_test_flag_03.Help(), "test flag 03");
- EXPECT_TRUE(
- absl::EndsWith(FLAGS_test_flag_03.Filename(), expected_file_name));
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_03.Filename(), expected_file_name))
+ << FLAGS_test_flag_03.Filename();
EXPECT_EQ(FLAGS_test_flag_04.Name(), "test_flag_04");
EXPECT_EQ(FLAGS_test_flag_04.Help(), "test flag 04");
- EXPECT_TRUE(
- absl::EndsWith(FLAGS_test_flag_04.Filename(), expected_file_name));
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_04.Filename(), expected_file_name))
+ << FLAGS_test_flag_04.Filename();
EXPECT_EQ(FLAGS_test_flag_05.Name(), "test_flag_05");
EXPECT_EQ(FLAGS_test_flag_05.Help(), "test flag 05");
- EXPECT_TRUE(
- absl::EndsWith(FLAGS_test_flag_05.Filename(), expected_file_name));
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_05.Filename(), expected_file_name))
+ << FLAGS_test_flag_05.Filename();
EXPECT_EQ(FLAGS_test_flag_06.Name(), "test_flag_06");
EXPECT_EQ(FLAGS_test_flag_06.Help(), "test flag 06");
- EXPECT_TRUE(
- absl::EndsWith(FLAGS_test_flag_06.Filename(), expected_file_name));
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_06.Filename(), expected_file_name))
+ << FLAGS_test_flag_06.Filename();
EXPECT_EQ(FLAGS_test_flag_07.Name(), "test_flag_07");
EXPECT_EQ(FLAGS_test_flag_07.Help(), "test flag 07");
- EXPECT_TRUE(
- absl::EndsWith(FLAGS_test_flag_07.Filename(), expected_file_name));
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_07.Filename(), expected_file_name))
+ << FLAGS_test_flag_07.Filename();
EXPECT_EQ(FLAGS_test_flag_08.Name(), "test_flag_08");
EXPECT_EQ(FLAGS_test_flag_08.Help(), "test flag 08");
- EXPECT_TRUE(
- absl::EndsWith(FLAGS_test_flag_08.Filename(), expected_file_name));
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_08.Filename(), expected_file_name))
+ << FLAGS_test_flag_08.Filename();
EXPECT_EQ(FLAGS_test_flag_09.Name(), "test_flag_09");
EXPECT_EQ(FLAGS_test_flag_09.Help(), "test flag 09");
- EXPECT_TRUE(
- absl::EndsWith(FLAGS_test_flag_09.Filename(), expected_file_name));
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_09.Filename(), expected_file_name))
+ << FLAGS_test_flag_09.Filename();
EXPECT_EQ(FLAGS_test_flag_10.Name(), "test_flag_10");
EXPECT_EQ(FLAGS_test_flag_10.Help(), "test flag 10");
- EXPECT_TRUE(
- absl::EndsWith(FLAGS_test_flag_10.Filename(), expected_file_name));
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_10.Filename(), expected_file_name))
+ << FLAGS_test_flag_10.Filename();
EXPECT_EQ(FLAGS_test_flag_11.Name(), "test_flag_11");
EXPECT_EQ(FLAGS_test_flag_11.Help(), "test flag 11");
- EXPECT_TRUE(
- absl::EndsWith(FLAGS_test_flag_11.Filename(), expected_file_name));
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_11.Filename(), expected_file_name))
+ << FLAGS_test_flag_11.Filename();
}
#endif // !ABSL_FLAGS_STRIP_NAMES
// --------------------------------------------------------------------
-TEST(FlagTest, TestDefault) {
+TEST_F(FlagTest, TestDefault) {
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), true);
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_02), 1234);
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_03), -34);
@@ -211,7 +246,7 @@ TEST(FlagTest, TestDefault) {
// --------------------------------------------------------------------
-TEST(FlagTest, TestGetSet) {
+TEST_F(FlagTest, TestGetSet) {
absl::SetFlag(&FLAGS_test_flag_01, false);
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), false);
@@ -258,7 +293,7 @@ ABSL_FLAG(std::string, test_flag_13, absl::StrCat("AAA", "BBB"),
namespace {
-TEST(FlagTest, TestNonConstexprDefault) {
+TEST_F(FlagTest, TestNonConstexprDefault) {
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), 1);
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_13), "AAABBB");
}
@@ -272,7 +307,7 @@ ABSL_FLAG(bool, test_flag_14, true, absl::StrCat("test ", "flag ", "14"));
namespace {
#if !ABSL_FLAGS_STRIP_HELP
-TEST(FlagTest, TestNonConstexprHelp) {
+TEST_F(FlagTest, TestNonConstexprHelp) {
EXPECT_EQ(FLAGS_test_flag_14.Help(), "test flag 14");
}
#endif //! ABSL_FLAGS_STRIP_HELP
@@ -296,7 +331,7 @@ namespace {
void TestFlagCB() { cb_test_value = absl::GetFlag(FLAGS_test_flag_with_cb); }
// Tests side-effects of callback invocation.
-TEST(FlagTest, CallbackInvocation) {
+TEST_F(FlagTest, CallbackInvocation) {
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_with_cb), 100);
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_with_lambda_cb), 200);
EXPECT_EQ(cb_test_value, 300);
@@ -343,7 +378,7 @@ ABSL_FLAG(CustomUDT, test_flag_15, CustomUDT(), "test flag 15");
namespace {
-TEST(FlagTest, TestCustomUDT) {
+TEST_F(FlagTest, TestCustomUDT) {
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_15), CustomUDT(1, 1));
absl::SetFlag(&FLAGS_test_flag_15, CustomUDT(2, 3));
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_15), CustomUDT(2, 3));
@@ -351,19 +386,20 @@ TEST(FlagTest, TestCustomUDT) {
// MSVC produces link error on the type mismatch.
// Linux does not have build errors and validations work as expected.
-#if 0 // !defined(_WIN32) && GTEST_HAS_DEATH_TEST
+#if !defined(_WIN32) && GTEST_HAS_DEATH_TEST
+
+using FlagDeathTest = FlagTest;
-TEST(Flagtest, TestTypeMismatchValidations) {
- // For builtin types, GetFlag() only does validation in debug mode.
+TEST_F(FlagDeathTest, TestTypeMismatchValidations) {
EXPECT_DEBUG_DEATH(
- absl::GetFlag(FLAGS_mistyped_int_flag),
+ static_cast<void>(absl::GetFlag(FLAGS_mistyped_int_flag)),
"Flag 'mistyped_int_flag' is defined as one type and declared "
"as another");
- EXPECT_DEATH(absl::SetFlag(&FLAGS_mistyped_int_flag, 0),
+ EXPECT_DEATH(absl::SetFlag(&FLAGS_mistyped_int_flag, 1),
"Flag 'mistyped_int_flag' is defined as one type and declared "
"as another");
- EXPECT_DEATH(absl::GetFlag(FLAGS_mistyped_string_flag),
+ EXPECT_DEATH(static_cast<void>(absl::GetFlag(FLAGS_mistyped_string_flag)),
"Flag 'mistyped_string_flag' is defined as one type and "
"declared as another");
EXPECT_DEATH(
@@ -409,7 +445,7 @@ ABSL_FLAG(ConversionTestVal, test_flag_16,
namespace {
-TEST(FlagTest, CanSetViaImplicitConversion) {
+TEST_F(FlagTest, CanSetViaImplicitConversion) {
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_16).a, 10);
absl::SetFlag(&FLAGS_test_flag_16,
ConversionTestVal::ViaImplicitConv::kEleven);
@@ -447,7 +483,7 @@ ABSL_FLAG(NonDfltConstructible, ndc_flag2, 0,
namespace {
-TEST(FlagTest, TestNonDefaultConstructibleType) {
+TEST_F(FlagTest, TestNonDefaultConstructibleType) {
EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag1).value, '1' + 100);
EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag2).value, 0);
@@ -468,7 +504,7 @@ ABSL_RETIRED_FLAG(std::string, old_str_flag, "", absl::StrCat("old ", "descr"));
namespace {
-TEST(FlagTest, TestRetiredFlagRegistration) {
+TEST_F(FlagTest, TestRetiredFlagRegistration) {
bool is_bool = false;
EXPECT_TRUE(flags::IsRetiredFlag("old_bool_flag", &is_bool));
EXPECT_TRUE(is_bool);
diff --git a/absl/flags/flag_test_defs.cc b/absl/flags/flag_test_defs.cc
index 3366c580..49f91dee 100644
--- a/absl/flags/flag_test_defs.cc
+++ b/absl/flags/flag_test_defs.cc
@@ -20,3 +20,5 @@
ABSL_FLAG(int, mistyped_int_flag, 0, "");
ABSL_FLAG(std::string, mistyped_string_flag, "", "");
+ABSL_RETIRED_FLAG(bool, old_bool_flag, true,
+ "repetition of retired flag definition");
diff --git a/absl/flags/internal/commandlineflag.cc b/absl/flags/internal/commandlineflag.cc
deleted file mode 100644
index f964165e..00000000
--- a/absl/flags/internal/commandlineflag.cc
+++ /dev/null
@@ -1,496 +0,0 @@
-//
-// Copyright 2019 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.
-
-#include "absl/flags/internal/commandlineflag.h"
-
-#include <cassert>
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/optimization.h"
-#include "absl/flags/config.h"
-#include "absl/flags/usage_config.h"
-#include "absl/strings/str_cat.h"
-#include "absl/synchronization/mutex.h"
-
-namespace absl {
-inline namespace lts_2019_08_08 {
-namespace flags_internal {
-
-// The help message indicating that the commandline flag has been
-// 'stripped'. It will not show up when doing "-help" and its
-// variants. The flag is stripped if ABSL_FLAGS_STRIP_HELP is set to 1
-// before including absl/flags/flag.h
-
-// This is used by this file, and also in commandlineflags_reporting.cc
-const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
-
-namespace {
-
-// Currently we only validate flag values for user-defined flag types.
-bool ShouldValidateFlagValue(const CommandLineFlag& flag) {
-#define DONT_VALIDATE(T) \
- if (flag.IsOfType<T>()) return false;
- ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(DONT_VALIDATE)
- DONT_VALIDATE(std::string)
- DONT_VALIDATE(std::vector<std::string>)
-#undef DONT_VALIDATE
-
- return true;
-}
-
-} // namespace
-
-absl::Mutex* InitFlag(CommandLineFlag* flag) {
- ABSL_CONST_INIT static absl::Mutex init_lock(absl::kConstInit);
- absl::Mutex* mu;
-
- {
- absl::MutexLock lock(&init_lock);
-
- if (flag->locks == nullptr) { // Must initialize Mutexes for this flag.
- flag->locks = new flags_internal::CommandLineFlagLocks;
- }
-
- mu = &flag->locks->primary_mu;
- }
-
- {
- absl::MutexLock lock(mu);
-
- if (!flag->retired && flag->def == nullptr) {
- // Need to initialize def and cur fields.
- flag->def = (*flag->make_init_value)();
- flag->cur = Clone(flag->op, flag->def);
- UpdateCopy(flag);
- flag->inited.store(true, std::memory_order_release);
- flag->InvokeCallback();
- }
- }
-
- flag->inited.store(true, std::memory_order_release);
- return mu;
-}
-
-// Ensure that the lazily initialized fields of *flag have been initialized,
-// and return &flag->locks->primary_mu.
-absl::Mutex* CommandLineFlag::InitFlagIfNecessary() const
- LOCK_RETURNED(locks->primary_mu) {
- if (!this->inited.load(std::memory_order_acquire)) {
- return InitFlag(const_cast<CommandLineFlag*>(this));
- }
-
- // All fields initialized; this->locks is therefore safe to read.
- return &this->locks->primary_mu;
-}
-
-void CommandLineFlag::Destroy() const {
- // Values are heap allocated for retired and Abseil Flags.
- if (IsRetired() || IsAbseilFlag()) {
- if (this->cur) Delete(this->op, this->cur);
- if (this->def) Delete(this->op, this->def);
- }
-
- delete this->locks;
-}
-
-bool CommandLineFlag::IsModified() const {
- absl::MutexLock l(InitFlagIfNecessary());
- return modified;
-}
-
-void CommandLineFlag::SetModified(bool is_modified) {
- absl::MutexLock l(InitFlagIfNecessary());
- modified = is_modified;
-}
-
-bool CommandLineFlag::IsSpecifiedOnCommandLine() const {
- absl::MutexLock l(InitFlagIfNecessary());
- return on_command_line;
-}
-
-absl::string_view CommandLineFlag::Typename() const {
- // We do not store/report type in Abseil Flags, so that user do not rely on in
- // at runtime
- if (IsAbseilFlag() || IsRetired()) return "";
-
-#define HANDLE_V1_BUILTIN_TYPE(t) \
- if (IsOfType<t>()) { \
- return #t; \
- }
-
- HANDLE_V1_BUILTIN_TYPE(bool);
- HANDLE_V1_BUILTIN_TYPE(int32_t);
- HANDLE_V1_BUILTIN_TYPE(int64_t);
- HANDLE_V1_BUILTIN_TYPE(uint64_t);
- HANDLE_V1_BUILTIN_TYPE(double);
-#undef HANDLE_V1_BUILTIN_TYPE
-
- if (IsOfType<std::string>()) {
- return "string";
- }
-
- return "";
-}
-
-std::string CommandLineFlag::Filename() const {
- return flags_internal::GetUsageConfig().normalize_filename(this->filename);
-}
-
-std::string CommandLineFlag::DefaultValue() const {
- absl::MutexLock l(InitFlagIfNecessary());
-
- return Unparse(this->marshalling_op, this->def);
-}
-
-std::string CommandLineFlag::CurrentValue() const {
- absl::MutexLock l(InitFlagIfNecessary());
-
- return Unparse(this->marshalling_op, this->cur);
-}
-
-bool CommandLineFlag::HasValidatorFn() const {
- absl::MutexLock l(InitFlagIfNecessary());
-
- return this->validator != nullptr;
-}
-
-bool CommandLineFlag::SetValidatorFn(FlagValidator fn) {
- absl::MutexLock l(InitFlagIfNecessary());
-
- // ok to register the same function over and over again
- if (fn == this->validator) return true;
-
- // Can't set validator to a different function, unless reset first.
- if (fn != nullptr && this->validator != nullptr) {
- ABSL_INTERNAL_LOG(
- WARNING, absl::StrCat("Ignoring SetValidatorFn() for flag '", Name(),
- "': validate-fn already registered"));
-
- return false;
- }
-
- this->validator = fn;
- return true;
-}
-
-bool CommandLineFlag::InvokeValidator(const void* value) const
- EXCLUSIVE_LOCKS_REQUIRED(this->locks->primary_mu) {
- if (!this->validator) {
- return true;
- }
-
- (void)value;
-
- ABSL_INTERNAL_LOG(
- FATAL,
- absl::StrCat("Flag '", Name(),
- "' of encapsulated type should not have a validator"));
-
- return false;
-}
-
-void CommandLineFlag::SetCallback(
- const flags_internal::FlagCallback mutation_callback) {
- absl::MutexLock l(InitFlagIfNecessary());
-
- callback = mutation_callback;
-
- InvokeCallback();
-}
-
-// If the flag has a mutation callback this function invokes it. While the
-// callback is being invoked the primary flag's mutex is unlocked and it is
-// re-locked back after call to callback is completed. Callback invocation is
-// guarded by flag's secondary mutex instead which prevents concurrent callback
-// invocation. Note that it is possible for other thread to grab the primary
-// lock and update flag's value at any time during the callback invocation.
-// This is by design. Callback can get a value of the flag if necessary, but it
-// might be different from the value initiated the callback and it also can be
-// different by the time the callback invocation is completed.
-// Requires that *primary_lock be held in exclusive mode; it may be released
-// and reacquired by the implementation.
-void CommandLineFlag::InvokeCallback()
- EXCLUSIVE_LOCKS_REQUIRED(this->locks->primary_mu) {
- if (!this->callback) return;
-
- // The callback lock is guaranteed initialized, because *locks->primary_mu
- // exists.
- absl::Mutex* callback_mu = &this->locks->callback_mu;
-
- // When executing the callback we need the primary flag's mutex to be unlocked
- // so that callback can retrieve the flag's value.
- this->locks->primary_mu.Unlock();
-
- {
- absl::MutexLock lock(callback_mu);
- this->callback();
- }
-
- this->locks->primary_mu.Lock();
-}
-
-// Attempts to parse supplied `value` string using parsing routine in the `flag`
-// argument. If parsing is successful, it will try to validate that the parsed
-// value is valid for the specified 'flag'. Finally this function stores the
-// parsed value in 'dst' assuming it is a pointer to the flag's value type. In
-// case if any error is encountered in either step, the error message is stored
-// in 'err'
-bool TryParseLocked(CommandLineFlag* flag, void* dst, absl::string_view value,
- std::string* err)
- EXCLUSIVE_LOCKS_REQUIRED(flag->locks->primary_mu) {
- void* tentative_value = Clone(flag->op, flag->def);
- std::string parse_err;
- if (!Parse(flag->marshalling_op, value, tentative_value, &parse_err)) {
- auto type_name = flag->Typename();
- absl::string_view err_sep = parse_err.empty() ? "" : "; ";
- absl::string_view typename_sep = type_name.empty() ? "" : " ";
- *err = absl::StrCat("Illegal value '", value, "' specified for",
- typename_sep, type_name, " flag '", flag->Name(), "'",
- err_sep, parse_err);
- Delete(flag->op, tentative_value);
- return false;
- }
-
- if (!flag->InvokeValidator(tentative_value)) {
- *err = absl::StrCat("Failed validation of new value '",
- Unparse(flag->marshalling_op, tentative_value),
- "' for flag '", flag->Name(), "'");
- Delete(flag->op, tentative_value);
- return false;
- }
-
- flag->counter++;
- Copy(flag->op, tentative_value, dst);
- Delete(flag->op, tentative_value);
- return true;
-}
-
-// 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 `err`
-// 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.
-bool CommandLineFlag::SetFromString(absl::string_view value,
- FlagSettingMode set_mode,
- ValueSource source, std::string* err) {
- if (IsRetired()) return false;
-
- absl::MutexLock l(InitFlagIfNecessary());
-
- // Direct-access flags can be modified without going through the
- // flag API. Detect such changes and update the flag->modified bit.
- if (!IsAbseilFlag()) {
- if (!this->modified && ChangedDirectly(this, this->cur, this->def)) {
- this->modified = true;
- }
- }
-
- switch (set_mode) {
- case SET_FLAGS_VALUE: {
- // set or modify the flag's value
- if (!TryParseLocked(this, this->cur, value, err)) return false;
- this->modified = true;
- UpdateCopy(this);
- InvokeCallback();
-
- if (source == kCommandLine) {
- this->on_command_line = true;
- }
- break;
- }
- case SET_FLAG_IF_DEFAULT: {
- // set the flag's value, but only if it hasn't been set by someone else
- if (!this->modified) {
- if (!TryParseLocked(this, this->cur, value, err)) return false;
- this->modified = true;
- UpdateCopy(this);
- InvokeCallback();
- } else {
- // TODO(rogeeff): review and fix this semantic. Currently we do not fail
- // in this case if flag is modified. This is misleading since the flag's
- // value is not updated even though we return true.
- // *err = absl::StrCat(this->Name(), " is already set to ",
- // CurrentValue(), "\n");
- // return false;
- return true;
- }
- break;
- }
- case SET_FLAGS_DEFAULT: {
- // modify the flag's default-value
- if (!TryParseLocked(this, this->def, value, err)) return false;
-
- if (!this->modified) {
- // Need to set both defvalue *and* current, in this case
- Copy(this->op, this->def, this->cur);
- UpdateCopy(this);
- InvokeCallback();
- }
- break;
- }
- default: {
- // unknown set_mode
- assert(false);
- return false;
- }
- }
-
- return true;
-}
-
-void CommandLineFlag::StoreAtomic(size_t size) {
- int64_t t = 0;
- assert(size <= sizeof(int64_t));
- memcpy(&t, this->cur, size);
- this->atomic.store(t, std::memory_order_release);
-}
-
-void CommandLineFlag::CheckDefaultValueParsingRoundtrip() const {
- std::string v = DefaultValue();
-
- absl::MutexLock lock(InitFlagIfNecessary());
-
- void* dst = Clone(this->op, this->def);
- std::string error;
- if (!flags_internal::Parse(this->marshalling_op, v, dst, &error)) {
- ABSL_INTERNAL_LOG(
- FATAL,
- absl::StrCat("Flag ", Name(), " (from ", Filename(),
- "): std::string form of default value '", v,
- "' could not be parsed; error=", error));
- }
-
- // We do not compare dst to def since parsing/unparsing may make
- // small changes, e.g., precision loss for floating point types.
- Delete(this->op, dst);
-}
-
-bool CommandLineFlag::ValidateDefaultValue() const {
- absl::MutexLock lock(InitFlagIfNecessary());
- return InvokeValidator(this->def);
-}
-
-bool CommandLineFlag::ValidateInputValue(absl::string_view value) const {
- absl::MutexLock l(InitFlagIfNecessary()); // protect default value access
-
- void* obj = Clone(this->op, this->def);
- std::string ignored_error;
- const bool result =
- flags_internal::Parse(this->marshalling_op, value, obj, &ignored_error) &&
- InvokeValidator(obj);
- Delete(this->op, obj);
- return result;
-}
-
-const int64_t CommandLineFlag::kAtomicInit;
-
-void CommandLineFlag::Read(void* dst,
- const flags_internal::FlagOpFn dst_op) const {
- absl::ReaderMutexLock l(InitFlagIfNecessary());
-
- // `dst_op` is the unmarshaling operation corresponding to the declaration
- // visibile at the call site. `op` is the Flag's defined unmarshalling
- // operation. They must match for this operation to be well-defined.
- if (ABSL_PREDICT_FALSE(dst_op != op)) {
- ABSL_INTERNAL_LOG(
- ERROR,
- absl::StrCat("Flag '", name,
- "' is defined as one type and declared as another"));
- }
- CopyConstruct(op, cur, dst);
-}
-
-void CommandLineFlag::Write(const void* src,
- const flags_internal::FlagOpFn src_op) {
- absl::MutexLock l(InitFlagIfNecessary());
-
- // `src_op` is the marshalling operation corresponding to the declaration
- // visible at the call site. `op` is the Flag's defined marshalling operation.
- // They must match for this operation to be well-defined.
- if (ABSL_PREDICT_FALSE(src_op != op)) {
- ABSL_INTERNAL_LOG(
- ERROR,
- absl::StrCat("Flag '", name,
- "' is defined as one type and declared as another"));
- }
-
- if (ShouldValidateFlagValue(*this)) {
- void* obj = Clone(op, src);
- std::string ignored_error;
- std::string src_as_str = Unparse(marshalling_op, src);
- if (!Parse(marshalling_op, src_as_str, obj, &ignored_error) ||
- !InvokeValidator(obj)) {
- ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '", name,
- "' to invalid value ", src_as_str));
- }
- Delete(op, obj);
- }
-
- modified = true;
- counter++;
- Copy(op, src, cur);
-
- UpdateCopy(this);
- InvokeCallback();
-}
-
-std::string HelpText::GetHelpText() const {
- if (help_function_) return help_function_();
- if (help_message_) return help_message_;
-
- return {};
-}
-
-// Update any copy of the flag value that is stored in an atomic word.
-// In addition if flag has a mutation callback this function invokes it.
-void UpdateCopy(CommandLineFlag* flag) {
-#define STORE_ATOMIC(T) \
- else if (flag->IsOfType<T>()) { \
- flag->StoreAtomic(sizeof(T)); \
- }
-
- if (false) {
- }
- ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(STORE_ATOMIC)
-#undef STORE_ATOMIC
-}
-
-// Return true iff flag value was changed via direct-access.
-bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b) {
- if (!flag->IsAbseilFlag()) {
-// Need to compare values for direct-access flags.
-#define CHANGED_FOR_TYPE(T) \
- if (flag->IsOfType<T>()) { \
- return *reinterpret_cast<const T*>(a) != *reinterpret_cast<const T*>(b); \
- }
-
- CHANGED_FOR_TYPE(bool);
- CHANGED_FOR_TYPE(int32_t);
- CHANGED_FOR_TYPE(int64_t);
- CHANGED_FOR_TYPE(uint64_t);
- CHANGED_FOR_TYPE(double);
- CHANGED_FOR_TYPE(std::string);
-#undef CHANGED_FOR_TYPE
- }
-
- return false;
-}
-
-} // namespace flags_internal
-} // inline namespace lts_2019_08_08
-} // namespace absl
diff --git a/absl/flags/internal/commandlineflag.h b/absl/flags/internal/commandlineflag.h
index 382553d2..6363c661 100644
--- a/absl/flags/internal/commandlineflag.h
+++ b/absl/flags/internal/commandlineflag.h
@@ -16,30 +16,41 @@
#ifndef ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
#define ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
-#include <atomic>
+#include <stddef.h>
+#include <stdint.h>
+#include <memory>
+#include <string>
+#include <typeinfo>
+
+#include "absl/base/config.h"
#include "absl/base/macros.h"
+#include "absl/flags/config.h"
#include "absl/flags/marshalling.h"
-#include "absl/synchronization/mutex.h"
+#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
-// Type-specific operations, eg., parsing, copying, etc. are provided
-// by function specific to that type with a signature matching FlagOpFn.
-enum FlagOp {
- kDelete,
- kClone,
- kCopy,
- kCopyConstruct,
- kSizeof,
- kParse,
- kUnparse
-};
-using FlagOpFn = void* (*)(FlagOp, const void*, void*);
-using FlagMarshallingOpFn = void* (*)(FlagOp, const void*, void*, void*);
+// An alias for flag static type id. Values of type identify the flag value type
+// simialarly to typeid(T), but without relying on RTTI being available. In most
+// cases this id is enough to uniquely identify the flag's value type. In a few
+// cases we'll have to resort to using actual RTTI implementation if it is
+// available.
+using FlagStaticTypeId = void* (*)();
+
+// Address of this function template is used in current implementation as a flag
+// static type id.
+template <typename T>
+void* FlagStaticTypeIdGen() {
+#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI)
+ return const_cast<std::type_info*>(&typeid(T));
+#else
+ return nullptr;
+#endif
+}
// Options that control SetCommandLineOptionWithMode.
enum FlagSettingMode {
@@ -62,215 +73,95 @@ enum ValueSource {
kProgrammaticChange,
};
-// Signature for the help generation function used as an argument for the
-// absl::Flag constructor.
-using HelpGenFunc = std::string (*)();
-
-// Signature for the function generating the initial flag value based (usually
-// based on default value supplied in flag's definition)
-using InitialValGenFunc = void* (*)();
-
-struct CommandLineFlagInfo;
-
-// Signature for the mutation callback used by watched Flags
-// The callback is noexcept.
-// TODO(rogeeff): add noexcept after C++17 support is added.
-using FlagCallback = void (*)();
-
-using FlagValidator = bool (*)();
-
-extern const char kStrippedFlagHelp[];
-
-// The per-type function
-template <typename T>
-void* FlagOps(FlagOp op, const void* v1, void* v2) {
- switch (op) {
- case kDelete:
- delete static_cast<const T*>(v1);
- return nullptr;
- case kClone:
- return new T(*static_cast<const T*>(v1));
- case kCopy:
- *static_cast<T*>(v2) = *static_cast<const T*>(v1);
- return nullptr;
- case kCopyConstruct:
- new (v2) T(*static_cast<const T*>(v1));
- return nullptr;
- case kSizeof:
- return reinterpret_cast<void*>(sizeof(T));
- default:
- return nullptr;
- }
-}
-
-template <typename T>
-void* FlagMarshallingOps(FlagOp op, const void* v1, void* v2, void* v3) {
- switch (op) {
- case kParse: {
- // initialize the temporary instance of type T based on current value in
- // destination (which is going to be flag's default value).
- T temp(*static_cast<T*>(v2));
- if (!absl::ParseFlag<T>(*static_cast<const absl::string_view*>(v1), &temp,
- static_cast<std::string*>(v3))) {
- return nullptr;
- }
- *static_cast<T*>(v2) = std::move(temp);
- return v2;
- }
- case kUnparse:
- *static_cast<std::string*>(v2) =
- absl::UnparseFlag<T>(*static_cast<const T*>(v1));
- return nullptr;
- default:
- return nullptr;
- }
-}
-
-// Functions that invoke flag-type-specific operations.
-inline void Delete(FlagOpFn op, const void* obj) {
- op(flags_internal::kDelete, obj, nullptr);
-}
-
-inline void* Clone(FlagOpFn op, const void* obj) {
- return op(flags_internal::kClone, obj, nullptr);
-}
-
-inline void Copy(FlagOpFn op, const void* src, void* dst) {
- op(flags_internal::kCopy, src, dst);
-}
-
-inline void CopyConstruct(FlagOpFn op, const void* src, void* dst) {
- op(flags_internal::kCopyConstruct, src, dst);
-}
-
-inline bool Parse(FlagMarshallingOpFn op, absl::string_view text, void* dst,
- std::string* error) {
- return op(flags_internal::kParse, &text, dst, error) != nullptr;
-}
-
-inline std::string Unparse(FlagMarshallingOpFn op, const void* val) {
- std::string result;
- op(flags_internal::kUnparse, val, &result, nullptr);
- return result;
-}
-
-inline size_t Sizeof(FlagOpFn op) {
- // This sequence of casts reverses the sequence from base::internal::FlagOps()
- return static_cast<size_t>(reinterpret_cast<intptr_t>(
- op(flags_internal::kSizeof, nullptr, nullptr)));
-}
-
-// The following struct contains the locks in a CommandLineFlag struct.
-// They are in a separate struct that is lazily allocated to avoid problems
-// with static initialization and to avoid multiple allocations.
-struct CommandLineFlagLocks {
- absl::Mutex primary_mu; // protects several fields in CommandLineFlag
- absl::Mutex callback_mu; // used to serialize callbacks
-};
-
-// Holds either a pointer to help text or a function which produces it. This is
-// needed for supporting both static initialization of Flags while supporting
-// the legacy registration framework. We can't use absl::variant<const char*,
-// const char*(*)()> since anybody passing 0 or nullptr in to a CommandLineFlag
-// would find an ambiguity.
-class HelpText {
+// Handle to FlagState objects. Specific flag state objects will restore state
+// of a flag produced this flag state from method CommandLineFlag::SaveState().
+class FlagStateInterface {
public:
- static constexpr HelpText FromFunctionPointer(const HelpGenFunc fn) {
- return HelpText(fn, nullptr);
- }
- static constexpr HelpText FromStaticCString(const char* msg) {
- return HelpText(nullptr, msg);
- }
-
- std::string GetHelpText() const;
-
- HelpText() = delete;
- HelpText(const HelpText&) = default;
- HelpText(HelpText&&) = default;
+ virtual ~FlagStateInterface() {}
- private:
- explicit constexpr HelpText(const HelpGenFunc fn, const char* msg)
- : help_function_(fn), help_message_(msg) {}
-
- HelpGenFunc help_function_;
- const char* help_message_;
+ // Restores the flag originated this object to the saved state.
+ virtual void Restore() const = 0;
};
// Holds all information for a flag.
-struct CommandLineFlag {
- constexpr CommandLineFlag(
- const char* name_arg, HelpText help_text, const char* filename_arg,
- const flags_internal::FlagOpFn op_arg,
- const flags_internal::FlagMarshallingOpFn marshalling_op_arg,
- const flags_internal::InitialValGenFunc initial_value_gen,
- const bool retired_arg, void* def_arg, void* cur_arg)
- : name(name_arg),
- help(help_text),
- filename(filename_arg),
- op(op_arg),
- marshalling_op(marshalling_op_arg),
- make_init_value(initial_value_gen),
- retired(retired_arg),
- inited(false),
- modified(false),
- on_command_line(false),
- validator(nullptr),
- callback(nullptr),
- def(def_arg),
- cur(cur_arg),
- counter(0),
- atomic(kAtomicInit),
- locks(nullptr) {}
-
- // Revert the init routine.
- void Destroy() const;
+class CommandLineFlag {
+ public:
+ constexpr CommandLineFlag() = default;
// Not copyable/assignable.
CommandLineFlag(const CommandLineFlag&) = delete;
CommandLineFlag& operator=(const CommandLineFlag&) = delete;
- absl::string_view Name() const { return name; }
- std::string Help() const { return help.GetHelpText(); }
- bool IsRetired() const { return this->retired; }
- bool IsModified() const;
- void SetModified(bool is_modified);
- bool IsSpecifiedOnCommandLine() const;
- // Returns true iff this is a handle to an Abseil Flag.
- bool IsAbseilFlag() const {
- // Set to null for V1 flags
- return this->make_init_value != nullptr;
- }
-
- absl::string_view Typename() const;
- std::string Filename() const;
- std::string DefaultValue() const;
- std::string CurrentValue() const;
-
- bool HasValidatorFn() const;
- bool SetValidatorFn(FlagValidator fn);
- bool InvokeValidator(const void* value) const;
+ // Non-polymorphic access methods.
// Return true iff flag has type T.
template <typename T>
inline bool IsOfType() const {
- return this->op == &flags_internal::FlagOps<T>;
+ return TypeId() == &flags_internal::FlagStaticTypeIdGen<T>;
}
// Attempts to retrieve the flag value. Returns value on success,
// absl::nullopt otherwise.
template <typename T>
absl::optional<T> Get() const {
- if (IsRetired() || flags_internal::FlagOps<T> != this->op)
+ if (IsRetired() || !IsOfType<T>()) {
return absl::nullopt;
+ }
- T res;
- Read(&res, flags_internal::FlagOps<T>);
-
- return res;
+ // 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);
}
- void SetCallback(const flags_internal::FlagCallback mutation_callback);
- void InvokeCallback();
+ // 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 name of the flag's value type for some built-in types or empty
+ // std::string.
+ virtual absl::string_view Typename() 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 { return false; }
+ // Returns true iff this is a handle to an Abseil Flag.
+ virtual bool IsAbseilFlag() const { return true; }
+ // Returns id of the flag's value type.
+ virtual FlagStaticTypeId TypeId() const = 0;
+ virtual bool IsModified() const = 0;
+ virtual bool IsSpecifiedOnCommandLine() const = 0;
+ virtual std::string DefaultValue() const = 0;
+ virtual std::string CurrentValue() const = 0;
+
+ // Interfaces to operate on validators.
+ virtual bool ValidateInputValue(absl::string_view value) 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<FlagStateInterface> SaveState() = 0;
// Sets the value of the flag based on specified std::string `value`. If the flag
// was successfully set to new value, it returns true. Otherwise, sets `error`
@@ -280,106 +171,43 @@ struct CommandLineFlag {
// * 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.
- bool SetFromString(absl::string_view value,
- flags_internal::FlagSettingMode set_mode,
- flags_internal::ValueSource source, std::string* error);
-
- void StoreAtomic(size_t size);
+ virtual bool SetFromString(absl::string_view value,
+ flags_internal::FlagSettingMode set_mode,
+ flags_internal::ValueSource source,
+ std::string* error) = 0;
- void CheckDefaultValueParsingRoundtrip() const;
- // Invoke the flag validators for old flags.
- // TODO(rogeeff): implement proper validators for Abseil Flags
- bool ValidateDefaultValue() const;
- bool ValidateInputValue(absl::string_view value) const;
-
- // Constant configuration for a particular flag.
- private:
- const char* const name;
- const HelpText help;
- const char* const filename;
+ // Checks that flags default value can be converted to std::string and back to the
+ // flag's value type.
+ virtual void CheckDefaultValueParsingRoundtrip() const = 0;
protected:
- const FlagOpFn op; // Type-specific handler
- const FlagMarshallingOpFn marshalling_op; // Marshalling ops handler
- const InitialValGenFunc make_init_value; // Makes initial value for the flag
- const bool retired; // Is the flag retired?
- std::atomic<bool> inited; // fields have been lazily initialized
+ ~CommandLineFlag() = default;
- // Mutable state (guarded by locks->primary_mu).
- bool modified; // Has flag value been modified?
- bool on_command_line; // Specified on command line.
- FlagValidator validator; // Validator function, or nullptr
- FlagCallback callback; // Mutation callback, or nullptr
- void* def; // Lazily initialized pointer to default value
- void* cur; // Lazily initialized pointer to current value
- int64_t counter; // Mutation counter
-
- // For some types, a copy of the current value is kept in an atomically
- // accessible field.
- static const int64_t kAtomicInit = 0xababababababababll;
- std::atomic<int64_t> atomic;
-
- // Lazily initialized mutexes for this flag value. We cannot inline a
- // SpinLock or Mutex here because those have non-constexpr constructors and
- // so would prevent constant initialization of this type.
- // TODO(rogeeff): fix it once Mutex has constexpr constructor
- struct CommandLineFlagLocks* locks; // locks, laziliy allocated.
-
- // Ensure that the lazily initialized fields of *flag have been initialized,
- // and return the lock which should be locked when flag's state is mutated.
- absl::Mutex* InitFlagIfNecessary() const;
-
- // copy construct new value of flag's type in a memory referenced by dst
- // based on current flag's value
- void Read(void* dst, const flags_internal::FlagOpFn dst_op) const;
- // updates flag's value to *src (locked)
- void Write(const void* src, const flags_internal::FlagOpFn src_op);
-
- friend class FlagRegistry;
- friend class FlagPtrMap;
- friend class FlagSaverImpl;
- friend void FillCommandLineFlagInfo(CommandLineFlag* flag,
- CommandLineFlagInfo* result);
- friend bool TryParseLocked(CommandLineFlag* flag, void* dst,
- absl::string_view value, std::string* err);
- friend absl::Mutex* InitFlag(CommandLineFlag* flag);
+ private:
+ // 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;
};
-// Update any copy of the flag value that is stored in an atomic word.
-// In addition if flag has a mutation callback this function invokes it. While
-// callback is being invoked the primary flag's mutex is unlocked and it is
-// re-locked back after call to callback is completed. Callback invocation is
-// guarded by flag's secondary mutex instead which prevents concurrent callback
-// invocation. Note that it is possible for other thread to grab the primary
-// lock and update flag's value at any time during the callback invocation.
-// This is by design. Callback can get a value of the flag if necessary, but it
-// might be different from the value initiated the callback and it also can be
-// different by the time the callback invocation is completed.
-// Requires that *primary_lock be held in exclusive mode; it may be released
-// and reacquired by the implementation.
-void UpdateCopy(CommandLineFlag* flag);
-// Return true iff flag value was changed via direct-access.
-bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b);
-
-// This macro is the "source of truth" for the list of supported flag types we
-// expect to perform lock free operations on. Specifically it generates code,
-// a one argument macro operating on a type, supplied as a macro argument, for
-// each type in the list.
-#define ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(A) \
- A(bool) \
- A(short) \
- A(unsigned short) \
- A(int) \
- A(unsigned int) \
- A(long) \
- A(unsigned long) \
- A(long long) \
- A(unsigned long long) \
- A(double) \
- A(float)
+// This macro is the "source of truth" for the list of supported flag built-in
+// types.
+#define ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(A) \
+ A(bool) \
+ A(short) \
+ A(unsigned short) \
+ A(int) \
+ A(unsigned int) \
+ A(long) \
+ A(unsigned long) \
+ A(long long) \
+ A(unsigned long long) \
+ A(double) \
+ A(float) \
+ A(std::string) \
+ A(std::vector<std::string>)
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
diff --git a/absl/flags/internal/commandlineflag_test.cc b/absl/flags/internal/commandlineflag_test.cc
index f0d57adb..0e8bc313 100644
--- a/absl/flags/internal/commandlineflag_test.cc
+++ b/absl/flags/internal/commandlineflag_test.cc
@@ -15,12 +15,17 @@
#include "absl/flags/internal/commandlineflag.h"
+#include <memory>
+#include <string>
+
#include "gtest/gtest.h"
#include "absl/flags/flag.h"
#include "absl/flags/internal/registry.h"
+#include "absl/flags/usage_config.h"
#include "absl/memory/memory.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
ABSL_FLAG(int, int_flag, 201, "int_flag help");
ABSL_FLAG(std::string, string_flag, "dflt",
@@ -33,10 +38,26 @@ namespace flags = absl::flags_internal;
class CommandLineFlagTest : public testing::Test {
protected:
+ static void SetUpTestSuite() {
+ // Install a function to normalize filenames before this test is run.
+ absl::FlagsUsageConfig default_config;
+ default_config.normalize_filename = &CommandLineFlagTest::NormalizeFileName;
+ absl::SetFlagsUsageConfig(default_config);
+ }
+
void SetUp() override { flag_saver_ = absl::make_unique<flags::FlagSaver>(); }
void TearDown() override { flag_saver_.reset(); }
private:
+ static std::string NormalizeFileName(absl::string_view fname) {
+#ifdef _WIN32
+ std::string normalized(fname);
+ std::replace(normalized.begin(), normalized.end(), '\\', '/');
+ fname = normalized;
+#endif
+ return std::string(fname);
+ }
+
std::unique_ptr<flags::FlagSaver> flag_saver_;
};
@@ -49,9 +70,10 @@ TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) {
EXPECT_EQ(flag_01->Typename(), "");
EXPECT_TRUE(!flag_01->IsRetired());
EXPECT_TRUE(flag_01->IsOfType<int>());
- EXPECT_TRUE(absl::EndsWith(
- flag_01->Filename(),
- "absl/flags/internal/commandlineflag_test.cc"));
+ EXPECT_TRUE(
+ absl::EndsWith(flag_01->Filename(),
+ "absl/flags/internal/commandlineflag_test.cc"))
+ << flag_01->Filename();
auto* flag_02 = flags::FindCommandLineFlag("string_flag");
@@ -61,9 +83,10 @@ TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) {
EXPECT_EQ(flag_02->Typename(), "");
EXPECT_TRUE(!flag_02->IsRetired());
EXPECT_TRUE(flag_02->IsOfType<std::string>());
- EXPECT_TRUE(absl::EndsWith(
- flag_02->Filename(),
- "absl/flags/internal/commandlineflag_test.cc"));
+ EXPECT_TRUE(
+ absl::EndsWith(flag_02->Filename(),
+ "absl/flags/internal/commandlineflag_test.cc"))
+ << flag_02->Filename();
auto* flag_03 = flags::FindRetiredFlag("bool_retired_flag");
diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc
new file mode 100644
index 00000000..5a921e28
--- /dev/null
+++ b/absl/flags/internal/flag.cc
@@ -0,0 +1,382 @@
+//
+// Copyright 2019 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.
+
+#include "absl/flags/internal/flag.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <atomic>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "absl/base/attributes.h"
+#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"
+#include "absl/synchronization/mutex.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace flags_internal {
+
+// The help message indicating that the commandline flag has been
+// 'stripped'. It will not show up when doing "-help" and its
+// variants. The flag is stripped if ABSL_FLAGS_STRIP_HELP is set to 1
+// before including absl/flags/flag.h
+const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
+
+namespace {
+
+// Currently we only validate flag values for user-defined flag types.
+bool ShouldValidateFlagValue(FlagStaticTypeId flag_type_id) {
+#define DONT_VALIDATE(T) \
+ if (flag_type_id == &FlagStaticTypeIdGen<T>) return false;
+ ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(DONT_VALIDATE)
+#undef DONT_VALIDATE
+
+ return true;
+}
+
+// RAII helper used to temporarily unlock and relock `absl::Mutex`.
+// This is used when we need to ensure that locks are released while
+// invoking user supplied callbacks and then reacquired, since callbacks may
+// need to acquire these locks themselves.
+class MutexRelock {
+ public:
+ explicit MutexRelock(absl::Mutex* mu) : mu_(mu) { mu_->Unlock(); }
+ ~MutexRelock() { mu_->Lock(); }
+
+ MutexRelock(const MutexRelock&) = delete;
+ MutexRelock& operator=(const MutexRelock&) = delete;
+
+ private:
+ absl::Mutex* mu_;
+};
+
+} // namespace
+
+void FlagImpl::Init() {
+ new (&data_guard_) absl::Mutex;
+
+ absl::MutexLock lock(reinterpret_cast<absl::Mutex*>(&data_guard_));
+
+ value_.dynamic = MakeInitValue().release();
+ StoreAtomic();
+}
+
+// Ensures that the lazily initialized data is initialized,
+// and returns pointer to the mutex guarding flags data.
+absl::Mutex* FlagImpl::DataGuard() const {
+ absl::call_once(const_cast<FlagImpl*>(this)->init_control_, &FlagImpl::Init,
+ const_cast<FlagImpl*>(this));
+
+ // data_guard_ is initialized.
+ return reinterpret_cast<absl::Mutex*>(&data_guard_);
+}
+
+void FlagImpl::AssertValidType(FlagStaticTypeId type_id) const {
+ FlagStaticTypeId this_type_id = flags_internal::StaticTypeId(op_);
+
+ // `type_id` is the type id corresponding to the declaration visibile at the
+ // call site. `this_type_id` is the type id corresponding to the type stored
+ // during flag definition. They must match for this operation to be
+ // well-defined.
+ if (ABSL_PREDICT_TRUE(type_id == this_type_id)) return;
+
+ void* lhs_runtime_type_id = type_id();
+ void* rhs_runtime_type_id = this_type_id();
+
+ if (lhs_runtime_type_id == rhs_runtime_type_id) return;
+
+#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI)
+ if (*reinterpret_cast<std::type_info*>(lhs_runtime_type_id) ==
+ *reinterpret_cast<std::type_info*>(rhs_runtime_type_id))
+ return;
+#endif
+
+ ABSL_INTERNAL_LOG(
+ FATAL, absl::StrCat("Flag '", Name(),
+ "' is defined as one type and declared as another"));
+}
+
+std::unique_ptr<void, DynValueDeleter> FlagImpl::MakeInitValue() const {
+ void* res = nullptr;
+ if (DefaultKind() == FlagDefaultKind::kDynamicValue) {
+ res = flags_internal::Clone(op_, default_value_.dynamic_value);
+ } else {
+ res = (*default_value_.gen_func)();
+ }
+ return {res, DynValueDeleter{op_}};
+}
+
+void FlagImpl::StoreValue(const void* src) {
+ flags_internal::Copy(op_, src, value_.dynamic);
+ StoreAtomic();
+ modified_ = true;
+ ++counter_;
+ InvokeCallback();
+}
+
+absl::string_view FlagImpl::Name() const { return name_; }
+
+std::string FlagImpl::Filename() const {
+ return flags_internal::GetUsageConfig().normalize_filename(filename_);
+}
+
+std::string FlagImpl::Help() const {
+ return HelpSourceKind() == FlagHelpKind::kLiteral ? help_.literal
+ : help_.gen_func();
+}
+
+bool FlagImpl::IsModified() const {
+ absl::MutexLock l(DataGuard());
+ return modified_;
+}
+
+bool FlagImpl::IsSpecifiedOnCommandLine() const {
+ absl::MutexLock l(DataGuard());
+ return on_command_line_;
+}
+
+std::string FlagImpl::DefaultValue() const {
+ absl::MutexLock l(DataGuard());
+
+ auto obj = MakeInitValue();
+ return flags_internal::Unparse(op_, obj.get());
+}
+
+std::string FlagImpl::CurrentValue() const {
+ absl::MutexLock l(DataGuard());
+
+ return flags_internal::Unparse(op_, value_.dynamic);
+}
+
+void FlagImpl::SetCallback(const FlagCallbackFunc mutation_callback) {
+ absl::MutexLock l(DataGuard());
+
+ if (callback_ == nullptr) {
+ callback_ = new FlagCallback;
+ }
+ callback_->func = mutation_callback;
+
+ InvokeCallback();
+}
+
+void FlagImpl::InvokeCallback() const {
+ if (!callback_) return;
+
+ // Make a copy of the C-style function pointer that we are about to invoke
+ // before we release the lock guarding it.
+ FlagCallbackFunc cb = callback_->func;
+
+ // If the flag has a mutation callback this function invokes it. While the
+ // callback is being invoked the primary flag's mutex is unlocked and it is
+ // re-locked back after call to callback is completed. Callback invocation is
+ // guarded by flag's secondary mutex instead which prevents concurrent
+ // callback invocation. Note that it is possible for other thread to grab the
+ // primary lock and update flag's value at any time during the callback
+ // invocation. This is by design. Callback can get a value of the flag if
+ // necessary, but it might be different from the value initiated the callback
+ // and it also can be different by the time the callback invocation is
+ // completed. Requires that *primary_lock be held in exclusive mode; it may be
+ // released and reacquired by the implementation.
+ MutexRelock relock(DataGuard());
+ absl::MutexLock lock(&callback_->guard);
+ cb();
+}
+
+bool FlagImpl::RestoreState(const void* value, bool modified,
+ bool on_command_line, int64_t counter) {
+ {
+ absl::MutexLock l(DataGuard());
+
+ if (counter_ == counter) return false;
+ }
+
+ Write(value);
+
+ {
+ absl::MutexLock l(DataGuard());
+
+ modified_ = modified;
+ on_command_line_ = on_command_line;
+ }
+
+ return true;
+}
+
+// Attempts to parse supplied `value` string using parsing routine in the `flag`
+// argument. If parsing successful, this function replaces the dst with newly
+// parsed value. In case if any error is encountered in either step, the error
+// message is stored in 'err'
+std::unique_ptr<void, DynValueDeleter> FlagImpl::TryParse(
+ absl::string_view value, std::string* err) const {
+ std::unique_ptr<void, DynValueDeleter> tentative_value = MakeInitValue();
+
+ std::string parse_err;
+ if (!flags_internal::Parse(op_, value, tentative_value.get(), &parse_err)) {
+ absl::string_view err_sep = parse_err.empty() ? "" : "; ";
+ *err = absl::StrCat("Illegal value '", value, "' specified for flag '",
+ Name(), "'", err_sep, parse_err);
+ return nullptr;
+ }
+
+ return tentative_value;
+}
+
+void FlagImpl::Read(void* dst) const {
+ absl::ReaderMutexLock l(DataGuard());
+
+ flags_internal::CopyConstruct(op_, value_.dynamic, dst);
+}
+
+void FlagImpl::StoreAtomic() {
+ size_t data_size = flags_internal::Sizeof(op_);
+
+ if (data_size <= sizeof(int64_t)) {
+ int64_t t = 0;
+ std::memcpy(&t, value_.dynamic, data_size);
+ value_.atomics.small_atomic.store(t, std::memory_order_release);
+ }
+#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD)
+ else if (data_size <= sizeof(FlagsInternalTwoWordsType)) {
+ FlagsInternalTwoWordsType t{0, 0};
+ std::memcpy(&t, value_.dynamic, data_size);
+ value_.atomics.big_atomic.store(t, std::memory_order_release);
+ }
+#endif
+}
+
+void FlagImpl::Write(const void* src) {
+ absl::MutexLock l(DataGuard());
+
+ if (ShouldValidateFlagValue(flags_internal::StaticTypeId(op_))) {
+ std::unique_ptr<void, DynValueDeleter> obj{flags_internal::Clone(op_, src),
+ DynValueDeleter{op_}};
+ std::string ignored_error;
+ std::string src_as_str = flags_internal::Unparse(op_, src);
+ if (!flags_internal::Parse(op_, src_as_str, obj.get(), &ignored_error)) {
+ ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '", Name(),
+ "' to invalid value ", src_as_str));
+ }
+ }
+
+ StoreValue(src);
+}
+
+// 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 `err`
+// 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.
+bool FlagImpl::SetFromString(absl::string_view value, FlagSettingMode set_mode,
+ ValueSource source, std::string* err) {
+ absl::MutexLock l(DataGuard());
+
+ switch (set_mode) {
+ case SET_FLAGS_VALUE: {
+ // set or modify the flag's value
+ auto tentative_value = TryParse(value, err);
+ if (!tentative_value) return false;
+
+ StoreValue(tentative_value.get());
+
+ if (source == kCommandLine) {
+ on_command_line_ = true;
+ }
+ break;
+ }
+ case SET_FLAG_IF_DEFAULT: {
+ // set the flag's value, but only if it hasn't been set by someone else
+ if (modified_) {
+ // TODO(rogeeff): review and fix this semantic. Currently we do not fail
+ // in this case if flag is modified. This is misleading since the flag's
+ // value is not updated even though we return true.
+ // *err = absl::StrCat(Name(), " is already set to ",
+ // CurrentValue(), "\n");
+ // return false;
+ return true;
+ }
+ auto tentative_value = TryParse(value, err);
+ if (!tentative_value) return false;
+
+ StoreValue(tentative_value.get());
+ break;
+ }
+ case SET_FLAGS_DEFAULT: {
+ auto tentative_value = TryParse(value, err);
+ if (!tentative_value) return false;
+
+ if (DefaultKind() == FlagDefaultKind::kDynamicValue) {
+ void* old_value = default_value_.dynamic_value;
+ default_value_.dynamic_value = tentative_value.release();
+ tentative_value.reset(old_value);
+ } else {
+ default_value_.dynamic_value = tentative_value.release();
+ def_kind_ = static_cast<uint8_t>(FlagDefaultKind::kDynamicValue);
+ }
+
+ if (!modified_) {
+ // Need to set both default value *and* current, in this case
+ StoreValue(default_value_.dynamic_value);
+ modified_ = false;
+ }
+ break;
+ }
+ }
+
+ return true;
+}
+
+void FlagImpl::CheckDefaultValueParsingRoundtrip() const {
+ std::string v = DefaultValue();
+
+ absl::MutexLock lock(DataGuard());
+
+ auto dst = MakeInitValue();
+ std::string error;
+ if (!flags_internal::Parse(op_, v, dst.get(), &error)) {
+ ABSL_INTERNAL_LOG(
+ FATAL,
+ absl::StrCat("Flag ", Name(), " (from ", Filename(),
+ "): std::string form of default value '", v,
+ "' could not be parsed; error=", error));
+ }
+
+ // We do not compare dst to def since parsing/unparsing may make
+ // small changes, e.g., precision loss for floating point types.
+}
+
+bool FlagImpl::ValidateInputValue(absl::string_view value) const {
+ absl::MutexLock l(DataGuard());
+
+ auto obj = MakeInitValue();
+ std::string ignored_error;
+ return flags_internal::Parse(op_, value, obj.get(), &ignored_error);
+}
+
+} // namespace flags_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h
index 9a5d9b4b..35a148cf 100644
--- a/absl/flags/internal/flag.h
+++ b/absl/flags/internal/flag.h
@@ -16,45 +16,547 @@
#ifndef ABSL_FLAGS_INTERNAL_FLAG_H_
#define ABSL_FLAGS_INTERNAL_FLAG_H_
+#include <stdint.h>
+
+#include <atomic>
+#include <cstring>
+#include <memory>
+#include <string>
+#include <type_traits>
+
+#include "absl/base/call_once.h"
+#include "absl/base/config.h"
+#include "absl/base/thread_annotations.h"
+#include "absl/flags/config.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/registry.h"
+#include "absl/memory/memory.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
-// This is "unspecified" implementation of absl::Flag<T> type.
template <typename T>
-class Flag : public flags_internal::CommandLineFlag {
+class Flag;
+
+///////////////////////////////////////////////////////////////////////////////
+// Flag value type operations, eg., parsing, copying, etc. are provided
+// by function specific to that type with a signature matching FlagOpFn.
+
+enum class FlagOp {
+ kDelete,
+ kClone,
+ kCopy,
+ kCopyConstruct,
+ kSizeof,
+ kStaticTypeId,
+ kParse,
+ kUnparse,
+};
+using FlagOpFn = void* (*)(FlagOp, const void*, void*, void*);
+
+// Flag value specific operations routine.
+template <typename T>
+void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3) {
+ switch (op) {
+ case FlagOp::kDelete:
+ delete static_cast<const T*>(v1);
+ return nullptr;
+ case FlagOp::kClone:
+ return new T(*static_cast<const T*>(v1));
+ case FlagOp::kCopy:
+ *static_cast<T*>(v2) = *static_cast<const T*>(v1);
+ return nullptr;
+ case FlagOp::kCopyConstruct:
+ new (v2) T(*static_cast<const T*>(v1));
+ return nullptr;
+ case FlagOp::kSizeof:
+ return reinterpret_cast<void*>(sizeof(T));
+ case FlagOp::kStaticTypeId:
+ return reinterpret_cast<void*>(&FlagStaticTypeIdGen<T>);
+ case FlagOp::kParse: {
+ // Initialize the temporary instance of type T based on current value in
+ // destination (which is going to be flag's default value).
+ T temp(*static_cast<T*>(v2));
+ if (!absl::ParseFlag<T>(*static_cast<const absl::string_view*>(v1), &temp,
+ static_cast<std::string*>(v3))) {
+ return nullptr;
+ }
+ *static_cast<T*>(v2) = std::move(temp);
+ return v2;
+ }
+ case FlagOp::kUnparse:
+ *static_cast<std::string*>(v2) =
+ absl::UnparseFlag<T>(*static_cast<const T*>(v1));
+ return nullptr;
+ default:
+ return nullptr;
+ }
+}
+
+// Deletes memory interpreting obj as flag value type pointer.
+inline void Delete(FlagOpFn op, const void* obj) {
+ op(FlagOp::kDelete, obj, nullptr, nullptr);
+}
+// Makes a copy of flag value pointed by obj.
+inline void* Clone(FlagOpFn op, const void* obj) {
+ return op(FlagOp::kClone, obj, nullptr, nullptr);
+}
+// Copies src to dst interpreting as flag value type pointers.
+inline void Copy(FlagOpFn op, const void* src, void* dst) {
+ op(FlagOp::kCopy, src, dst, nullptr);
+}
+// Construct a copy of flag value in a location pointed by dst
+// based on src - pointer to the flag's value.
+inline void CopyConstruct(FlagOpFn op, const void* src, void* dst) {
+ op(FlagOp::kCopyConstruct, src, dst, nullptr);
+}
+// Returns true if parsing of input text is successfull.
+inline bool Parse(FlagOpFn op, absl::string_view text, void* dst,
+ std::string* error) {
+ return op(FlagOp::kParse, &text, dst, error) != nullptr;
+}
+// Returns string representing supplied value.
+inline std::string Unparse(FlagOpFn op, const void* val) {
+ std::string result;
+ op(FlagOp::kUnparse, val, &result, nullptr);
+ return result;
+}
+// Returns size of flag value type.
+inline size_t Sizeof(FlagOpFn op) {
+ // This sequence of casts reverses the sequence from
+ // `flags_internal::FlagOps()`
+ return static_cast<size_t>(reinterpret_cast<intptr_t>(
+ op(FlagOp::kSizeof, nullptr, nullptr, nullptr)));
+}
+// Returns static type id coresponding to the value type.
+inline FlagStaticTypeId StaticTypeId(FlagOpFn op) {
+ return reinterpret_cast<FlagStaticTypeId>(
+ op(FlagOp::kStaticTypeId, nullptr, nullptr, nullptr));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Persistent state of the flag data.
+
+template <typename T>
+class FlagState : public flags_internal::FlagStateInterface {
+ public:
+ FlagState(Flag<T>* flag, T&& cur, bool modified, bool on_command_line,
+ int64_t counter)
+ : flag_(flag),
+ cur_value_(std::move(cur)),
+ modified_(modified),
+ on_command_line_(on_command_line),
+ counter_(counter) {}
+
+ ~FlagState() override = default;
+
+ private:
+ friend class Flag<T>;
+
+ // Restores the flag to the saved state.
+ void Restore() const override;
+
+ // Flag and saved flag data.
+ Flag<T>* flag_;
+ T cur_value_;
+ bool modified_;
+ bool on_command_line_;
+ int64_t counter_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Flag help auxiliary structs.
+
+// This is help argument for absl::Flag encapsulating the string literal pointer
+// or pointer to function generating it as well as enum descriminating two
+// cases.
+using HelpGenFunc = std::string (*)();
+
+union FlagHelpMsg {
+ constexpr explicit FlagHelpMsg(const char* help_msg) : literal(help_msg) {}
+ constexpr explicit FlagHelpMsg(HelpGenFunc help_gen) : gen_func(help_gen) {}
+
+ const char* literal;
+ HelpGenFunc gen_func;
+};
+
+enum class FlagHelpKind : uint8_t { kLiteral = 0, kGenFunc = 1 };
+
+struct FlagHelpArg {
+ FlagHelpMsg source;
+ FlagHelpKind kind;
+};
+
+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 <typename T>
+const char* HelpConstexprWrap(const T&) {
+ return nullptr;
+}
+constexpr const char* HelpConstexprWrap(const char* p) { return p; }
+constexpr const char* HelpConstexprWrap(char* p) { return p; }
+
+// These two HelpArg overloads allows us to select at compile time one of two
+// way to pass Help argument to absl::Flag. We'll be passing
+// AbslFlagHelpGenFor##name as T and integer 0 as a single argument to prefer
+// first overload if possible. If T::Const is evaluatable on constexpr
+// context (see non template int parameter below) we'll choose first overload.
+// In this case the help message expression is immediately evaluated and is used
+// to construct the absl::Flag. No additionl code is generated by ABSL_FLAG.
+// Otherwise SFINAE kicks in and first overload is dropped from the
+// consideration, in which case the second overload will be used. The second
+// overload does not attempt to evaluate the help message expression
+// immediately and instead delays the evaluation by returing the function
+// pointer (&T::NonConst) genering the help message when necessary. This is
+// evaluatable in constexpr context, but the cost is an extra function being
+// generated in the ABSL_FLAG code.
+template <typename T, int = (T::Const(), 1)>
+constexpr FlagHelpArg HelpArg(int) {
+ return {FlagHelpMsg(T::Const()), FlagHelpKind::kLiteral};
+}
+
+template <typename T>
+constexpr FlagHelpArg HelpArg(char) {
+ return {FlagHelpMsg(&T::NonConst), FlagHelpKind::kGenFunc};
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Flag default value auxiliary structs.
+
+// Signature for the function generating the initial flag value (usually
+// based on default value supplied in flag's definition)
+using FlagDfltGenFunc = void* (*)();
+
+union FlagDefaultSrc {
+ constexpr explicit FlagDefaultSrc(FlagDfltGenFunc gen_func_arg)
+ : gen_func(gen_func_arg) {}
+
+ void* dynamic_value;
+ FlagDfltGenFunc gen_func;
+};
+
+enum class FlagDefaultKind : uint8_t { kDynamicValue = 0, kGenFunc = 1 };
+
+///////////////////////////////////////////////////////////////////////////////
+// Flag current value auxiliary structs.
+
+// The minimum atomic size we believe to generate lock free code, i.e. all
+// trivially copyable types not bigger this size generate lock free code.
+static constexpr int kMinLockFreeAtomicSize = 8;
+
+// The same as kMinLockFreeAtomicSize but maximum atomic size. As double words
+// might use two registers, we want to dispatch the logic for them.
+#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD)
+static constexpr int kMaxLockFreeAtomicSize = 16;
+#else
+static constexpr int kMaxLockFreeAtomicSize = 8;
+#endif
+
+// We can use atomic in cases when it fits in the register, trivially copyable
+// in order to make memcpy operations.
+template <typename T>
+struct IsAtomicFlagTypeTrait {
+ static constexpr bool value =
+ (sizeof(T) <= kMaxLockFreeAtomicSize &&
+ type_traits_internal::is_trivially_copyable<T>::value);
+};
+
+// Clang does not always produce cmpxchg16b instruction when alignment of a 16
+// bytes type is not 16.
+struct alignas(16) FlagsInternalTwoWordsType {
+ int64_t first;
+ int64_t second;
+};
+
+constexpr bool operator==(const FlagsInternalTwoWordsType& that,
+ const FlagsInternalTwoWordsType& other) {
+ return that.first == other.first && that.second == other.second;
+}
+constexpr bool operator!=(const FlagsInternalTwoWordsType& that,
+ const FlagsInternalTwoWordsType& other) {
+ return !(that == other);
+}
+
+constexpr int64_t SmallAtomicInit() { return 0xababababababababll; }
+
+template <typename T, typename S = void>
+struct BestAtomicType {
+ using type = int64_t;
+ static constexpr int64_t AtomicInit() { return SmallAtomicInit(); }
+};
+
+template <typename T>
+struct BestAtomicType<
+ T, typename std::enable_if<(kMinLockFreeAtomicSize < sizeof(T) &&
+ sizeof(T) <= kMaxLockFreeAtomicSize),
+ void>::type> {
+ using type = FlagsInternalTwoWordsType;
+ static constexpr FlagsInternalTwoWordsType AtomicInit() {
+ return {SmallAtomicInit(), SmallAtomicInit()};
+ }
+};
+
+struct FlagValue {
+ // Heap allocated value.
+ void* dynamic = nullptr;
+ // For some types, a copy of the current value is kept in an atomically
+ // accessible field.
+ union Atomics {
+ // Using small atomic for small types.
+ std::atomic<int64_t> small_atomic;
+ template <typename T,
+ typename K = typename std::enable_if<
+ (sizeof(T) <= kMinLockFreeAtomicSize), void>::type>
+ int64_t load() const {
+ return small_atomic.load(std::memory_order_acquire);
+ }
+
+#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD)
+ // Using big atomics for big types.
+ std::atomic<FlagsInternalTwoWordsType> big_atomic;
+ template <typename T, typename K = typename std::enable_if<
+ (kMinLockFreeAtomicSize < sizeof(T) &&
+ sizeof(T) <= kMaxLockFreeAtomicSize),
+ void>::type>
+ FlagsInternalTwoWordsType load() const {
+ return big_atomic.load(std::memory_order_acquire);
+ }
+ constexpr Atomics()
+ : big_atomic{FlagsInternalTwoWordsType{SmallAtomicInit(),
+ SmallAtomicInit()}} {}
+#else
+ constexpr Atomics() : small_atomic{SmallAtomicInit()} {}
+#endif
+ };
+ Atomics atomics{};
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Flag callback auxiliary structs.
+
+// Signature for the mutation callback used by watched Flags
+// The callback is noexcept.
+// TODO(rogeeff): add noexcept after C++17 support is added.
+using FlagCallbackFunc = void (*)();
+
+struct FlagCallback {
+ FlagCallbackFunc func;
+ absl::Mutex guard; // Guard for concurrent callback invocations.
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Flag implementation, which does not depend on flag value type.
+// The class encapsulates the Flag's data and access to it.
+
+struct DynValueDeleter {
+ explicit DynValueDeleter(FlagOpFn op_arg = nullptr) : op(op_arg) {}
+ void operator()(void* ptr) const {
+ if (op != nullptr) Delete(op, ptr);
+ }
+
+ FlagOpFn op;
+};
+
+class FlagImpl {
public:
- constexpr Flag(const char* name, const flags_internal::HelpGenFunc help_gen,
- const char* filename,
- const flags_internal::FlagMarshallingOpFn marshalling_op_arg,
- const flags_internal::InitialValGenFunc initial_value_gen)
- : flags_internal::CommandLineFlag(
- name, flags_internal::HelpText::FromFunctionPointer(help_gen),
- filename, &flags_internal::FlagOps<T>, marshalling_op_arg,
- initial_value_gen,
- /*retired_arg=*/false, /*def_arg=*/nullptr,
- /*cur_arg=*/nullptr) {}
+ constexpr FlagImpl(const char* name, const char* filename, FlagOpFn op,
+ FlagHelpArg help, FlagDfltGenFunc default_value_gen)
+ : name_(name),
+ filename_(filename),
+ op_(op),
+ help_(help.source),
+ help_source_kind_(static_cast<uint8_t>(help.kind)),
+ def_kind_(static_cast<uint8_t>(FlagDefaultKind::kGenFunc)),
+ modified_(false),
+ on_command_line_(false),
+ counter_(0),
+ callback_(nullptr),
+ default_value_(default_value_gen),
+ data_guard_{} {}
+
+ // Constant access methods
+ absl::string_view Name() const;
+ std::string Filename() const;
+ std::string Help() const;
+ bool IsModified() const ABSL_LOCKS_EXCLUDED(*DataGuard());
+ bool IsSpecifiedOnCommandLine() const ABSL_LOCKS_EXCLUDED(*DataGuard());
+ std::string DefaultValue() const ABSL_LOCKS_EXCLUDED(*DataGuard());
+ std::string CurrentValue() const ABSL_LOCKS_EXCLUDED(*DataGuard());
+ void Read(void* dst) const ABSL_LOCKS_EXCLUDED(*DataGuard());
+
+ template <typename T, typename std::enable_if<
+ !IsAtomicFlagTypeTrait<T>::value, int>::type = 0>
+ void Get(T* dst) const {
+ AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>);
+ Read(dst);
+ }
+ // Overload for `GetFlag()` for types that support lock-free reads.
+ template <typename T, typename std::enable_if<IsAtomicFlagTypeTrait<T>::value,
+ int>::type = 0>
+ void Get(T* dst) const {
+ // For flags of types which can be accessed "atomically" we want to avoid
+ // slowing down flag value access due to type validation. That's why
+ // this validation is hidden behind !NDEBUG
+#ifndef NDEBUG
+ AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>);
+#endif
+ using U = flags_internal::BestAtomicType<T>;
+ typename U::type r = value_.atomics.template load<T>();
+ if (r != U::AtomicInit()) {
+ std::memcpy(static_cast<void*>(dst), &r, sizeof(T));
+ } else {
+ Read(dst);
+ }
+ }
+ template <typename T>
+ void Set(const T& src) {
+ AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>);
+ Write(&src);
+ }
+
+ // Mutating access methods
+ void Write(const void* src) ABSL_LOCKS_EXCLUDED(*DataGuard());
+ bool SetFromString(absl::string_view value, FlagSettingMode set_mode,
+ ValueSource source, std::string* err)
+ ABSL_LOCKS_EXCLUDED(*DataGuard());
+ // If possible, updates copy of the Flag's value that is stored in an
+ // atomic word.
+ void StoreAtomic() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
+
+ // Interfaces to operate on callbacks.
+ void SetCallback(const FlagCallbackFunc mutation_callback)
+ ABSL_LOCKS_EXCLUDED(*DataGuard());
+ void InvokeCallback() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
+
+ // Interfaces to save/restore mutable flag data
+ template <typename T>
+ std::unique_ptr<FlagStateInterface> SaveState(Flag<T>* flag) const
+ ABSL_LOCKS_EXCLUDED(*DataGuard()) {
+ T&& cur_value = flag->Get();
+ absl::MutexLock l(DataGuard());
+
+ return absl::make_unique<FlagState<T>>(
+ flag, std::move(cur_value), modified_, on_command_line_, counter_);
+ }
+ bool RestoreState(const void* value, bool modified, bool on_command_line,
+ int64_t counter) ABSL_LOCKS_EXCLUDED(*DataGuard());
+
+ // Value validation interfaces.
+ void CheckDefaultValueParsingRoundtrip() const
+ ABSL_LOCKS_EXCLUDED(*DataGuard());
+ bool ValidateInputValue(absl::string_view value) const
+ ABSL_LOCKS_EXCLUDED(*DataGuard());
+
+ private:
+ // Ensures that `data_guard_` is initialized and returns it.
+ absl::Mutex* DataGuard() const ABSL_LOCK_RETURNED((absl::Mutex*)&data_guard_);
+ // Returns heap allocated value of type T initialized with default value.
+ std::unique_ptr<void, DynValueDeleter> MakeInitValue() const
+ ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
+ // Flag initialization called via absl::call_once.
+ void Init();
+ // Attempts to parse supplied `value` std::string. If parsing is successful,
+ // returns new value. Otherwise returns nullptr.
+ std::unique_ptr<void, DynValueDeleter> TryParse(absl::string_view value,
+ std::string* err) const
+ ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
+ // Stores the flag value based on the pointer to the source.
+ void StoreValue(const void* src) ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
+
+ FlagHelpKind HelpSourceKind() const {
+ return static_cast<FlagHelpKind>(help_source_kind_);
+ }
+ FlagDefaultKind DefaultKind() const
+ ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()) {
+ return static_cast<FlagDefaultKind>(def_kind_);
+ }
+ // Used in read/write operations to validate source/target has correct type.
+ // For example if flag is declared as absl::Flag<int> FLAGS_foo, a call to
+ // absl::GetFlag(FLAGS_foo) validates that the type of FLAGS_foo is indeed
+ // int. To do that we pass the "assumed" type id (which is deduced from type
+ // int) as an argument `op`, which is in turn is validated against the type id
+ // stored in flag object by flag definition statement.
+ void AssertValidType(FlagStaticTypeId type_id) const;
+
+ // Immutable flag's state.
+
+ // Flags name passed to ABSL_FLAG as second arg.
+ const char* const name_;
+ // The file name where ABSL_FLAG resides.
+ const char* const filename_;
+ // Type-specific operations "vtable".
+ const FlagOpFn op_;
+ // Help message literal or function to generate it.
+ const FlagHelpMsg help_;
+ // Indicates if help message was supplied as literal or generator func.
+ const uint8_t help_source_kind_ : 1;
+
+ // ------------------------------------------------------------------------
+ // The bytes containing the const bitfields must not be shared with bytes
+ // containing the mutable bitfields.
+ // ------------------------------------------------------------------------
+
+ // Unique tag for absl::call_once call to initialize this flag.
+ //
+ // The placement of this variable between the immutable and mutable bitfields
+ // is important as prevents them from occupying the same byte. If you remove
+ // this variable, make sure to maintain this property.
+ absl::once_flag init_control_;
+
+ // Mutable flag's state (guarded by `data_guard_`).
+
+ // If def_kind_ == kDynamicValue, default_value_ holds a dynamically allocated
+ // value.
+ uint8_t def_kind_ : 1 ABSL_GUARDED_BY(*DataGuard());
+ // Has this flag's value been modified?
+ bool modified_ : 1 ABSL_GUARDED_BY(*DataGuard());
+ // Has this flag been specified on command line.
+ bool on_command_line_ : 1 ABSL_GUARDED_BY(*DataGuard());
+
+ // Mutation counter
+ int64_t counter_ ABSL_GUARDED_BY(*DataGuard());
+ // Optional flag's callback and absl::Mutex to guard the invocations.
+ FlagCallback* callback_ ABSL_GUARDED_BY(*DataGuard());
+ // Either a pointer to the function generating the default value based on the
+ // value specified in ABSL_FLAG or pointer to the dynamically set default
+ // value via SetCommandLineOptionWithMode. def_kind_ is used to distinguish
+ // these two cases.
+ FlagDefaultSrc default_value_ ABSL_GUARDED_BY(*DataGuard());
+ // Current Flag Value
+ FlagValue value_;
+
+ // This is reserved space for an absl::Mutex to guard flag data. It will be
+ // initialized in FlagImpl::Init via placement new.
+ // We can't use "absl::Mutex data_guard_", since this class is not literal.
+ // We do not want to use "absl::Mutex* data_guard_", since this would require
+ // heap allocation during initialization, which is both slows program startup
+ // and can fail. Using reserved space + placement new allows us to avoid both
+ // problems.
+ alignas(absl::Mutex) mutable char data_guard_[sizeof(absl::Mutex)];
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// The Flag object parameterized by the flag's value type. This class implements
+// flag reflection handle interface.
+
+template <typename T>
+class Flag final : public flags_internal::CommandLineFlag {
+ public:
+ constexpr Flag(const char* name, const char* filename, const FlagHelpArg help,
+ const FlagDfltGenFunc default_value_gen)
+ : impl_(name, filename, &FlagOps<T>, help, default_value_gen) {}
T Get() const {
- // Implementation notes:
- //
- // We are wrapping a union around the value of `T` to serve three purposes:
- //
- // 1. `U.value` has correct size and alignment for a value of type `T`
- // 2. The `U.value` constructor is not invoked since U's constructor does
- // not
- // do it explicitly.
- // 3. The `U.value` destructor is invoked since U's destructor does it
- // explicitly. This makes `U` a kind of RAII wrapper around non default
- // constructible value of T, which is destructed when we leave the
- // scope. We do need to destroy U.value, which is constructed by
- // CommandLineFlag::Read even though we left it in a moved-from state
- // after std::move.
- //
- // All of this serves to avoid requiring `T` being default constructible.
+ // See implementation notes in CommandLineFlag::Get().
union U {
T value;
U() {}
@@ -62,23 +564,69 @@ class Flag : public flags_internal::CommandLineFlag {
};
U u;
- this->Read(&u.value, &flags_internal::FlagOps<T>);
+ impl_.Get(&u.value);
return std::move(u.value);
}
+ void Set(const T& v) { impl_.Set(v); }
+ void SetCallback(const FlagCallbackFunc mutation_callback) {
+ impl_.SetCallback(mutation_callback);
+ }
- bool AtomicGet(T* v) const {
- const int64_t r = this->atomic.load(std::memory_order_acquire);
- if (r != flags_internal::CommandLineFlag::kAtomicInit) {
- memcpy(v, &r, sizeof(T));
- return true;
- }
+ // CommandLineFlag interface
+ absl::string_view Name() const override { return impl_.Name(); }
+ std::string Filename() const override { return impl_.Filename(); }
+ absl::string_view Typename() const override { return ""; }
+ std::string Help() const override { return impl_.Help(); }
+ bool IsModified() const override { return impl_.IsModified(); }
+ bool IsSpecifiedOnCommandLine() const override {
+ return impl_.IsSpecifiedOnCommandLine();
+ }
+ std::string DefaultValue() const override { return impl_.DefaultValue(); }
+ std::string CurrentValue() const override { return impl_.CurrentValue(); }
+ bool ValidateInputValue(absl::string_view value) const override {
+ return impl_.ValidateInputValue(value);
+ }
+
+ // Interfaces to save and restore flags to/from persistent state.
+ // Returns current flag state or nullptr if flag does not support
+ // saving and restoring a state.
+ std::unique_ptr<FlagStateInterface> SaveState() override {
+ return impl_.SaveState(this);
+ }
- return false;
+ // Restores the flag state to the supplied state object. If there is
+ // nothing to restore returns false. Otherwise returns true.
+ bool RestoreState(const FlagState<T>& flag_state) {
+ return impl_.RestoreState(&flag_state.cur_value_, flag_state.modified_,
+ flag_state.on_command_line_, flag_state.counter_);
+ }
+ bool SetFromString(absl::string_view value, FlagSettingMode set_mode,
+ ValueSource source, std::string* error) override {
+ return impl_.SetFromString(value, set_mode, source, error);
}
+ void CheckDefaultValueParsingRoundtrip() const override {
+ impl_.CheckDefaultValueParsingRoundtrip();
+ }
+
+ private:
+ friend class FlagState<T>;
+
+ void Read(void* dst) const override { impl_.Read(dst); }
+ FlagStaticTypeId TypeId() const override { return &FlagStaticTypeIdGen<T>; }
- void Set(const T& v) { this->Write(&v, &flags_internal::FlagOps<T>); }
+ // Flag's data
+ FlagImpl impl_;
};
+template <typename T>
+inline void FlagState<T>::Restore() const {
+ if (flag_->RestoreState(*this)) {
+ ABSL_INTERNAL_LOG(INFO,
+ absl::StrCat("Restore saved value of ", flag_->Name(),
+ " to: ", flag_->CurrentValue()));
+ }
+}
+
// This class facilitates Flag object registration and tail expression-based
// flag definition, for example:
// ABSL_FLAG(int, foo, 42, "Foo help").OnUpdate(NotifyFooWatcher);
@@ -89,7 +637,7 @@ class FlagRegistrar {
if (do_register) flags_internal::RegisterCommandLineFlag(flag_);
}
- FlagRegistrar& OnUpdate(flags_internal::FlagCallback cb) && {
+ FlagRegistrar& OnUpdate(FlagCallbackFunc cb) && {
flag_->SetCallback(cb);
return *this;
}
@@ -117,7 +665,7 @@ T* MakeFromDefaultValue(EmptyBraces) {
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_INTERNAL_FLAG_H_
diff --git a/absl/flags/internal/parse.h b/absl/flags/internal/parse.h
index 6c79dc87..03e8a07b 100644
--- a/absl/flags/internal/parse.h
+++ b/absl/flags/internal/parse.h
@@ -19,6 +19,7 @@
#include <string>
#include <vector>
+#include "absl/base/config.h"
#include "absl/flags/declare.h"
ABSL_DECLARE_FLAG(std::vector<std::string>, flagfile);
@@ -27,7 +28,7 @@ ABSL_DECLARE_FLAG(std::vector<std::string>, tryfromenv);
ABSL_DECLARE_FLAG(std::vector<std::string>, undefok);
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
enum class ArgvListAction { kRemoveParsedArgs, kKeepParsedArgs };
@@ -44,7 +45,7 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
OnUndefinedFlag on_undef_flag);
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_INTERNAL_PARSE_H_
diff --git a/absl/flags/internal/path_util.h b/absl/flags/internal/path_util.h
index 623a7bc9..365c8305 100644
--- a/absl/flags/internal/path_util.h
+++ b/absl/flags/internal/path_util.h
@@ -16,11 +16,12 @@
#ifndef ABSL_FLAGS_INTERNAL_PATH_UTIL_H_
#define ABSL_FLAGS_INTERNAL_PATH_UTIL_H_
+#include "absl/base/config.h"
#include "absl/strings/match.h"
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
// A portable interface that returns the basename of the filename passed as an
@@ -56,7 +57,7 @@ inline absl::string_view Package(absl::string_view filename) {
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_INTERNAL_PATH_UTIL_H_
diff --git a/absl/flags/internal/program_name.cc b/absl/flags/internal/program_name.cc
index 28b8ed38..51d698da 100644
--- a/absl/flags/internal/program_name.cc
+++ b/absl/flags/internal/program_name.cc
@@ -17,16 +17,21 @@
#include <string>
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/base/const_init.h"
+#include "absl/base/thread_annotations.h"
#include "absl/flags/internal/path_util.h"
+#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
ABSL_CONST_INIT static absl::Mutex program_name_guard(absl::kConstInit);
ABSL_CONST_INIT static std::string* program_name
- GUARDED_BY(program_name_guard) = nullptr;
+ ABSL_GUARDED_BY(program_name_guard) = nullptr;
std::string ProgramInvocationName() {
absl::MutexLock l(&program_name_guard);
@@ -51,5 +56,5 @@ void SetProgramInvocationName(absl::string_view prog_name_str) {
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/internal/program_name.h b/absl/flags/internal/program_name.h
index a2f0ca10..b99b94fe 100644
--- a/absl/flags/internal/program_name.h
+++ b/absl/flags/internal/program_name.h
@@ -18,13 +18,14 @@
#include <string>
+#include "absl/base/config.h"
#include "absl/strings/string_view.h"
// --------------------------------------------------------------------
// Program name
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
// Returns program invocation name or "UNKNOWN" if `SetProgramInvocationName()`
@@ -43,7 +44,7 @@ std::string ShortProgramInvocationName();
void SetProgramInvocationName(absl::string_view prog_name_str);
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_INTERNAL_PROGRAM_NAME_H_
diff --git a/absl/flags/internal/program_name_test.cc b/absl/flags/internal/program_name_test.cc
index ed69218b..269142f2 100644
--- a/absl/flags/internal/program_name_test.cc
+++ b/absl/flags/internal/program_name_test.cc
@@ -15,8 +15,11 @@
#include "absl/flags/internal/program_name.h"
+#include <string>
+
#include "gtest/gtest.h"
#include "absl/strings/match.h"
+#include "absl/strings/string_view.h"
namespace {
diff --git a/absl/flags/internal/registry.cc b/absl/flags/internal/registry.cc
index e39264e5..e434a859 100644
--- a/absl/flags/internal/registry.cc
+++ b/absl/flags/internal/registry.cc
@@ -15,9 +15,20 @@
#include "absl/flags/internal/registry.h"
-#include "absl/base/dynamic_annotations.h"
+#include <assert.h>
+#include <stdlib.h>
+
+#include <functional>
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
-#include "absl/flags/config.h"
+#include "absl/base/thread_annotations.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"
@@ -30,20 +41,8 @@
// set it.
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
-namespace {
-
-void DestroyFlag(CommandLineFlag* flag) NO_THREAD_SAFETY_ANALYSIS {
- flag->Destroy();
-
- // CommandLineFlag handle object is heap allocated for non Abseil Flags.
- if (!flag->IsAbseilFlag()) {
- delete flag;
- }
-}
-
-} // namespace
// --------------------------------------------------------------------
// FlagRegistry
@@ -58,17 +57,13 @@ void DestroyFlag(CommandLineFlag* flag) NO_THREAD_SAFETY_ANALYSIS {
class FlagRegistry {
public:
FlagRegistry() = default;
- ~FlagRegistry() {
- for (auto& p : flags_) {
- DestroyFlag(p.second);
- }
- }
+ ~FlagRegistry() = default;
// Store a flag in this registry. Takes ownership of *flag.
void RegisterFlag(CommandLineFlag* flag);
- void Lock() EXCLUSIVE_LOCK_FUNCTION(lock_) { lock_.Lock(); }
- void Unlock() UNLOCK_FUNCTION(lock_) { lock_.Unlock(); }
+ void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION(lock_) { lock_.Lock(); }
+ void Unlock() ABSL_UNLOCK_FUNCTION(lock_) { lock_.Unlock(); }
// Returns the flag object for the specified name, or nullptr if not found.
// Will emit a warning if a 'retired' flag is specified.
@@ -114,6 +109,7 @@ class FlagRegistryLock {
FlagRegistry* const fr_;
};
+void DestroyRetiredFlag(CommandLineFlag* flag);
} // namespace
void FlagRegistry::RegisterFlag(CommandLineFlag* flag) {
@@ -131,7 +127,7 @@ void FlagRegistry::RegisterFlag(CommandLineFlag* flag) {
(flag->IsRetired() ? old_flag->Filename() : flag->Filename()),
"'."),
true);
- } else if (flag->op != old_flag->op) {
+ } else if (flag->TypeId() != old_flag->TypeId()) {
flags_internal::ReportUsageError(
absl::StrCat("Flag '", flag->Name(),
"' was defined more than once but with "
@@ -141,8 +137,8 @@ void FlagRegistry::RegisterFlag(CommandLineFlag* flag) {
flag->Typename(), "', respectively."),
true);
} else if (old_flag->IsRetired()) {
- // Retired definitions are idempotent. Just keep the old one.
- DestroyFlag(flag);
+ // Retired flag can just be deleted.
+ DestroyRetiredFlag(flag);
return;
} else if (old_flag->Filename() != flag->Filename()) {
flags_internal::ReportUsageError(
@@ -201,112 +197,34 @@ CommandLineFlag* FlagRegistry::FindRetiredFlagLocked(absl::string_view name) {
class FlagSaverImpl {
public:
- // Constructs an empty FlagSaverImpl object.
- FlagSaverImpl() {}
- ~FlagSaverImpl() {
- // reclaim memory from each of our CommandLineFlags
- for (const SavedFlag& src : backup_registry_) {
- Delete(src.op, src.current);
- Delete(src.op, src.default_value);
- }
- }
+ FlagSaverImpl() = default;
+ FlagSaverImpl(const FlagSaverImpl&) = delete;
+ void operator=(const FlagSaverImpl&) = delete;
// Saves the flag states from the flag registry into this object.
// It's an error to call this more than once.
- // Must be called when the registry mutex is not held.
void SaveFromRegistry() {
assert(backup_registry_.empty()); // call only once!
- SavedFlag saved;
flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) {
- if (flag->IsRetired()) return;
-
- saved.name = flag->Name();
- saved.op = flag->op;
- saved.marshalling_op = flag->marshalling_op;
- {
- absl::MutexLock l(flag->InitFlagIfNecessary());
- saved.validator = flag->validator;
- saved.modified = flag->modified;
- saved.on_command_line = flag->on_command_line;
- saved.current = Clone(saved.op, flag->cur);
- saved.default_value = Clone(saved.op, flag->def);
- saved.counter = flag->counter;
+ if (auto flag_state = flag->SaveState()) {
+ backup_registry_.emplace_back(std::move(flag_state));
}
- backup_registry_.push_back(saved);
});
}
- // Restores the saved flag states into the flag registry. We
- // assume no flags were added or deleted from the registry since
- // the SaveFromRegistry; if they were, that's trouble! Must be
- // called when the registry mutex is not held.
+ // Restores the saved flag states into the flag registry.
void RestoreToRegistry() {
- FlagRegistry* const global_registry = FlagRegistry::GlobalRegistry();
- FlagRegistryLock frl(global_registry);
- for (const SavedFlag& src : backup_registry_) {
- CommandLineFlag* flag = global_registry->FindFlagLocked(src.name);
- // If null, flag got deleted from registry.
- if (!flag) continue;
-
- bool restored = false;
- {
- absl::MutexLock l(flag->InitFlagIfNecessary());
- flag->validator = src.validator;
- flag->modified = src.modified;
- flag->on_command_line = src.on_command_line;
- if (flag->counter != src.counter ||
- ChangedDirectly(flag, src.default_value, flag->def)) {
- restored = true;
- Copy(src.op, src.default_value, flag->def);
- }
- if (flag->counter != src.counter ||
- ChangedDirectly(flag, src.current, flag->cur)) {
- restored = true;
- Copy(src.op, src.current, flag->cur);
- UpdateCopy(flag);
- flag->InvokeCallback();
- }
- }
-
- if (restored) {
- flag->counter++;
-
- // Revalidate the flag because the validator might store state based
- // on the flag's value, which just changed due to the restore.
- // Failing validation is ignored because it's assumed that the flag
- // was valid previously and there's little that can be done about it
- // here, anyway.
- flag->ValidateInputValue(flag->CurrentValue());
-
- ABSL_INTERNAL_LOG(
- INFO, absl::StrCat("Restore saved value of ", flag->Name(), ": ",
- Unparse(src.marshalling_op, src.current)));
- }
+ for (const auto& flag_state : backup_registry_) {
+ flag_state->Restore();
}
}
private:
- struct SavedFlag {
- absl::string_view name;
- FlagOpFn op;
- FlagMarshallingOpFn marshalling_op;
- int64_t counter;
- bool modified;
- bool on_command_line;
- bool (*validator)();
- const void* current; // nullptr after restore
- const void* default_value; // nullptr after restore
- };
-
- std::vector<SavedFlag> backup_registry_;
-
- FlagSaverImpl(const FlagSaverImpl&); // no copying!
- void operator=(const FlagSaverImpl&);
+ std::vector<std::unique_ptr<flags_internal::FlagStateInterface>>
+ backup_registry_;
};
-FlagSaver::FlagSaver() : impl_(new FlagSaverImpl()) {
- impl_->SaveFromRegistry();
-}
+FlagSaver::FlagSaver() : impl_(new FlagSaverImpl) { impl_->SaveFromRegistry(); }
void FlagSaver::Ignore() {
delete impl_;
@@ -321,44 +239,6 @@ FlagSaver::~FlagSaver() {
}
// --------------------------------------------------------------------
-// GetAllFlags()
-// The main way the FlagRegistry class exposes its data. This
-// returns, as strings, all the info about all the flags in
-// the main registry, sorted first by filename they are defined
-// in, and then by flagname.
-// --------------------------------------------------------------------
-
-struct FilenameFlagnameLess {
- bool operator()(const CommandLineFlagInfo& a,
- const CommandLineFlagInfo& b) const {
- int cmp = absl::string_view(a.filename).compare(b.filename);
- if (cmp != 0) return cmp < 0;
- return a.name < b.name;
- }
-};
-
-void FillCommandLineFlagInfo(CommandLineFlag* flag,
- CommandLineFlagInfo* result) {
- result->name = std::string(flag->Name());
- result->type = std::string(flag->Typename());
- result->description = flag->Help();
- result->filename = flag->Filename();
-
- if (!flag->IsAbseilFlag()) {
- if (!flag->IsModified() && ChangedDirectly(flag, flag->cur, flag->def)) {
- flag->modified = true;
- }
- }
-
- result->current_value = flag->CurrentValue();
- result->default_value = flag->DefaultValue();
- result->is_default = !flag->IsModified();
- result->has_validator_fn = flag->HasValidatorFn();
- absl::MutexLock l(flag->InitFlagIfNecessary());
- result->flag_ptr = flag->IsAbseilFlag() ? nullptr : flag->cur;
-}
-
-// --------------------------------------------------------------------
CommandLineFlag* FindCommandLineFlag(absl::string_view name) {
if (name.empty()) return nullptr;
@@ -393,21 +273,6 @@ void ForEachFlag(std::function<void(CommandLineFlag*)> visitor) {
// --------------------------------------------------------------------
-void GetAllFlags(std::vector<CommandLineFlagInfo>* OUTPUT) {
- flags_internal::ForEachFlag([&](CommandLineFlag* flag) {
- if (flag->IsRetired()) return;
-
- CommandLineFlagInfo fi;
- FillCommandLineFlagInfo(flag, &fi);
- OUTPUT->push_back(fi);
- });
-
- // Now sort the flags, first by filename they occur in, then alphabetically
- std::sort(OUTPUT->begin(), OUTPUT->end(), FilenameFlagnameLess());
-}
-
-// --------------------------------------------------------------------
-
bool RegisterCommandLineFlag(CommandLineFlag* flag) {
FlagRegistry::GlobalRegistry()->RegisterFlag(flag);
return true;
@@ -415,14 +280,55 @@ bool RegisterCommandLineFlag(CommandLineFlag* flag) {
// --------------------------------------------------------------------
-bool Retire(FlagOpFn ops, FlagMarshallingOpFn marshalling_ops,
- const char* name) {
- auto* flag = new CommandLineFlag(
- name,
- /*help_text=*/absl::flags_internal::HelpText::FromStaticCString(nullptr),
- /*filename_arg=*/"RETIRED", ops, marshalling_ops,
- /*initial_value_gen=*/nullptr,
- /*retired_arg=*/true, nullptr, nullptr);
+namespace {
+
+class RetiredFlagObj final : public flags_internal::CommandLineFlag {
+ public:
+ constexpr RetiredFlagObj(const char* name, FlagStaticTypeId type_id)
+ : name_(name), type_id_(type_id) {}
+
+ private:
+ absl::string_view Name() const override { return name_; }
+ std::string Filename() const override { return "RETIRED"; }
+ absl::string_view Typename() const override { return ""; }
+ FlagStaticTypeId TypeId() const override { return type_id_; }
+ std::string Help() const override { return ""; }
+ bool IsRetired() const override { return true; }
+ bool IsModified() const override { return false; }
+ bool IsSpecifiedOnCommandLine() const override { return false; }
+ std::string DefaultValue() const override { return ""; }
+ std::string CurrentValue() const override { return ""; }
+
+ // Any input is valid
+ bool ValidateInputValue(absl::string_view) const override { return true; }
+
+ std::unique_ptr<flags_internal::FlagStateInterface> SaveState() override {
+ return nullptr;
+ }
+
+ bool SetFromString(absl::string_view, flags_internal::FlagSettingMode,
+ flags_internal::ValueSource, std::string*) override {
+ return false;
+ }
+
+ void CheckDefaultValueParsingRoundtrip() const override {}
+
+ void Read(void*) const override {}
+
+ // Data members
+ const char* const name_;
+ const FlagStaticTypeId type_id_;
+};
+
+void DestroyRetiredFlag(flags_internal::CommandLineFlag* flag) {
+ assert(flag->IsRetired());
+ delete static_cast<RetiredFlagObj*>(flag);
+}
+
+} // namespace
+
+bool Retire(const char* name, FlagStaticTypeId type_id) {
+ auto* flag = new flags_internal::RetiredFlagObj(name, type_id);
FlagRegistry::GlobalRegistry()->RegisterFlag(flag);
return true;
}
@@ -441,5 +347,5 @@ bool IsRetiredFlag(absl::string_view name, bool* type_is_bool) {
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/internal/registry.h b/absl/flags/internal/registry.h
index d851d245..69ff889f 100644
--- a/absl/flags/internal/registry.h
+++ b/absl/flags/internal/registry.h
@@ -20,43 +20,18 @@
#include <map>
#include <string>
+#include "absl/base/config.h"
#include "absl/base/macros.h"
#include "absl/flags/internal/commandlineflag.h"
+#include "absl/strings/string_view.h"
// --------------------------------------------------------------------
// Global flags registry API.
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
-// CommandLineFlagInfo holds all information for a flag.
-struct CommandLineFlagInfo {
- std::string name; // the name of the flag
- std::string type; // DO NOT use. Use flag->IsOfType<T>() instead.
- std::string description; // the "help text" associated with the flag
- std::string current_value; // the current value, as a std::string
- std::string default_value; // the default value, as a std::string
- std::string filename; // 'cleaned' version of filename holding the flag
- bool has_validator_fn; // true if RegisterFlagValidator called on this flag
-
- bool is_default; // true if the flag has the default value and
- // has not been set explicitly from the cmdline
- // or via SetCommandLineOption.
-
- // nullptr for ABSL_FLAG. A pointer to the flag's current value
- // otherwise. E.g., for DEFINE_int32(foo, ...), flag_ptr will be
- // &FLAGS_foo.
- const void* flag_ptr;
-};
-
-//-----------------------------------------------------------------------------
-
-void FillCommandLineFlagInfo(CommandLineFlag* flag,
- CommandLineFlagInfo* result);
-
-//-----------------------------------------------------------------------------
-
CommandLineFlag* FindCommandLineFlag(absl::string_view name);
CommandLineFlag* FindRetiredFlag(absl::string_view name);
@@ -69,11 +44,6 @@ void ForEachFlag(std::function<void(CommandLineFlag*)> visitor);
//-----------------------------------------------------------------------------
-// Store the list of all flags in *OUTPUT, sorted by file.
-void GetAllFlags(std::vector<CommandLineFlagInfo>* OUTPUT);
-
-//-----------------------------------------------------------------------------
-
bool RegisterCommandLineFlag(CommandLineFlag*);
//-----------------------------------------------------------------------------
@@ -107,29 +77,14 @@ bool RegisterCommandLineFlag(CommandLineFlag*);
// 4: Remove the old_lib 'retired' registration.
// 5: Eventually delete the graveyard registration entirely.
//
-// Returns bool to enable use in namespace-scope initializers.
-// For example:
-//
-// static const bool dummy = base::RetiredFlag<int32_t>("myflag");
-//
-// Or to declare several at once:
-//
-// static bool dummies[] = {
-// base::RetiredFlag<std::string>("some_string_flag"),
-// base::RetiredFlag<double>("some_double_flag"),
-// base::RetiredFlag<int32_t>("some_int32_flag")
-// };
// Retire flag with name "name" and type indicated by ops.
-bool Retire(FlagOpFn ops, FlagMarshallingOpFn marshalling_ops,
- const char* name);
+bool Retire(const char* name, FlagStaticTypeId type_id);
// Registered a retired flag with name 'flag_name' and type 'T'.
template <typename T>
inline bool RetiredFlag(const char* flag_name) {
- return flags_internal::Retire(flags_internal::FlagOps<T>,
- flags_internal::FlagMarshallingOps<T>,
- flag_name);
+ return flags_internal::Retire(flag_name, &FlagStaticTypeIdGen<T>);
}
// If the flag is retired, returns true and indicates in |*type_is_bool|
@@ -163,7 +118,7 @@ class FlagSaver {
};
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_INTERNAL_REGISTRY_H_
diff --git a/absl/flags/internal/type_erased.cc b/absl/flags/internal/type_erased.cc
index 6b91b261..490bc4eb 100644
--- a/absl/flags/internal/type_erased.cc
+++ b/absl/flags/internal/type_erased.cc
@@ -15,13 +15,19 @@
#include "absl/flags/internal/type_erased.h"
+#include <assert.h>
+
+#include <string>
+
+#include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
-#include "absl/flags/config.h"
+#include "absl/flags/internal/commandlineflag.h"
+#include "absl/flags/internal/registry.h"
#include "absl/flags/usage_config.h"
-#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
bool GetCommandLineOption(absl::string_view name, std::string* value) {
@@ -37,30 +43,6 @@ bool GetCommandLineOption(absl::string_view name, std::string* value) {
return true;
}
-bool GetCommandLineFlagInfo(absl::string_view name,
- CommandLineFlagInfo* OUTPUT) {
- if (name.empty()) return false;
-
- CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name);
- if (flag == nullptr || flag->IsRetired()) {
- return false;
- }
-
- assert(OUTPUT);
- FillCommandLineFlagInfo(flag, OUTPUT);
- return true;
-}
-
-CommandLineFlagInfo GetCommandLineFlagInfoOrDie(absl::string_view name) {
- CommandLineFlagInfo info;
- if (!GetCommandLineFlagInfo(name, &info)) {
- ABSL_INTERNAL_LOG(FATAL, absl::StrCat("Flag '", name, "' does not exist"));
- }
- return info;
-}
-
-// --------------------------------------------------------------------
-
bool SetCommandLineOption(absl::string_view name, absl::string_view value) {
return SetCommandLineOptionWithMode(name, value,
flags_internal::SET_FLAGS_VALUE);
@@ -104,5 +86,5 @@ bool SpecifiedOnCommandLine(absl::string_view name) {
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/internal/type_erased.h b/absl/flags/internal/type_erased.h
index c9de6de9..188429c7 100644
--- a/absl/flags/internal/type_erased.h
+++ b/absl/flags/internal/type_erased.h
@@ -18,14 +18,16 @@
#include <string>
+#include "absl/base/config.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/registry.h"
+#include "absl/strings/string_view.h"
// --------------------------------------------------------------------
// Registry interfaces operating on type erased handles.
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
// If a flag named "name" exists, store its current value in *OUTPUT
@@ -33,17 +35,6 @@ namespace flags_internal {
// Thread-safe.
bool GetCommandLineOption(absl::string_view name, std::string* value);
-// If a flag named "name" exists, store its information in *OUTPUT
-// and return true. Else return false without changing *OUTPUT.
-// Thread-safe.
-bool GetCommandLineFlagInfo(absl::string_view name,
- CommandLineFlagInfo* OUTPUT);
-
-// Returns the CommandLineFlagInfo of the flagname. exit() with an
-// error code if name not found.
-// Thread-safe.
-CommandLineFlagInfo GetCommandLineFlagInfoOrDie(absl::string_view name);
-
// Set the value of the flag named "name" to value. If successful,
// returns true. If not successful (e.g., the flag was not found or
// the value is not a valid value), returns false.
@@ -93,7 +84,7 @@ inline bool GetByName(absl::string_view name, T* dst) {
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_INTERNAL_TYPE_ERASED_H_
diff --git a/absl/flags/internal/type_erased_test.cc b/absl/flags/internal/type_erased_test.cc
index ac749a60..4ce59810 100644
--- a/absl/flags/internal/type_erased_test.cc
+++ b/absl/flags/internal/type_erased_test.cc
@@ -15,12 +15,15 @@
#include "absl/flags/internal/type_erased.h"
-#include <cmath>
+#include <memory>
+#include <string>
#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"
-#include "absl/strings/str_cat.h"
ABSL_FLAG(int, int_flag, 1, "int_flag help");
ABSL_FLAG(std::string, string_flag, "dflt", "string_flag help");
@@ -116,6 +119,13 @@ TEST_F(TypeErasedTest, TestSetCommandLineOptionWithMode_SET_FLAGS_DEFAULT) {
EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "101",
flags::SET_FLAGS_DEFAULT));
+ // Set it again to ensure that resetting logic is covered.
+ EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "102",
+ flags::SET_FLAGS_DEFAULT));
+
+ EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "103",
+ flags::SET_FLAGS_DEFAULT));
+
EXPECT_TRUE(flags::SetCommandLineOptionWithMode("string_flag", "asdfgh",
flags::SET_FLAGS_DEFAULT));
EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh");
diff --git a/absl/flags/internal/usage.cc b/absl/flags/internal/usage.cc
index a9a8a21e..ff907161 100644
--- a/absl/flags/internal/usage.cc
+++ b/absl/flags/internal/usage.cc
@@ -15,18 +15,24 @@
#include "absl/flags/internal/usage.h"
+#include <functional>
#include <map>
+#include <ostream>
#include <string>
+#include <utility>
+#include <vector>
+#include "absl/base/config.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/program_name.h"
+#include "absl/flags/internal/registry.h"
#include "absl/flags/usage_config.h"
-#include "absl/strings/ascii.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
-#include "absl/synchronization/mutex.h"
ABSL_FLAG(bool, help, false,
"show help on important flags for this binary [tip: all flags can "
@@ -44,10 +50,31 @@ ABSL_FLAG(std::string, helpmatch, "",
"show help on modules whose name contains the specified substr");
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
namespace {
+absl::string_view TypenameForHelp(const flags_internal::CommandLineFlag& flag) {
+ // Only report names of v1 built-in types
+#define HANDLE_V1_BUILTIN_TYPE(t) \
+ if (flag.IsOfType<t>()) { \
+ return #t; \
+ }
+
+ HANDLE_V1_BUILTIN_TYPE(bool);
+ HANDLE_V1_BUILTIN_TYPE(int32_t);
+ HANDLE_V1_BUILTIN_TYPE(int64_t);
+ HANDLE_V1_BUILTIN_TYPE(uint64_t);
+ HANDLE_V1_BUILTIN_TYPE(double);
+#undef HANDLE_V1_BUILTIN_TYPE
+
+ if (flag.IsOfType<std::string>()) {
+ return "string";
+ }
+
+ return "";
+}
+
// This class is used to emit an XML element with `tag` and `text`.
// It adds opening and closing tags and escapes special characters in the text.
// For example:
@@ -180,14 +207,14 @@ void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag,
FlagHelpPrettyPrinter printer(80, out); // Max line length is 80.
// Flag name.
- printer.Write(absl::StrCat("-", flag.Name()));
+ printer.Write(absl::StrCat("--", flag.Name()));
// Flag help.
printer.Write(absl::StrCat("(", flag.Help(), ");"), /*wrap_line=*/true);
// Flag data type (for V1 flags only).
if (!flag.IsAbseilFlag() && !flag.IsRetired()) {
- printer.Write(absl::StrCat("type: ", flag.Typename(), ";"));
+ printer.Write(absl::StrCat("type: ", TypenameForHelp(flag), ";"));
}
// The listed default value will be the actual default from the flag
@@ -224,6 +251,9 @@ void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
} else {
// XML schema is not a part of our public API for now.
out << "<?xml version=\"1.0\"?>\n"
+ << "<!-- This output should be used with care. We do not report type "
+ "names for flags with user defined types -->\n"
+ << "<!-- Prefer flag only_check_args for validating flag inputs -->\n"
// The document.
<< "<AllFlags>\n"
// The program name and usage.
@@ -381,5 +411,5 @@ int HandleUsageFlags(std::ostream& out,
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/internal/usage.h b/absl/flags/internal/usage.h
index f3794afe..6b080fd1 100644
--- a/absl/flags/internal/usage.h
+++ b/absl/flags/internal/usage.h
@@ -19,6 +19,7 @@
#include <iosfwd>
#include <string>
+#include "absl/base/config.h"
#include "absl/flags/declare.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/strings/string_view.h"
@@ -27,7 +28,7 @@
// Usage reporting interfaces
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
// The format to report the help messages in.
@@ -65,7 +66,7 @@ int HandleUsageFlags(std::ostream& out,
absl::string_view program_usage_message);
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
ABSL_DECLARE_FLAG(bool, help);
diff --git a/absl/flags/internal/usage_test.cc b/absl/flags/internal/usage_test.cc
index 781e1d2b..e1e57e55 100644
--- a/absl/flags/internal/usage_test.cc
+++ b/absl/flags/internal/usage_test.cc
@@ -15,17 +15,23 @@
#include "absl/flags/internal/usage.h"
+#include <stdint.h>
+
#include <sstream>
+#include <string>
#include "gtest/gtest.h"
+#include "absl/flags/declare.h"
#include "absl/flags/flag.h"
+#include "absl/flags/internal/parse.h"
#include "absl/flags/internal/path_util.h"
#include "absl/flags/internal/program_name.h"
-#include "absl/flags/parse.h"
+#include "absl/flags/internal/registry.h"
#include "absl/flags/usage.h"
#include "absl/flags/usage_config.h"
#include "absl/memory/memory.h"
#include "absl/strings/match.h"
+#include "absl/strings/string_view.h"
ABSL_FLAG(int, usage_reporting_test_flag_01, 101,
"usage_reporting_test_flag_01 help message");
@@ -67,9 +73,9 @@ static std::string NormalizeFileName(absl::string_view fname) {
fname = normalized;
#endif
- auto absl_pos = fname.find("/absl/");
+ auto absl_pos = fname.rfind("absl/");
if (absl_pos != absl::string_view::npos) {
- fname = fname.substr(absl_pos + 1);
+ fname = fname.substr(absl_pos);
}
return std::string(fname);
}
@@ -111,7 +117,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_01) {
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
EXPECT_EQ(
test_buf.str(),
- R"( -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
+ R"( --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
default: 101;
)");
}
@@ -123,7 +129,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_02) {
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
EXPECT_EQ(
test_buf.str(),
- R"( -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
+ R"( --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
default: false;
)");
}
@@ -135,7 +141,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_03) {
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
EXPECT_EQ(
test_buf.str(),
- R"( -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
+ R"( --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
default: 1.03;
)");
}
@@ -147,7 +153,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_04) {
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
EXPECT_EQ(
test_buf.str(),
- R"( -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
+ R"( --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
default: 1000000000000004;
)");
}
@@ -159,7 +165,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_05) {
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
EXPECT_EQ(
test_buf.str(),
- R"( -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
+ R"( --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
default: UDT{};
)");
}
@@ -171,17 +177,17 @@ TEST_F(UsageReportingTest, TestFlagsHelpHRF) {
R"(usage_test: Custom usage message
Flags from absl/flags/internal/usage_test.cc:
- -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
+ --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
default: 101;
- -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
+ --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
default: false;
- -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
+ --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
default: 1.03;
- -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
+ --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
default: 1000000000000004;
- -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
+ --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
default: UDT{};
- -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
+ --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
Some more help.
Even more long long long long long long long long long long long long help
@@ -247,17 +253,17 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpshort) {
R"(usage_test: Custom usage message
Flags from absl/flags/internal/usage_test.cc:
- -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
+ --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
default: 101;
- -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
+ --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
default: false;
- -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
+ --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
default: 1.03;
- -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
+ --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
default: 1000000000000004;
- -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
+ --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
default: UDT{};
- -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
+ --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
Some more help.
Even more long long long long long long long long long long long long help
@@ -276,17 +282,17 @@ TEST_F(UsageReportingTest, TestUsageFlag_help) {
R"(usage_test: Custom usage message
Flags from absl/flags/internal/usage_test.cc:
- -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
+ --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
default: 101;
- -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
+ --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
default: false;
- -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
+ --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
default: 1.03;
- -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
+ --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
default: 1000000000000004;
- -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
+ --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
default: UDT{};
- -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
+ --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
Some more help.
Even more long long long long long long long long long long long long help
@@ -307,17 +313,17 @@ TEST_F(UsageReportingTest, TestUsageFlag_helppackage) {
R"(usage_test: Custom usage message
Flags from absl/flags/internal/usage_test.cc:
- -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
+ --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
default: 101;
- -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
+ --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
default: false;
- -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
+ --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
default: 1.03;
- -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
+ --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
default: 1000000000000004;
- -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
+ --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
default: UDT{};
- -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
+ --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
Some more help.
Even more long long long long long long long long long long long long help
@@ -372,17 +378,17 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
R"(usage_test: Custom usage message
Flags from absl/flags/internal/usage_test.cc:
- -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
+ --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
default: 101;
- -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
+ --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
default: false;
- -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
+ --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
default: 1.03;
- -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
+ --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
default: 1000000000000004;
- -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
+ --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
default: UDT{};
- -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
+ --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
Some more help.
Even more long long long long long long long long long long long long help
@@ -395,7 +401,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
} // namespace
int main(int argc, char* argv[]) {
- absl::GetFlag(FLAGS_undefok); // Force linking of parse.cc
+ (void)absl::GetFlag(FLAGS_undefok); // Force linking of parse.cc
flags::SetProgramInvocationName("usage_test");
absl::SetProgramUsageMessage(kTestUsageMessage);
::testing::InitGoogleTest(&argc, argv);
diff --git a/absl/flags/marshalling.cc b/absl/flags/marshalling.cc
index 71b01d77..6f2ddda8 100644
--- a/absl/flags/marshalling.cc
+++ b/absl/flags/marshalling.cc
@@ -15,18 +15,28 @@
#include "absl/flags/marshalling.h"
+#include <stddef.h>
+
+#include <cmath>
#include <limits>
+#include <string>
+#include <type_traits>
+#include <vector>
+#include "absl/base/config.h"
+#include "absl/base/log_severity.h"
#include "absl/base/macros.h"
+#include "absl/strings/ascii.h"
#include "absl/strings/match.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_split.h"
+#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
// --------------------------------------------------------------------
@@ -187,5 +197,44 @@ std::string AbslUnparseFlag(const std::vector<std::string>& v) {
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+
+bool AbslParseFlag(absl::string_view text, absl::LogSeverity* dst,
+ std::string* err) {
+ text = absl::StripAsciiWhitespace(text);
+ if (text.empty()) {
+ *err = "no value provided";
+ return false;
+ }
+ if (text.front() == 'k' || text.front() == 'K') text.remove_prefix(1);
+ if (absl::EqualsIgnoreCase(text, "info")) {
+ *dst = absl::LogSeverity::kInfo;
+ return true;
+ }
+ if (absl::EqualsIgnoreCase(text, "warning")) {
+ *dst = absl::LogSeverity::kWarning;
+ return true;
+ }
+ if (absl::EqualsIgnoreCase(text, "error")) {
+ *dst = absl::LogSeverity::kError;
+ return true;
+ }
+ if (absl::EqualsIgnoreCase(text, "fatal")) {
+ *dst = absl::LogSeverity::kFatal;
+ return true;
+ }
+ std::underlying_type<absl::LogSeverity>::type numeric_value;
+ if (absl::ParseFlag(text, &numeric_value, err)) {
+ *dst = static_cast<absl::LogSeverity>(numeric_value);
+ return true;
+ }
+ *err = "only integers and absl::LogSeverity enumerators are accepted";
+ return false;
+}
+
+std::string AbslUnparseFlag(absl::LogSeverity v) {
+ if (v == absl::NormalizeLogSeverity(v)) return absl::LogSeverityName(v);
+ return absl::UnparseFlag(static_cast<int>(v));
+}
+
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/marshalling.h b/absl/flags/marshalling.h
index 5598d444..0b503354 100644
--- a/absl/flags/marshalling.h
+++ b/absl/flags/marshalling.h
@@ -33,15 +33,16 @@
// * `double`
// * `std::string`
// * `std::vector<std::string>`
+// * `absl::LogSeverity` (provided natively for layering reasons)
//
// Note that support for integral types is implemented using overloads for
// variable-width fundamental types (`short`, `int`, `long`, etc.). However,
// you should prefer the fixed-width integral types (`int32_t`, `uint64_t`,
// etc.) we've noted above within flag definitions.
-
//
// In addition, several Abseil libraries provide their own custom support for
-// Abseil flags.
+// Abseil flags. Documentation for these formats is provided in the type's
+// `AbslParseFlag()` definition.
//
// The Abseil time library provides the following support for civil time values:
//
@@ -164,10 +165,11 @@
#include <string>
#include <vector>
+#include "absl/base/config.h"
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
// Overloads of `AbslParseFlag()` and `AbslUnparseFlag()` for fundamental types.
@@ -179,8 +181,8 @@ bool AbslParseFlag(absl::string_view, unsigned int*, std::string*); // NOLINT
bool AbslParseFlag(absl::string_view, long*, std::string*); // NOLINT
bool AbslParseFlag(absl::string_view, unsigned long*, std::string*); // NOLINT
bool AbslParseFlag(absl::string_view, long long*, std::string*); // NOLINT
-bool AbslParseFlag(absl::string_view, unsigned long long*,
- std::string*); // NOLINT
+bool AbslParseFlag(absl::string_view, unsigned long long*, // NOLINT
+ std::string*);
bool AbslParseFlag(absl::string_view, float*, std::string*);
bool AbslParseFlag(absl::string_view, double*, std::string*);
bool AbslParseFlag(absl::string_view, std::string*, std::string*);
@@ -249,7 +251,14 @@ inline std::string UnparseFlag(const T& v) {
return flags_internal::Unparse(v);
}
-} // inline namespace lts_2019_08_08
+// Overloads for `absl::LogSeverity` can't (easily) appear alongside that type's
+// definition because it is layered below flags. See proper documentation in
+// base/log_severity.h.
+enum class LogSeverity : int;
+bool AbslParseFlag(absl::string_view, absl::LogSeverity*, std::string*);
+std::string AbslUnparseFlag(absl::LogSeverity);
+
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_MARSHALLING_H_
diff --git a/absl/flags/marshalling_test.cc b/absl/flags/marshalling_test.cc
index 37cd1940..4a64ce11 100644
--- a/absl/flags/marshalling_test.cc
+++ b/absl/flags/marshalling_test.cc
@@ -15,7 +15,12 @@
#include "absl/flags/marshalling.h"
+#include <stdint.h>
+
#include <cmath>
+#include <limits>
+#include <string>
+#include <vector>
#include "gtest/gtest.h"
diff --git a/absl/flags/parse.cc b/absl/flags/parse.cc
index fd80a0c8..812e4981 100644
--- a/absl/flags/parse.cc
+++ b/absl/flags/parse.cc
@@ -17,43 +17,58 @@
#include <stdlib.h>
+#include <algorithm>
#include <fstream>
#include <iostream>
+#include <iterator>
+#include <string>
#include <tuple>
+#include <utility>
+#include <vector>
#ifdef _WIN32
#include <windows.h>
#endif
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/base/const_init.h"
+#include "absl/base/thread_annotations.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/program_name.h"
#include "absl/flags/internal/registry.h"
#include "absl/flags/internal/usage.h"
#include "absl/flags/usage.h"
#include "absl/flags/usage_config.h"
+#include "absl/strings/ascii.h"
#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "absl/synchronization/mutex.h"
// --------------------------------------------------------------------
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
namespace {
ABSL_CONST_INIT absl::Mutex processing_checks_guard(absl::kConstInit);
ABSL_CONST_INIT bool flagfile_needs_processing
- GUARDED_BY(processing_checks_guard) = false;
+ ABSL_GUARDED_BY(processing_checks_guard) = false;
ABSL_CONST_INIT bool fromenv_needs_processing
- GUARDED_BY(processing_checks_guard) = false;
+ ABSL_GUARDED_BY(processing_checks_guard) = false;
ABSL_CONST_INIT bool tryfromenv_needs_processing
- GUARDED_BY(processing_checks_guard) = false;
+ ABSL_GUARDED_BY(processing_checks_guard) = false;
} // namespace
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
ABSL_FLAG(std::vector<std::string>, flagfile, {},
@@ -111,7 +126,7 @@ ABSL_FLAG(std::vector<std::string>, undefok, {},
"with that name");
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
namespace {
@@ -280,9 +295,7 @@ void CheckDefaultValuesParsingRoundtrip() {
#define IGNORE_TYPE(T) \
if (flag->IsOfType<T>()) return;
- ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(IGNORE_TYPE)
- IGNORE_TYPE(std::string)
- IGNORE_TYPE(std::vector<std::string>)
+ ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(IGNORE_TYPE)
#undef IGNORE_TYPE
flag->CheckDefaultValueParsingRoundtrip();
@@ -525,7 +538,7 @@ std::tuple<bool, absl::string_view> DeduceFlagValue(const CommandLineFlag& flag,
// --my_string_var --foo=bar
// We look for a flag of std::string type, whose value begins with a
// dash and corresponds to known flag or standalone --.
- if (value[0] == '-' && flag.IsOfType<std::string>()) {
+ if (!value.empty() && value[0] == '-' && flag.IsOfType<std::string>()) {
auto maybe_flag_name = std::get<0>(SplitNameAndValue(value.substr(1)));
if (maybe_flag_name.empty() ||
@@ -751,5 +764,5 @@ std::vector<char*> ParseCommandLine(int argc, char* argv[]) {
flags_internal::OnUndefinedFlag::kAbortIfUndefined);
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/parse.h b/absl/flags/parse.h
index 469bd506..f37b0602 100644
--- a/absl/flags/parse.h
+++ b/absl/flags/parse.h
@@ -26,10 +26,11 @@
#include <string>
#include <vector>
+#include "absl/base/config.h"
#include "absl/flags/internal/parse.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// ParseCommandLine()
//
@@ -54,7 +55,7 @@ inline namespace lts_2019_08_08 {
// help messages and then exits the program.
std::vector<char*> ParseCommandLine(int argc, char* argv[]);
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_PARSE_H_
diff --git a/absl/flags/parse_test.cc b/absl/flags/parse_test.cc
index 447a3bc7..6f49377a 100644
--- a/absl/flags/parse_test.cc
+++ b/absl/flags/parse_test.cc
@@ -15,14 +15,22 @@
#include "absl/flags/parse.h"
+#include <stdlib.h>
+
#include <fstream>
+#include <string>
+#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/internal/scoped_set_env.h"
+#include "absl/flags/declare.h"
#include "absl/flags/flag.h"
+#include "absl/flags/internal/parse.h"
+#include "absl/flags/internal/registry.h"
#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
#include "absl/types/span.h"
@@ -92,8 +100,11 @@ const std::string& GetTestTempDir() {
auto len = GetTempPathA(MAX_PATH, temp_path_buffer);
if (len < MAX_PATH && len != 0) {
- std::string temp_dir_name = absl::StrCat(
- temp_path_buffer, "\\parse_test.", GetCurrentProcessId());
+ std::string temp_dir_name = temp_path_buffer;
+ if (!absl::EndsWith(temp_dir_name, "\\")) {
+ temp_dir_name.push_back('\\');
+ }
+ absl::StrAppend(&temp_dir_name, "parse_test.", GetCurrentProcessId());
if (CreateDirectoryA(temp_dir_name.c_str(), nullptr)) {
*res = temp_dir_name;
}
@@ -104,11 +115,11 @@ const std::string& GetTestTempDir() {
*res = unique_name;
}
#endif
+ }
- if (res->empty()) {
- ABSL_INTERNAL_LOG(FATAL,
- "Failed to make temporary directory for data files");
- }
+ if (res->empty()) {
+ ABSL_INTERNAL_LOG(FATAL,
+ "Failed to make temporary directory for data files");
}
#ifdef _WIN32
diff --git a/absl/flags/usage.cc b/absl/flags/usage.cc
index 12c346b7..452f6675 100644
--- a/absl/flags/usage.cc
+++ b/absl/flags/usage.cc
@@ -14,18 +14,25 @@
// limitations under the License.
#include "absl/flags/usage.h"
+#include <stdlib.h>
+
#include <string>
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/base/const_init.h"
+#include "absl/base/thread_annotations.h"
#include "absl/flags/internal/usage.h"
+#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
namespace {
ABSL_CONST_INIT absl::Mutex usage_message_guard(absl::kConstInit);
ABSL_CONST_INIT std::string* program_usage_message
- GUARDED_BY(usage_message_guard) = nullptr;
+ ABSL_GUARDED_BY(usage_message_guard) = nullptr;
} // namespace
} // namespace flags_internal
@@ -54,5 +61,5 @@ absl::string_view ProgramUsageMessage() {
: "Warning: SetProgramUsageMessage() never called";
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/usage.h b/absl/flags/usage.h
index c232a7d0..ad12ab7a 100644
--- a/absl/flags/usage.h
+++ b/absl/flags/usage.h
@@ -16,13 +16,14 @@
#ifndef ABSL_FLAGS_USAGE_H_
#define ABSL_FLAGS_USAGE_H_
+#include "absl/base/config.h"
#include "absl/strings/string_view.h"
// --------------------------------------------------------------------
// Usage reporting interfaces
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// Sets the "usage" message to be used by help reporting routines.
// For example:
@@ -36,7 +37,7 @@ void SetProgramUsageMessage(absl::string_view new_usage_message);
// Returns the usage message set by SetProgramUsageMessage().
absl::string_view ProgramUsageMessage();
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_USAGE_H_
diff --git a/absl/flags/usage_config.cc b/absl/flags/usage_config.cc
index a538acd5..2d837ec5 100644
--- a/absl/flags/usage_config.cc
+++ b/absl/flags/usage_config.cc
@@ -16,12 +16,16 @@
#include "absl/flags/usage_config.h"
#include <iostream>
-#include <memory>
+#include <string>
#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/base/const_init.h"
+#include "absl/base/thread_annotations.h"
#include "absl/flags/internal/path_util.h"
#include "absl/flags/internal/program_name.h"
-#include "absl/strings/str_cat.h"
+#include "absl/strings/match.h"
+#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "absl/synchronization/mutex.h"
@@ -34,7 +38,7 @@ ABSL_ATTRIBUTE_WEAK void AbslInternalReportFatalUsageError(absl::string_view) {}
} // extern "C"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
namespace {
@@ -95,7 +99,7 @@ std::string NormalizeFilename(absl::string_view filename) {
ABSL_CONST_INIT absl::Mutex custom_usage_config_guard(absl::kConstInit);
ABSL_CONST_INIT FlagsUsageConfig* custom_usage_config
- GUARDED_BY(custom_usage_config_guard) = nullptr;
+ ABSL_GUARDED_BY(custom_usage_config_guard) = nullptr;
} // namespace
@@ -150,5 +154,5 @@ void SetFlagsUsageConfig(FlagsUsageConfig usage_config) {
flags_internal::custom_usage_config = new FlagsUsageConfig(usage_config);
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/usage_config.h b/absl/flags/usage_config.h
index c6d34e4a..0ed7e1b4 100644
--- a/absl/flags/usage_config.h
+++ b/absl/flags/usage_config.h
@@ -27,6 +27,7 @@
#include <functional>
#include <string>
+#include "absl/base/config.h"
#include "absl/strings/string_view.h"
// -----------------------------------------------------------------------------
@@ -54,7 +55,7 @@
// Shows help on modules whose name contains the specified substring
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
using FlagKindFilter = std::function<bool (absl::string_view)>;
@@ -102,7 +103,7 @@ struct FlagsUsageConfig {
// normalize_filename("/my_company/some_long_path/src/project/file.cc")
// might produce
// "project/file.cc".
- std::function<std::string (absl::string_view)> normalize_filename;
+ std::function<std::string(absl::string_view)> normalize_filename;
};
// SetFlagsUsageConfig()
@@ -119,7 +120,7 @@ FlagsUsageConfig GetUsageConfig();
void ReportUsageError(absl::string_view msg, bool is_fatal);
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
extern "C" {
diff --git a/absl/flags/usage_config_test.cc b/absl/flags/usage_config_test.cc
index 3bde13af..70eca30b 100644
--- a/absl/flags/usage_config_test.cc
+++ b/absl/flags/usage_config_test.cc
@@ -15,10 +15,13 @@
#include "absl/flags/usage_config.h"
+#include <string>
+
#include "gtest/gtest.h"
#include "absl/flags/internal/path_util.h"
#include "absl/flags/internal/program_name.h"
#include "absl/strings/match.h"
+#include "absl/strings/string_view.h"
namespace {