tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

InitializedOnce.h (9423B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 // Class template for objects that can only be initialized once.
      8 
      9 #ifndef mozilla_mfbt_initializedonce_h__
     10 #define mozilla_mfbt_initializedonce_h__
     11 
     12 #include "mozilla/Assertions.h"
     13 #include "mozilla/Maybe.h"
     14 
     15 #include <type_traits>
     16 
     17 namespace mozilla {
     18 
     19 namespace detail {
     20 
     21 enum struct InitWhen { InConstructorOnly, LazyAllowed };
     22 enum struct DestroyWhen { EarlyAllowed, InDestructorOnly };
     23 
     24 namespace ValueCheckPolicies {
     25 template <typename T>
     26 struct AllowAnyValue {
     27  constexpr static bool Check(const T& /*aValue*/) { return true; }
     28 };
     29 
     30 template <typename T>
     31 struct ConvertsToTrue {
     32  constexpr static bool Check(const T& aValue) {
     33    return static_cast<bool>(aValue);
     34  }
     35 };
     36 }  // namespace ValueCheckPolicies
     37 
     38 // A kind of mozilla::Maybe that can only be initialized and cleared once. It
     39 // cannot be re-initialized. This is a more stateful than a const Maybe<T> in
     40 // that it can be cleared, but much less stateful than a non-const Maybe<T>
     41 // which could be reinitialized multiple times. Can only be used with const T
     42 // to ensure that the contents cannot be modified either.
     43 // TODO: Make constructors constexpr when Maybe's constructors are constexpr
     44 // (Bug 1601336).
     45 template <typename T, InitWhen InitWhenVal, DestroyWhen DestroyWhenVal,
     46          template <typename> class ValueCheckPolicy =
     47              ValueCheckPolicies::AllowAnyValue>
     48 class InitializedOnce final {
     49  static_assert(std::is_const_v<T>);
     50  using MaybeType = Maybe<std::remove_const_t<T>>;
     51 
     52  template <typename Dummy>
     53  using requires_lazy_init_allowed =
     54      std::enable_if_t<InitWhenVal == InitWhen::LazyAllowed, Dummy>;
     55 
     56  template <typename Dummy>
     57  using requires_early_destroy_allowed =
     58      std::enable_if_t<DestroyWhenVal == DestroyWhen::EarlyAllowed, Dummy>;
     59 
     60 public:
     61  using ValueType = T;
     62 
     63  template <typename Dummy = void, typename = requires_lazy_init_allowed<Dummy>>
     64  explicit constexpr InitializedOnce() {}
     65 
     66  // note: aArg0 is named separately here to disallow calling this with no
     67  // arguments. The default constructor should only be available conditionally
     68  // and is declared above.
     69  template <typename Arg0, typename... Args>
     70  explicit constexpr InitializedOnce(Arg0&& aArg0, Args&&... aArgs)
     71      : mMaybe{Some(std::remove_const_t<T>{std::forward<Arg0>(aArg0),
     72                                           std::forward<Args>(aArgs)...})} {
     73    MOZ_ASSERT(ValueCheckPolicy<T>::Check(*mMaybe));
     74  }
     75 
     76  InitializedOnce(const InitializedOnce&) = delete;
     77  InitializedOnce(InitializedOnce&& aOther) : mMaybe{std::move(aOther.mMaybe)} {
     78    static_assert(DestroyWhenVal == DestroyWhen::EarlyAllowed);
     79 #ifdef DEBUG
     80    aOther.mWasReset = true;
     81 #endif
     82  }
     83  InitializedOnce& operator=(const InitializedOnce&) = delete;
     84  InitializedOnce& operator=(InitializedOnce&& aOther) {
     85    static_assert(InitWhenVal == InitWhen::LazyAllowed &&
     86                  DestroyWhenVal == DestroyWhen::EarlyAllowed);
     87    MOZ_ASSERT(!mWasReset);
     88    MOZ_ASSERT(!mMaybe);
     89    mMaybe.~MaybeType();
     90    new (&mMaybe) MaybeType{std::move(aOther.mMaybe)};
     91 #ifdef DEBUG
     92    aOther.mWasReset = true;
     93 #endif
     94    return *this;
     95  }
     96 
     97  template <typename... Args, typename Dummy = void,
     98            typename = requires_lazy_init_allowed<Dummy>>
     99  constexpr void init(Args&&... aArgs) {
    100    MOZ_ASSERT(mMaybe.isNothing());
    101    MOZ_ASSERT(!mWasReset);
    102    mMaybe.emplace(std::remove_const_t<T>{std::forward<Args>(aArgs)...});
    103    MOZ_ASSERT(ValueCheckPolicy<T>::Check(*mMaybe));
    104  }
    105 
    106  constexpr explicit operator bool() const { return isSome(); }
    107  constexpr bool isSome() const { return mMaybe.isSome(); }
    108  constexpr bool isNothing() const { return mMaybe.isNothing(); }
    109 
    110  constexpr T& operator*() const { return *mMaybe; }
    111  constexpr T* operator->() const { return mMaybe.operator->(); }
    112 
    113  constexpr T& ref() const { return mMaybe.ref(); }
    114 
    115  template <typename Dummy = void,
    116            typename = requires_early_destroy_allowed<Dummy>>
    117  void destroy() {
    118    MOZ_ASSERT(mMaybe.isSome());
    119    maybeDestroy();
    120  }
    121 
    122  template <typename Dummy = void,
    123            typename = requires_early_destroy_allowed<Dummy>>
    124  void maybeDestroy() {
    125    mMaybe.reset();
    126 #ifdef DEBUG
    127    mWasReset = true;
    128 #endif
    129  }
    130 
    131  template <typename Dummy = void,
    132            typename = requires_early_destroy_allowed<Dummy>>
    133  T release() {
    134    MOZ_ASSERT(mMaybe.isSome());
    135    auto res = std::move(mMaybe.ref());
    136    destroy();
    137    return res;
    138  }
    139 
    140 private:
    141  MaybeType mMaybe;
    142 #ifdef DEBUG
    143  bool mWasReset = false;
    144 #endif
    145 };
    146 
    147 template <typename T, InitWhen InitWhenVal, DestroyWhen DestroyWhenVal,
    148          template <typename> class ValueCheckPolicy>
    149 class LazyInitializer {
    150 public:
    151  explicit LazyInitializer(InitializedOnce<T, InitWhenVal, DestroyWhenVal,
    152                                           ValueCheckPolicy>& aLazyInitialized)
    153      : mLazyInitialized{aLazyInitialized} {}
    154 
    155  template <typename U>
    156  LazyInitializer& operator=(U&& aValue) {
    157    mLazyInitialized.init(std::forward<U>(aValue));
    158    return *this;
    159  }
    160 
    161  LazyInitializer(const LazyInitializer&) = delete;
    162  LazyInitializer& operator=(const LazyInitializer&) = delete;
    163 
    164 private:
    165  InitializedOnce<T, InitWhenVal, DestroyWhenVal, ValueCheckPolicy>&
    166      mLazyInitialized;
    167 };
    168 
    169 }  // namespace detail
    170 
    171 // The following *InitializedOnce* template aliases allow to declare class
    172 // member variables that can only be initialized once, but maybe destroyed
    173 // earlier explicitly than in the containing classes destructor.
    174 // The intention is to restrict the possible state transitions for member
    175 // variables that can almost be const, but not quite. This may be particularly
    176 // useful for classes with a lot of members. Uses in other contexts, e.g. as
    177 // local variables, are possible, but probably seldom useful. They can only be
    178 // instantiated with a const element type. Any misuses that cannot be detected
    179 // at compile time trigger a MOZ_ASSERT at runtime. Individually spelled out
    180 // assertions for these aspects are not necessary, which may improve the
    181 // readability of the code without impairing safety.
    182 //
    183 // The base variant InitializedOnce requires initialization in the constructor,
    184 // but allows early destruction using destroy(), and allow move construction. It
    185 // is similar to Maybe<const T> in some sense, but a Maybe<const T> could be
    186 // reinitialized arbitrarily. InitializedOnce expresses the intent not to do
    187 // this, and prohibits reinitialization.
    188 //
    189 // The Lazy* variants allow default construction, and can be initialized lazily
    190 // using init() in that case, but it cannot be reinitialized either. They do not
    191 // allow early destruction.
    192 //
    193 // The Lazy*EarlyDestructible variants allow lazy initialization, early
    194 // destruction, move construction and move assignment. This should be used only
    195 // when really required.
    196 //
    197 // The *NotNull variants only allow initialization with values that convert to
    198 // bool as true. They are named NotNull because the typical use case is with
    199 // (smart) pointer types, but any other type convertible to bool will also work
    200 // analogously.
    201 //
    202 // There is no variant combining detail::DestroyWhen::InConstructorOnly with
    203 // detail::DestroyWhen::InDestructorOnly because this would be equivalent to a
    204 // const member.
    205 //
    206 // For special cases, e.g. requiring custom value check policies,
    207 // detail::InitializedOnce might be instantiated directly, but be mindful when
    208 // doing this.
    209 
    210 template <typename T>
    211 using InitializedOnce =
    212    detail::InitializedOnce<T, detail::InitWhen::InConstructorOnly,
    213                            detail::DestroyWhen::EarlyAllowed>;
    214 
    215 template <typename T>
    216 using InitializedOnceNotNull =
    217    detail::InitializedOnce<T, detail::InitWhen::InConstructorOnly,
    218                            detail::DestroyWhen::EarlyAllowed,
    219                            detail::ValueCheckPolicies::ConvertsToTrue>;
    220 
    221 template <typename T>
    222 using LazyInitializedOnce =
    223    detail::InitializedOnce<T, detail::InitWhen::LazyAllowed,
    224                            detail::DestroyWhen::InDestructorOnly>;
    225 
    226 template <typename T>
    227 using LazyInitializedOnceNotNull =
    228    detail::InitializedOnce<T, detail::InitWhen::LazyAllowed,
    229                            detail::DestroyWhen::InDestructorOnly,
    230                            detail::ValueCheckPolicies::ConvertsToTrue>;
    231 
    232 template <typename T>
    233 using LazyInitializedOnceEarlyDestructible =
    234    detail::InitializedOnce<T, detail::InitWhen::LazyAllowed,
    235                            detail::DestroyWhen::EarlyAllowed>;
    236 
    237 template <typename T>
    238 using LazyInitializedOnceNotNullEarlyDestructible =
    239    detail::InitializedOnce<T, detail::InitWhen::LazyAllowed,
    240                            detail::DestroyWhen::EarlyAllowed,
    241                            detail::ValueCheckPolicies::ConvertsToTrue>;
    242 
    243 template <typename T, detail::InitWhen InitWhenVal,
    244          detail::DestroyWhen DestroyWhenVal,
    245          template <typename> class ValueCheckPolicy>
    246 auto do_Init(detail::InitializedOnce<T, InitWhenVal, DestroyWhenVal,
    247                                     ValueCheckPolicy>& aLazyInitialized) {
    248  return detail::LazyInitializer(aLazyInitialized);
    249 }
    250 
    251 }  // namespace mozilla
    252 
    253 #endif