Bug 1649121: part 58) Change `DetermineLastNode` to return the found node. r=masayuki
[gecko.git] / mfbt / InitializedOnce.h
blob83b19232c45ca1c14568b018f2759b8f8cf92520
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/. */
7 // Class template for objects that can only be initialized once.
9 #ifndef mozilla_mfbt_initializedonce_h__
10 #define mozilla_mfbt_initializedonce_h__
12 #include "mozilla/Assertions.h"
13 #include "mozilla/Maybe.h"
15 #include <type_traits>
17 namespace mozilla {
19 namespace detail {
21 enum struct InitWhen { InConstructorOnly, LazyAllowed };
22 enum struct DestroyWhen { EarlyAllowed, InDestructorOnly };
24 namespace ValueCheckPolicies {
25 template <typename T>
26 struct AllowAnyValue {
27 constexpr static bool Check(const T& /*aValue*/) { return true; }
30 template <typename T>
31 struct ConvertsToTrue {
32 constexpr static bool Check(const T& aValue) {
33 return static_cast<bool>(aValue);
36 } // namespace ValueCheckPolicies
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>>;
52 public:
53 template <typename Dummy = void>
54 explicit constexpr InitializedOnce(
55 std::enable_if_t<InitWhenVal == InitWhen::LazyAllowed, Dummy>* =
56 nullptr) {}
58 // note: aArg0 is named separately here to disallow calling this with no
59 // arguments. The default constructor should only be available conditionally
60 // and is declared above.
61 template <typename Arg0, typename... Args>
62 explicit constexpr InitializedOnce(Arg0&& aArg0, Args&&... aArgs)
63 : mMaybe{Some(std::remove_const_t<T>{std::forward<Arg0>(aArg0),
64 std::forward<Args>(aArgs)...})} {
65 MOZ_ASSERT(ValueCheckPolicy<T>::Check(*mMaybe));
68 InitializedOnce(const InitializedOnce&) = delete;
69 InitializedOnce(InitializedOnce&& aOther) : mMaybe{std::move(aOther.mMaybe)} {
70 static_assert(DestroyWhenVal == DestroyWhen::EarlyAllowed);
71 #ifdef DEBUG
72 aOther.mWasReset = true;
73 #endif
75 InitializedOnce& operator=(const InitializedOnce&) = delete;
76 InitializedOnce& operator=(InitializedOnce&& aOther) {
77 static_assert(InitWhenVal == InitWhen::LazyAllowed &&
78 DestroyWhenVal == DestroyWhen::EarlyAllowed);
79 MOZ_ASSERT(!mWasReset);
80 MOZ_ASSERT(!mMaybe);
81 mMaybe.~MaybeType();
82 new (&mMaybe) MaybeType{std::move(aOther.mMaybe)};
83 #ifdef DEBUG
84 aOther.mWasReset = true;
85 #endif
86 return *this;
89 template <typename... Args, typename Dummy = void>
90 constexpr std::enable_if_t<InitWhenVal == InitWhen::LazyAllowed, Dummy> init(
91 Args&&... aArgs) {
92 MOZ_ASSERT(mMaybe.isNothing());
93 MOZ_ASSERT(!mWasReset);
94 mMaybe.emplace(std::remove_const_t<T>{std::forward<Args>(aArgs)...});
95 MOZ_ASSERT(ValueCheckPolicy<T>::Check(*mMaybe));
98 constexpr explicit operator bool() const { return isSome(); }
99 constexpr bool isSome() const { return mMaybe.isSome(); }
100 constexpr bool isNothing() const { return mMaybe.isNothing(); }
102 constexpr T& operator*() const { return *mMaybe; }
103 constexpr T* operator->() const { return mMaybe.operator->(); }
105 constexpr T& ref() const { return mMaybe.ref(); }
107 template <typename Dummy = void>
108 std::enable_if_t<DestroyWhenVal == DestroyWhen::EarlyAllowed, Dummy>
109 destroy() {
110 MOZ_ASSERT(mMaybe.isSome());
111 maybeDestroy();
114 template <typename Dummy = void>
115 std::enable_if_t<DestroyWhenVal == DestroyWhen::EarlyAllowed, Dummy>
116 maybeDestroy() {
117 mMaybe.reset();
118 #ifdef DEBUG
119 mWasReset = true;
120 #endif
123 template <typename Dummy = T>
124 std::enable_if_t<DestroyWhenVal == DestroyWhen::EarlyAllowed, Dummy>
125 release() {
126 MOZ_ASSERT(mMaybe.isSome());
127 auto res = std::move(mMaybe.ref());
128 destroy();
129 return res;
132 private:
133 MaybeType mMaybe;
134 #ifdef DEBUG
135 bool mWasReset = false;
136 #endif
138 } // namespace detail
140 // The following *InitializedOnce* template aliases allow to declare class
141 // member variables that can only be initialized once, but maybe destroyed
142 // earlier explicitly than in the containing classes destructor.
143 // The intention is to restrict the possible state transitions for member
144 // variables that can almost be const, but not quite. This may be particularly
145 // useful for classes with a lot of members. Uses in other contexts, e.g. as
146 // local variables, are possible, but probably seldom useful. They can only be
147 // instantiated with a const element type. Any misuses that cannot be detected
148 // at compile time trigger a MOZ_ASSERT at runtime. Individually spelled out
149 // assertions for these aspects are not necessary, which may improve the
150 // readability of the code without impairing safety.
152 // The base variant InitializedOnce requires initialization in the constructor,
153 // but allows early destruction using destroy(), and allow move construction. It
154 // is similar to Maybe<const T> in some sense, but a Maybe<const T> could be
155 // reinitialized arbitrarily. InitializedOnce expresses the intent not to do
156 // this, and prohibits reinitialization.
158 // The Lazy* variants allow default construction, and can be initialized lazily
159 // using init() in that case, but it cannot be reinitialized either. They do not
160 // allow early destruction.
162 // The Lazy*EarlyDestructible variants allow lazy initialization, early
163 // destruction, move construction and move assignment. This should be used only
164 // when really required.
166 // The *NotNull variants only allow initialization with values that convert to
167 // bool as true. They are named NotNull because the typical use case is with
168 // (smart) pointer types, but any other type convertible to bool will also work
169 // analogously.
171 // There is no variant combining detail::DestroyWhen::InConstructorOnly with
172 // detail::DestroyWhen::InDestructorOnly because this would be equivalent to a
173 // const member.
175 // For special cases, e.g. requiring custom value check policies,
176 // detail::InitializedOnce might be instantiated directly, but be mindful when
177 // doing this.
179 template <typename T>
180 using InitializedOnce =
181 detail::InitializedOnce<T, detail::InitWhen::InConstructorOnly,
182 detail::DestroyWhen::EarlyAllowed>;
184 template <typename T>
185 using InitializedOnceNotNull =
186 detail::InitializedOnce<T, detail::InitWhen::InConstructorOnly,
187 detail::DestroyWhen::EarlyAllowed,
188 detail::ValueCheckPolicies::ConvertsToTrue>;
190 template <typename T>
191 using LazyInitializedOnce =
192 detail::InitializedOnce<T, detail::InitWhen::LazyAllowed,
193 detail::DestroyWhen::InDestructorOnly>;
195 template <typename T>
196 using LazyInitializedOnceNotNull =
197 detail::InitializedOnce<T, detail::InitWhen::LazyAllowed,
198 detail::DestroyWhen::InDestructorOnly,
199 detail::ValueCheckPolicies::ConvertsToTrue>;
201 template <typename T>
202 using LazyInitializedOnceEarlyDestructible =
203 detail::InitializedOnce<T, detail::InitWhen::LazyAllowed,
204 detail::DestroyWhen::EarlyAllowed>;
206 template <typename T>
207 using LazyInitializedOnceNotNullEarlyDestructible =
208 detail::InitializedOnce<T, detail::InitWhen::LazyAllowed,
209 detail::DestroyWhen::EarlyAllowed,
210 detail::ValueCheckPolicies::ConvertsToTrue>;
212 } // namespace mozilla
214 #endif