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
>>;
53 template <typename Dummy
= void>
54 explicit constexpr InitializedOnce(
55 std::enable_if_t
<InitWhenVal
== InitWhen::LazyAllowed
, Dummy
>* =
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
);
72 aOther
.mWasReset
= true;
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
);
82 new (&mMaybe
) MaybeType
{std::move(aOther
.mMaybe
)};
84 aOther
.mWasReset
= true;
89 template <typename
... Args
, typename Dummy
= void>
90 constexpr std::enable_if_t
<InitWhenVal
== InitWhen::LazyAllowed
, Dummy
> init(
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
>
110 MOZ_ASSERT(mMaybe
.isSome());
114 template <typename Dummy
= void>
115 std::enable_if_t
<DestroyWhenVal
== DestroyWhen::EarlyAllowed
, Dummy
>
123 template <typename Dummy
= T
>
124 std::enable_if_t
<DestroyWhenVal
== DestroyWhen::EarlyAllowed
, Dummy
>
126 MOZ_ASSERT(mMaybe
.isSome());
127 auto res
= std::move(mMaybe
.ref());
135 bool mWasReset
= false;
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
171 // There is no variant combining detail::DestroyWhen::InConstructorOnly with
172 // detail::DestroyWhen::InDestructorOnly because this would be equivalent to a
175 // For special cases, e.g. requiring custom value check policies,
176 // detail::InitializedOnce might be instantiated directly, but be mindful when
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