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>
21 enum struct InitWhen
{ InConstructorOnly
, LazyAllowed
};
22 enum struct DestroyWhen
{ EarlyAllowed
, InDestructorOnly
};
24 namespace ValueCheckPolicies
{
26 struct AllowAnyValue
{
27 constexpr static bool Check(const T
& /*aValue*/) { return true; }
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
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
>>;
55 template <typename Dummy
= void>
56 explicit constexpr InitializedOnce(
57 std::enable_if_t
<InitWhenVal
== InitWhen::LazyAllowed
, Dummy
>* =
60 // note: aArg0 is named separately here to disallow calling this with no
61 // arguments. The default constructor should only be available conditionally
62 // and is declared above.
63 template <typename Arg0
, typename
... Args
>
64 explicit constexpr InitializedOnce(Arg0
&& aArg0
, Args
&&... aArgs
)
65 : mMaybe
{Some(std::remove_const_t
<T
>{std::forward
<Arg0
>(aArg0
),
66 std::forward
<Args
>(aArgs
)...})} {
67 MOZ_ASSERT(ValueCheckPolicy
<T
>::Check(*mMaybe
));
70 InitializedOnce(const InitializedOnce
&) = delete;
71 InitializedOnce(InitializedOnce
&& aOther
) : mMaybe
{std::move(aOther
.mMaybe
)} {
72 static_assert(DestroyWhenVal
== DestroyWhen::EarlyAllowed
);
74 aOther
.mWasReset
= true;
77 InitializedOnce
& operator=(const InitializedOnce
&) = delete;
78 InitializedOnce
& operator=(InitializedOnce
&& aOther
) {
79 static_assert(InitWhenVal
== InitWhen::LazyAllowed
&&
80 DestroyWhenVal
== DestroyWhen::EarlyAllowed
);
81 MOZ_ASSERT(!mWasReset
);
84 new (&mMaybe
) MaybeType
{std::move(aOther
.mMaybe
)};
86 aOther
.mWasReset
= true;
91 template <typename
... Args
, typename Dummy
= void>
92 constexpr std::enable_if_t
<InitWhenVal
== InitWhen::LazyAllowed
, Dummy
> init(
94 MOZ_ASSERT(mMaybe
.isNothing());
95 MOZ_ASSERT(!mWasReset
);
96 mMaybe
.emplace(std::remove_const_t
<T
>{std::forward
<Args
>(aArgs
)...});
97 MOZ_ASSERT(ValueCheckPolicy
<T
>::Check(*mMaybe
));
100 constexpr explicit operator bool() const { return isSome(); }
101 constexpr bool isSome() const { return mMaybe
.isSome(); }
102 constexpr bool isNothing() const { return mMaybe
.isNothing(); }
104 constexpr T
& operator*() const { return *mMaybe
; }
105 constexpr T
* operator->() const { return mMaybe
.operator->(); }
107 constexpr T
& ref() const { return mMaybe
.ref(); }
109 template <typename Dummy
= void>
110 std::enable_if_t
<DestroyWhenVal
== DestroyWhen::EarlyAllowed
, Dummy
>
112 MOZ_ASSERT(mMaybe
.isSome());
116 template <typename Dummy
= void>
117 std::enable_if_t
<DestroyWhenVal
== DestroyWhen::EarlyAllowed
, Dummy
>
125 template <typename Dummy
= T
>
126 std::enable_if_t
<DestroyWhenVal
== DestroyWhen::EarlyAllowed
, Dummy
>
128 MOZ_ASSERT(mMaybe
.isSome());
129 auto res
= std::move(mMaybe
.ref());
137 bool mWasReset
= false;
141 template <typename T
, InitWhen InitWhenVal
, DestroyWhen DestroyWhenVal
,
142 template <typename
> class ValueCheckPolicy
>
143 class LazyInitializer
{
145 explicit LazyInitializer(InitializedOnce
<T
, InitWhenVal
, DestroyWhenVal
,
146 ValueCheckPolicy
>& aLazyInitialized
)
147 : mLazyInitialized
{aLazyInitialized
} {}
149 template <typename U
>
150 LazyInitializer
& operator=(U
&& aValue
) {
151 mLazyInitialized
.init(std::forward
<U
>(aValue
));
155 LazyInitializer(const LazyInitializer
&) = delete;
156 LazyInitializer
& operator=(const LazyInitializer
&) = delete;
159 InitializedOnce
<T
, InitWhenVal
, DestroyWhenVal
, ValueCheckPolicy
>&
163 } // namespace detail
165 // The following *InitializedOnce* template aliases allow to declare class
166 // member variables that can only be initialized once, but maybe destroyed
167 // earlier explicitly than in the containing classes destructor.
168 // The intention is to restrict the possible state transitions for member
169 // variables that can almost be const, but not quite. This may be particularly
170 // useful for classes with a lot of members. Uses in other contexts, e.g. as
171 // local variables, are possible, but probably seldom useful. They can only be
172 // instantiated with a const element type. Any misuses that cannot be detected
173 // at compile time trigger a MOZ_ASSERT at runtime. Individually spelled out
174 // assertions for these aspects are not necessary, which may improve the
175 // readability of the code without impairing safety.
177 // The base variant InitializedOnce requires initialization in the constructor,
178 // but allows early destruction using destroy(), and allow move construction. It
179 // is similar to Maybe<const T> in some sense, but a Maybe<const T> could be
180 // reinitialized arbitrarily. InitializedOnce expresses the intent not to do
181 // this, and prohibits reinitialization.
183 // The Lazy* variants allow default construction, and can be initialized lazily
184 // using init() in that case, but it cannot be reinitialized either. They do not
185 // allow early destruction.
187 // The Lazy*EarlyDestructible variants allow lazy initialization, early
188 // destruction, move construction and move assignment. This should be used only
189 // when really required.
191 // The *NotNull variants only allow initialization with values that convert to
192 // bool as true. They are named NotNull because the typical use case is with
193 // (smart) pointer types, but any other type convertible to bool will also work
196 // There is no variant combining detail::DestroyWhen::InConstructorOnly with
197 // detail::DestroyWhen::InDestructorOnly because this would be equivalent to a
200 // For special cases, e.g. requiring custom value check policies,
201 // detail::InitializedOnce might be instantiated directly, but be mindful when
204 template <typename T
>
205 using InitializedOnce
=
206 detail::InitializedOnce
<T
, detail::InitWhen::InConstructorOnly
,
207 detail::DestroyWhen::EarlyAllowed
>;
209 template <typename T
>
210 using InitializedOnceNotNull
=
211 detail::InitializedOnce
<T
, detail::InitWhen::InConstructorOnly
,
212 detail::DestroyWhen::EarlyAllowed
,
213 detail::ValueCheckPolicies::ConvertsToTrue
>;
215 template <typename T
>
216 using LazyInitializedOnce
=
217 detail::InitializedOnce
<T
, detail::InitWhen::LazyAllowed
,
218 detail::DestroyWhen::InDestructorOnly
>;
220 template <typename T
>
221 using LazyInitializedOnceNotNull
=
222 detail::InitializedOnce
<T
, detail::InitWhen::LazyAllowed
,
223 detail::DestroyWhen::InDestructorOnly
,
224 detail::ValueCheckPolicies::ConvertsToTrue
>;
226 template <typename T
>
227 using LazyInitializedOnceEarlyDestructible
=
228 detail::InitializedOnce
<T
, detail::InitWhen::LazyAllowed
,
229 detail::DestroyWhen::EarlyAllowed
>;
231 template <typename T
>
232 using LazyInitializedOnceNotNullEarlyDestructible
=
233 detail::InitializedOnce
<T
, detail::InitWhen::LazyAllowed
,
234 detail::DestroyWhen::EarlyAllowed
,
235 detail::ValueCheckPolicies::ConvertsToTrue
>;
237 template <typename T
, detail::InitWhen InitWhenVal
,
238 detail::DestroyWhen DestroyWhenVal
,
239 template <typename
> class ValueCheckPolicy
>
240 auto do_Init(detail::InitializedOnce
<T
, InitWhenVal
, DestroyWhenVal
,
241 ValueCheckPolicy
>& aLazyInitialized
) {
242 return detail::LazyInitializer(aLazyInitialized
);
245 } // namespace mozilla