summaryrefslogtreecommitdiffstats
path: root/mfbt/InitializedOnce.h
diff options
context:
space:
mode:
Diffstat (limited to 'mfbt/InitializedOnce.h')
-rw-r--r--mfbt/InitializedOnce.h247
1 files changed, 247 insertions, 0 deletions
diff --git a/mfbt/InitializedOnce.h b/mfbt/InitializedOnce.h
new file mode 100644
index 0000000000..aac152df35
--- /dev/null
+++ b/mfbt/InitializedOnce.h
@@ -0,0 +1,247 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Class template for objects that can only be initialized once.
+
+#ifndef mozilla_mfbt_initializedonce_h__
+#define mozilla_mfbt_initializedonce_h__
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Maybe.h"
+
+#include <type_traits>
+
+namespace mozilla {
+
+namespace detail {
+
+enum struct InitWhen { InConstructorOnly, LazyAllowed };
+enum struct DestroyWhen { EarlyAllowed, InDestructorOnly };
+
+namespace ValueCheckPolicies {
+template <typename T>
+struct AllowAnyValue {
+ constexpr static bool Check(const T& /*aValue*/) { return true; }
+};
+
+template <typename T>
+struct ConvertsToTrue {
+ constexpr static bool Check(const T& aValue) {
+ return static_cast<bool>(aValue);
+ }
+};
+} // namespace ValueCheckPolicies
+
+// A kind of mozilla::Maybe that can only be initialized and cleared once. It
+// cannot be re-initialized. This is a more stateful than a const Maybe<T> in
+// that it can be cleared, but much less stateful than a non-const Maybe<T>
+// which could be reinitialized multiple times. Can only be used with const T
+// to ensure that the contents cannot be modified either.
+// TODO: Make constructors constexpr when Maybe's constructors are constexpr
+// (Bug 1601336).
+template <typename T, InitWhen InitWhenVal, DestroyWhen DestroyWhenVal,
+ template <typename> class ValueCheckPolicy =
+ ValueCheckPolicies::AllowAnyValue>
+class InitializedOnce final {
+ static_assert(std::is_const_v<T>);
+ using MaybeType = Maybe<std::remove_const_t<T>>;
+
+ public:
+ using ValueType = T;
+
+ template <typename Dummy = void>
+ explicit constexpr InitializedOnce(
+ std::enable_if_t<InitWhenVal == InitWhen::LazyAllowed, Dummy>* =
+ nullptr) {}
+
+ // note: aArg0 is named separately here to disallow calling this with no
+ // arguments. The default constructor should only be available conditionally
+ // and is declared above.
+ template <typename Arg0, typename... Args>
+ explicit constexpr InitializedOnce(Arg0&& aArg0, Args&&... aArgs)
+ : mMaybe{Some(std::remove_const_t<T>{std::forward<Arg0>(aArg0),
+ std::forward<Args>(aArgs)...})} {
+ MOZ_ASSERT(ValueCheckPolicy<T>::Check(*mMaybe));
+ }
+
+ InitializedOnce(const InitializedOnce&) = delete;
+ InitializedOnce(InitializedOnce&& aOther) : mMaybe{std::move(aOther.mMaybe)} {
+ static_assert(DestroyWhenVal == DestroyWhen::EarlyAllowed);
+#ifdef DEBUG
+ aOther.mWasReset = true;
+#endif
+ }
+ InitializedOnce& operator=(const InitializedOnce&) = delete;
+ InitializedOnce& operator=(InitializedOnce&& aOther) {
+ static_assert(InitWhenVal == InitWhen::LazyAllowed &&
+ DestroyWhenVal == DestroyWhen::EarlyAllowed);
+ MOZ_ASSERT(!mWasReset);
+ MOZ_ASSERT(!mMaybe);
+ mMaybe.~MaybeType();
+ new (&mMaybe) MaybeType{std::move(aOther.mMaybe)};
+#ifdef DEBUG
+ aOther.mWasReset = true;
+#endif
+ return *this;
+ }
+
+ template <typename... Args, typename Dummy = void>
+ constexpr std::enable_if_t<InitWhenVal == InitWhen::LazyAllowed, Dummy> init(
+ Args&&... aArgs) {
+ MOZ_ASSERT(mMaybe.isNothing());
+ MOZ_ASSERT(!mWasReset);
+ mMaybe.emplace(std::remove_const_t<T>{std::forward<Args>(aArgs)...});
+ MOZ_ASSERT(ValueCheckPolicy<T>::Check(*mMaybe));
+ }
+
+ constexpr explicit operator bool() const { return isSome(); }
+ constexpr bool isSome() const { return mMaybe.isSome(); }
+ constexpr bool isNothing() const { return mMaybe.isNothing(); }
+
+ constexpr T& operator*() const { return *mMaybe; }
+ constexpr T* operator->() const { return mMaybe.operator->(); }
+
+ constexpr T& ref() const { return mMaybe.ref(); }
+
+ template <typename Dummy = void>
+ std::enable_if_t<DestroyWhenVal == DestroyWhen::EarlyAllowed, Dummy>
+ destroy() {
+ MOZ_ASSERT(mMaybe.isSome());
+ maybeDestroy();
+ }
+
+ template <typename Dummy = void>
+ std::enable_if_t<DestroyWhenVal == DestroyWhen::EarlyAllowed, Dummy>
+ maybeDestroy() {
+ mMaybe.reset();
+#ifdef DEBUG
+ mWasReset = true;
+#endif
+ }
+
+ template <typename Dummy = T>
+ std::enable_if_t<DestroyWhenVal == DestroyWhen::EarlyAllowed, Dummy>
+ release() {
+ MOZ_ASSERT(mMaybe.isSome());
+ auto res = std::move(mMaybe.ref());
+ destroy();
+ return res;
+ }
+
+ private:
+ MaybeType mMaybe;
+#ifdef DEBUG
+ bool mWasReset = false;
+#endif
+};
+
+template <typename T, InitWhen InitWhenVal, DestroyWhen DestroyWhenVal,
+ template <typename> class ValueCheckPolicy>
+class LazyInitializer {
+ public:
+ explicit LazyInitializer(InitializedOnce<T, InitWhenVal, DestroyWhenVal,
+ ValueCheckPolicy>& aLazyInitialized)
+ : mLazyInitialized{aLazyInitialized} {}
+
+ template <typename U>
+ LazyInitializer& operator=(U&& aValue) {
+ mLazyInitialized.init(std::forward<U>(aValue));
+ return *this;
+ }
+
+ LazyInitializer(const LazyInitializer&) = delete;
+ LazyInitializer& operator=(const LazyInitializer&) = delete;
+
+ private:
+ InitializedOnce<T, InitWhenVal, DestroyWhenVal, ValueCheckPolicy>&
+ mLazyInitialized;
+};
+
+} // namespace detail
+
+// The following *InitializedOnce* template aliases allow to declare class
+// member variables that can only be initialized once, but maybe destroyed
+// earlier explicitly than in the containing classes destructor.
+// The intention is to restrict the possible state transitions for member
+// variables that can almost be const, but not quite. This may be particularly
+// useful for classes with a lot of members. Uses in other contexts, e.g. as
+// local variables, are possible, but probably seldom useful. They can only be
+// instantiated with a const element type. Any misuses that cannot be detected
+// at compile time trigger a MOZ_ASSERT at runtime. Individually spelled out
+// assertions for these aspects are not necessary, which may improve the
+// readability of the code without impairing safety.
+//
+// The base variant InitializedOnce requires initialization in the constructor,
+// but allows early destruction using destroy(), and allow move construction. It
+// is similar to Maybe<const T> in some sense, but a Maybe<const T> could be
+// reinitialized arbitrarily. InitializedOnce expresses the intent not to do
+// this, and prohibits reinitialization.
+//
+// The Lazy* variants allow default construction, and can be initialized lazily
+// using init() in that case, but it cannot be reinitialized either. They do not
+// allow early destruction.
+//
+// The Lazy*EarlyDestructible variants allow lazy initialization, early
+// destruction, move construction and move assignment. This should be used only
+// when really required.
+//
+// The *NotNull variants only allow initialization with values that convert to
+// bool as true. They are named NotNull because the typical use case is with
+// (smart) pointer types, but any other type convertible to bool will also work
+// analogously.
+//
+// There is no variant combining detail::DestroyWhen::InConstructorOnly with
+// detail::DestroyWhen::InDestructorOnly because this would be equivalent to a
+// const member.
+//
+// For special cases, e.g. requiring custom value check policies,
+// detail::InitializedOnce might be instantiated directly, but be mindful when
+// doing this.
+
+template <typename T>
+using InitializedOnce =
+ detail::InitializedOnce<T, detail::InitWhen::InConstructorOnly,
+ detail::DestroyWhen::EarlyAllowed>;
+
+template <typename T>
+using InitializedOnceNotNull =
+ detail::InitializedOnce<T, detail::InitWhen::InConstructorOnly,
+ detail::DestroyWhen::EarlyAllowed,
+ detail::ValueCheckPolicies::ConvertsToTrue>;
+
+template <typename T>
+using LazyInitializedOnce =
+ detail::InitializedOnce<T, detail::InitWhen::LazyAllowed,
+ detail::DestroyWhen::InDestructorOnly>;
+
+template <typename T>
+using LazyInitializedOnceNotNull =
+ detail::InitializedOnce<T, detail::InitWhen::LazyAllowed,
+ detail::DestroyWhen::InDestructorOnly,
+ detail::ValueCheckPolicies::ConvertsToTrue>;
+
+template <typename T>
+using LazyInitializedOnceEarlyDestructible =
+ detail::InitializedOnce<T, detail::InitWhen::LazyAllowed,
+ detail::DestroyWhen::EarlyAllowed>;
+
+template <typename T>
+using LazyInitializedOnceNotNullEarlyDestructible =
+ detail::InitializedOnce<T, detail::InitWhen::LazyAllowed,
+ detail::DestroyWhen::EarlyAllowed,
+ detail::ValueCheckPolicies::ConvertsToTrue>;
+
+template <typename T, detail::InitWhen InitWhenVal,
+ detail::DestroyWhen DestroyWhenVal,
+ template <typename> class ValueCheckPolicy>
+auto do_Init(detail::InitializedOnce<T, InitWhenVal, DestroyWhenVal,
+ ValueCheckPolicy>& aLazyInitialized) {
+ return detail::LazyInitializer(aLazyInitialized);
+}
+
+} // namespace mozilla
+
+#endif