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.h106
1 files changed, 104 insertions, 2 deletions
diff --git a/absl/flags/flag.h b/absl/flags/flag.h
index 36c771cf..881f8502 100644
--- a/absl/flags/flag.h
+++ b/absl/flags/flag.h
@@ -63,8 +63,100 @@ namespace absl {
// 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)
template <typename T>
using Flag = flags_internal::Flag<T>;
+#else
+// MSVC debug builds do not implement constexpr correctly for classes with
+// virtual methods. To work around this we adding 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 virtual methods 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 constructed. 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.
+
+namespace flags_internal {
+void LockGlobalConstructionGuard();
+void UnlockGlobalConstructionGuard();
+} // namespace flags_internal
+
+template <typename T>
+class Flag {
+ public:
+ constexpr Flag(const char* name, const flags_internal::HelpGenFunc help_gen,
+ const char* filename,
+ const flags_internal::FlagMarshallingOpFn marshalling_op,
+ const flags_internal::InitialValGenFunc initial_value_gen)
+ : name_(name),
+ help_gen_(help_gen),
+ filename_(filename),
+ marshalling_op_(marshalling_op),
+ initial_value_gen_(initial_value_gen),
+ inited_(false) {}
+
+ flags_internal::Flag<T>* GetImpl() const {
+ if (!inited_.load(std::memory_order_acquire)) {
+ flags_internal::LockGlobalConstructionGuard();
+
+ if (inited_.load(std::memory_order_acquire)) {
+ return impl_;
+ }
+
+ impl_ = new flags_internal::Flag<T>(name_, help_gen_, filename_,
+ marshalling_op_, initial_value_gen_);
+ inited_.store(true, std::memory_order_release);
+
+ flags_internal::UnlockGlobalConstructionGuard();
+ }
+
+ return impl_;
+ }
+
+ // absl::Flag API
+ 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(); }
+ void SetModified(bool is_modified) { GetImpl()->SetModified(is_modified); }
+ bool IsSpecifiedOnCommandLine() const {
+ 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(); }
+ bool HasValidatorFn() const { return GetImpl()->HasValidatorFn(); }
+ bool InvokeValidator(const void* value) const {
+ return GetImpl()->InvokeValidator(value);
+ }
+ template <typename T>
+ inline bool IsOfType() const {
+ return GetImpl()->IsOftype<T>();
+ }
+ 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::FlagCallback mutation_callback) {
+ GetImpl()->SetCallback(mutation_callback);
+ }
+ void InvokeCallback() { GetImpl()->InvokeCallback(); }
+
+ private:
+ const char* name_;
+ const flags_internal::HelpGenFunc help_gen_;
+ const char* filename_;
+ const flags_internal::FlagMarshallingOpFn marshalling_op_;
+ const flags_internal::InitialValGenFunc initial_value_gen_;
+
+ mutable std::atomic<bool> inited_;
+ mutable flags_internal::Flag<T>* impl_ = nullptr;
+};
+#endif
// GetFlag()
//
@@ -83,7 +175,7 @@ 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) {
+ABSL_MUST_USE_RESULT T GetFlag(const absl::Flag<T>& flag) {
#define ABSL_FLAGS_INTERNAL_LOCK_FREE_VALIDATE(BIT) \
static_assert( \
!std::is_same<T, BIT>::value, \
@@ -96,7 +188,7 @@ T GetFlag(const absl::Flag<T>& flag) {
// 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_MUST_USE_RESULT 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
@@ -184,13 +276,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)
#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)
#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