flag_msvc.inc 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  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. // Do not include this file directly.
  16. // Include absl/flags/flag.h instead.
  17. // MSVC debug builds do not implement initialization with constexpr constructors
  18. // correctly. To work around this we add a level of indirection, so that the
  19. // class `absl::Flag` contains an `internal::Flag*` (instead of being an alias
  20. // to that class) and dynamically allocates an instance when necessary. We also
  21. // forward all calls to internal::Flag methods via trampoline methods. In this
  22. // setup the `absl::Flag` class does not have constructor and virtual methods,
  23. // all the data members are public and thus MSVC is able to initialize it at
  24. // link time. To deal with multiple threads accessing the flag for the first
  25. // time concurrently we use an atomic boolean indicating if flag object is
  26. // initialized. We also employ the double-checked locking pattern where the
  27. // second level of protection is a global Mutex, so if two threads attempt to
  28. // construct the flag concurrently only one wins.
  29. //
  30. // This solution is based on a recomendation here:
  31. // https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html?childToView=648454#comment-648454
  32. namespace flags_internal {
  33. absl::Mutex* GetGlobalConstructionGuard();
  34. } // namespace flags_internal
  35. // Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API.
  36. // See https://abseil.io/docs/cpp/guides/flags
  37. template <typename T>
  38. class Flag {
  39. public:
  40. // No constructor and destructor to ensure this is an aggregate type.
  41. // Visual Studio 2015 still requires the constructor for class to be
  42. // constexpr initializable.
  43. #if _MSC_VER <= 1900
  44. constexpr Flag(const char* name, const char* filename,
  45. const flags_internal::HelpGenFunc help_gen,
  46. const flags_internal::FlagDfltGenFunc default_value_gen)
  47. : name_(name),
  48. filename_(filename),
  49. help_gen_(help_gen),
  50. default_value_gen_(default_value_gen),
  51. inited_(false),
  52. impl_(nullptr) {}
  53. #endif
  54. flags_internal::Flag<T>& GetImpl() const {
  55. if (!inited_.load(std::memory_order_acquire)) {
  56. absl::MutexLock l(flags_internal::GetGlobalConstructionGuard());
  57. if (inited_.load(std::memory_order_acquire)) {
  58. return *impl_;
  59. }
  60. impl_ = new flags_internal::Flag<T>(
  61. name_, filename_,
  62. {flags_internal::FlagHelpMsg(help_gen_),
  63. flags_internal::FlagHelpKind::kGenFunc},
  64. {flags_internal::FlagDefaultSrc(default_value_gen_),
  65. flags_internal::FlagDefaultKind::kGenFunc});
  66. inited_.store(true, std::memory_order_release);
  67. }
  68. return *impl_;
  69. }
  70. // Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API.
  71. // See https://abseil.io/docs/cpp/guides/flags
  72. bool IsRetired() const { return GetImpl().IsRetired(); }
  73. absl::string_view Name() const { return GetImpl().Name(); }
  74. std::string Help() const { return GetImpl().Help(); }
  75. bool IsModified() const { return GetImpl().IsModified(); }
  76. bool IsSpecifiedOnCommandLine() const {
  77. return GetImpl().IsSpecifiedOnCommandLine();
  78. }
  79. std::string Filename() const { return GetImpl().Filename(); }
  80. std::string DefaultValue() const { return GetImpl().DefaultValue(); }
  81. std::string CurrentValue() const { return GetImpl().CurrentValue(); }
  82. template <typename U>
  83. inline bool IsOfType() const {
  84. return GetImpl().template IsOfType<U>();
  85. }
  86. T Get() const {
  87. return flags_internal::FlagImplPeer::InvokeGet<T>(GetImpl());
  88. }
  89. void Set(const T& v) {
  90. flags_internal::FlagImplPeer::InvokeSet(GetImpl(), v);
  91. }
  92. void InvokeCallback() { GetImpl().InvokeCallback(); }
  93. const CommandLineFlag& Reflect() const {
  94. return flags_internal::FlagImplPeer::InvokeReflect(GetImpl());
  95. }
  96. // The data members are logically private, but they need to be public for
  97. // this to be an aggregate type.
  98. const char* name_;
  99. const char* filename_;
  100. const flags_internal::HelpGenFunc help_gen_;
  101. const flags_internal::FlagDfltGenFunc default_value_gen_;
  102. mutable std::atomic<bool> inited_;
  103. mutable flags_internal::Flag<T>* impl_;
  104. };