diff --git a/mfbt/MaybeOneOf.h b/mfbt/MaybeOneOf.h new file mode 100644 index 000000000000..8917444c9697 --- /dev/null +++ b/mfbt/MaybeOneOf.h @@ -0,0 +1,131 @@ +/* -*- 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/. */ + +#ifndef mozilla_MaybeOneOf_h +#define mozilla_MaybeOneOf_h + +#include "mozilla/Alignment.h" +#include "mozilla/Assertions.h" +#include "mozilla/Move.h" +#include "mozilla/TemplateLib.h" + +// For placement new +#include + +namespace mozilla { + +/* + * MaybeOneOf is like Maybe, but it supports constructing either T1 + * or T2. When a MaybeOneOf is constructed, it is |empty()|, i.e., + * no value has been constructed and no destructor will be called when the + * MaybeOneOf is destroyed. Upon calling |construct()| or + * |construct()|, a T1 or T2 object will be constructed with the given + * arguments and that object will be destroyed when the owning MaybeOneOf is + * destroyed. + */ +template +class MaybeOneOf +{ + AlignedStorage::value> storage; + + enum State { None, SomeT1, SomeT2 } state; + template struct Type2State {}; + + template + T& as() { + MOZ_ASSERT(state == Type2State::result); + return *(T*)storage.addr(); + } + + template + const T& as() const { + MOZ_ASSERT(state == Type2State::result); + return *(T*)storage.addr(); + } + + public: + MaybeOneOf() : state(None) {} + ~MaybeOneOf() { destroyIfConstructed(); } + + bool empty() const { return state == None; } + + template + bool constructed() const { return state == Type2State::result; } + + template + void construct() { + MOZ_ASSERT(state == None); + state = Type2State::result; + ::new (storage.addr()) T(); + } + + template + void construct(U&& u) { + MOZ_ASSERT(state == None); + state = Type2State::result; + ::new (storage.addr()) T(Move(u)); + } + + template + void construct(const U1& u1) { + MOZ_ASSERT(state == None); + state = Type2State::result; + ::new (storage.addr()) T(u1); + } + + template + void construct(const U1& u1, const U2& u2) { + MOZ_ASSERT(state == None); + state = Type2State::result; + ::new (storage.addr()) T(u1, u2); + } + + template + T& ref() { + return as(); + } + + template + const T& ref() const { + return as(); + } + + void destroy() { + MOZ_ASSERT(state == SomeT1 || state == SomeT2); + if (state == SomeT1) + as().~T1(); + else if (state == SomeT2) + as().~T2(); + state = None; + } + + void destroyIfConstructed() { + if (!empty()) + destroy(); + } + + private: + MaybeOneOf(const MaybeOneOf& other) MOZ_DELETE; + const MaybeOneOf& operator=(const MaybeOneOf& other) MOZ_DELETE; +}; + +template +template +struct MaybeOneOf::Type2State { + typedef MaybeOneOf Enclosing; + static const typename Enclosing::State result = Enclosing::SomeT1; +}; + +template +template +struct MaybeOneOf::Type2State { + typedef MaybeOneOf Enclosing; + static const typename Enclosing::State result = Enclosing::SomeT2; +}; + +} // namespace mozilla + +#endif /* mozilla_MaybeOneOf_h */ diff --git a/mfbt/moz.build b/mfbt/moz.build index 7aeda130a66b..f3a8b393a4bb 100644 --- a/mfbt/moz.build +++ b/mfbt/moz.build @@ -41,6 +41,7 @@ EXPORTS.mozilla = [ 'MacroForEach.h', 'MathAlgorithms.h', 'Maybe.h', + 'MaybeOneOf.h', 'MemoryChecking.h', 'MemoryReporting.h', 'Move.h',