1// 2// Copyright 2021 The Abseil Authors. 3// 4// Licensed under the Apache License, Version 2.0 (the "License"); 5// you may not use this file except in compliance with the License. 6// You may obtain a copy of the License at 7// 8// https://www.apache.org/licenses/LICENSE-2.0 9// 10// Unless required by applicable law or agreed to in writing, software 11// distributed under the License is distributed on an "AS IS" BASIS, 12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13// See the License for the specific language governing permissions and 14// limitations under the License. 15 16// Do not include this file directly. 17// Include absl/flags/flag.h instead. 18 19// MSVC debug builds do not implement initialization with constexpr constructors 20// correctly. To work around this we add a level of indirection, so that the 21// class `absl::Flag` contains an `internal::Flag*` (instead of being an alias 22// to that class) and dynamically allocates an instance when necessary. We also 23// forward all calls to internal::Flag methods via trampoline methods. In this 24// setup the `absl::Flag` class does not have constructor and virtual methods, 25// all the data members are public and thus MSVC is able to initialize it at 26// link time. To deal with multiple threads accessing the flag for the first 27// time concurrently we use an atomic boolean indicating if flag object is 28// initialized. We also employ the double-checked locking pattern where the 29// second level of protection is a global Mutex, so if two threads attempt to 30// construct the flag concurrently only one wins. 31// 32// This solution is based on a recomendation here: 33// https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html?childToView=648454#comment-648454 34 35namespace flags_internal { 36absl::Mutex* GetGlobalConstructionGuard(); 37} // namespace flags_internal 38 39// Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API. 40// See https://abseil.io/docs/cpp/guides/flags 41template <typename T> 42class Flag { 43 public: 44 // No constructor and destructor to ensure this is an aggregate type. 45 // Visual Studio 2015 still requires the constructor for class to be 46 // constexpr initializable. 47#if _MSC_VER <= 1900 48 constexpr Flag(const char* name, const char* filename, 49 const flags_internal::HelpGenFunc help_gen, 50 const flags_internal::FlagDfltGenFunc default_value_gen) 51 : name_(name), 52 filename_(filename), 53 help_gen_(help_gen), 54 default_value_gen_(default_value_gen), 55 inited_(false), 56 impl_(nullptr) {} 57#endif 58 59 flags_internal::Flag<T>& GetImpl() const { 60 if (!inited_.load(std::memory_order_acquire)) { 61 absl::MutexLock l(flags_internal::GetGlobalConstructionGuard()); 62 63 if (inited_.load(std::memory_order_acquire)) { 64 return *impl_; 65 } 66 67 impl_ = new flags_internal::Flag<T>( 68 name_, filename_, 69 {flags_internal::FlagHelpMsg(help_gen_), 70 flags_internal::FlagHelpKind::kGenFunc}, 71 {flags_internal::FlagDefaultSrc(default_value_gen_), 72 flags_internal::FlagDefaultKind::kGenFunc}); 73 inited_.store(true, std::memory_order_release); 74 } 75 76 return *impl_; 77 } 78 79 // Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API. 80 // See https://abseil.io/docs/cpp/guides/flags 81 bool IsRetired() const { return GetImpl().IsRetired(); } 82 absl::string_view Name() const { return GetImpl().Name(); } 83 std::string Help() const { return GetImpl().Help(); } 84 bool IsModified() const { return GetImpl().IsModified(); } 85 bool IsSpecifiedOnCommandLine() const { 86 return GetImpl().IsSpecifiedOnCommandLine(); 87 } 88 std::string Filename() const { return GetImpl().Filename(); } 89 std::string DefaultValue() const { return GetImpl().DefaultValue(); } 90 std::string CurrentValue() const { return GetImpl().CurrentValue(); } 91 template <typename U> 92 inline bool IsOfType() const { 93 return GetImpl().template IsOfType<U>(); 94 } 95 T Get() const { 96 return flags_internal::FlagImplPeer::InvokeGet<T>(GetImpl()); 97 } 98 void Set(const T& v) { 99 flags_internal::FlagImplPeer::InvokeSet(GetImpl(), v); 100 } 101 void InvokeCallback() { GetImpl().InvokeCallback(); } 102 103 const CommandLineFlag& Reflect() const { 104 return flags_internal::FlagImplPeer::InvokeReflect(GetImpl()); 105 } 106 107 // The data members are logically private, but they need to be public for 108 // this to be an aggregate type. 109 const char* name_; 110 const char* filename_; 111 const flags_internal::HelpGenFunc help_gen_; 112 const flags_internal::FlagDfltGenFunc default_value_gen_; 113 114 mutable std::atomic<bool> inited_; 115 mutable flags_internal::Flag<T>* impl_; 116}; 117