Closed Bug 1597954 Opened 5 years ago Closed 5 years ago

Add a class template for objects that can only be initialized once

Categories

(Core :: MFBT, task)

task
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla75
Tracking Status
firefox75 --- fixed

People

(Reporter: sg, Assigned: sg)

Details

Attachments

(2 files)

When working on dom/indexedDB, a class template that is initializable only once has shown to be useful to avoid usage errors and reduce the number of state assertions. It is based on mozilla::Maybe and exposes a small subset of its interface.

I feel this might be useful for other code as well, so it might be moved to MFBT. I can do this and add appropriate tests if this makes sense.

My initial version looks like this:

// 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. Use with a const T to ensure
// that the contents cannot be modified either.
template <typename T, bool AllowLazy = false>
class InitializedOnce {
 public:
  template <typename Dummy = void>
  InitializedOnce(std::enable_if_t<AllowLazy, Dummy>* = nullptr) {}

  template <typename... Args>
  InitializedOnce(Args&&... aArgs)
      : mMaybe{Some(T{std::forward<Args>(aArgs)...})} {}

  InitializedOnce& operator=(const InitializedOnce&) = delete;
  InitializedOnce& operator=(InitializedOnce&&) = delete;

  template <typename... Args, typename Dummy = void>
  std::enable_if_t<AllowLazy, Dummy> init(Args&&... aArgs) {
    MOZ_ASSERT(mMaybe.isNothing());
    mMaybe.emplace(T{std::forward<Args>(aArgs)...});
  }

  explicit operator bool() const { return isSome(); }
  bool isSome() const { return mMaybe.isSome(); }
  bool isNothing() const { return mMaybe.isNothing(); }

  T& operator*() const { return *mMaybe; }
  T* operator->() const { return mMaybe.operator->(); }

  void reset() {
    MOZ_ASSERT(mMaybe.isSome());
    mMaybe.reset();
  }

 private:
  Maybe<T> mMaybe;
};

Why have AllowLazy = true if that just makes the class equivalent to Maybe with a slightly different spelling?

(In reply to Nathan Froyd [:froydnj] from comment #1)

Why have AllowLazy = true if that just makes the class equivalent to Maybe with a slightly different spelling?

With AllowLazy = true, you do not need to initialize it on construction, but it may still be initialized only once (either on construction or lazily with init). Maybe would allow to initialize it multiple times by reassigning or emplace.

What I missed until now is to disable the assignment operators.

(In reply to Simon Giesecke [:sg] [he/him] from comment #2)

(In reply to Nathan Froyd [:froydnj] from comment #1)

Why have AllowLazy = true if that just makes the class equivalent to Maybe with a slightly different spelling?

With AllowLazy = true, you do not need to initialize it on construction, but it may still be initialized only once (either on construction or lazily with init). Maybe would allow to initialize it multiple times by reassigning or emplace.

What I missed until now is to disable the assignment operators.

But you have reset, so you can just reset and then init again, no?

(In reply to Nathan Froyd [:froydnj] from comment #3)

(In reply to Simon Giesecke [:sg] [he/him] from comment #2)

(In reply to Nathan Froyd [:froydnj] from comment #1)

Why have AllowLazy = true if that just makes the class equivalent to Maybe with a slightly different spelling?

With AllowLazy = true, you do not need to initialize it on construction, but it may still be initialized only once (either on construction or lazily with init). Maybe would allow to initialize it multiple times by reassigning or emplace.

What I missed until now is to disable the assignment operators.

But you have reset, so you can just reset and then init again, no?

Mh, yes, that's true. Another flag would probably be needed to prohibit such a usage sequence as well.

FYI, this is the patch which ought to add this locally to dom/indexedDB and also uses it at a few places: https://phabricator.services.mozilla.com/D53951

It has now landed and been extended a bit to allow checking for valid values at the time of initialization: https://searchfox.org/mozilla-central/source/dom/indexedDB/InitializedOnce.h

Status: NEW → ASSIGNED
Summary: Add a class template that can only be initialized once → Add a class template for objects that can only be initialized once
Attachment #9127274 - Attachment description: Bug 1597954 - Add a class template for objects can only be initialized once. r=froydnj → Bug 1597954 - Add a class template for objects that can only be initialized once. r=froydnj
Pushed by sgiesecke@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/711622b6327c Add a class template for objects that can only be initialized once. r=froydnj
Status: ASSIGNED → RESOLVED
Closed: 5 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla75
Pushed by sgiesecke@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/b31ddbcdd2f2 Switch indexedDB to use InitializedOnce from mfbt. r=dom-workers-and-storage-reviewers,janv
Pushed by sgiesecke@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/7a219404ebe4 Switch indexedDB to use InitializedOnce from mfbt. r=dom-workers-and-storage-reviewers,janv
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: