summaryrefslogtreecommitdiff
path: root/absl/flags/flag.h
diff options
context:
space:
mode:
Diffstat (limited to 'absl/flags/flag.h')
-rw-r--r--absl/flags/flag.h170
1 files changed, 145 insertions, 25 deletions
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
//