diff --git a/mfbt/Variant.h b/mfbt/Variant.h index b74fe04e66c3..d7a65d6b87b1 100644 --- a/mfbt/Variant.h +++ b/mfbt/Variant.h @@ -295,6 +295,12 @@ struct AsVariantTemporary } // namespace detail +// Used to unambiguously specify one of the Variant's type. +template struct VariantType { using Type = T; }; + +// Used to specify one of the Variant's type by index. +template struct VariantIndex { static constexpr size_t index = N; }; + /** * # mozilla::Variant * @@ -315,19 +321,33 @@ struct AsVariantTemporary * * Variant v1('a'); * Variant, B, C> v2(MakeUnique()); + * Variant v3(VariantType, 0); // disambiguation needed + * Variant v4(VariantIndex<1>, 0); // 2nd int * * Because specifying the full type of a Variant value is often verbose, - * AsVariant() can be used to construct a Variant value using type inference in - * contexts such as expressions or when returning values from functions. Because - * AsVariant() must copy or move the value into a temporary and this cannot - * necessarily be elided by the compiler, it's mostly appropriate only for use - * with primitive or very small types. + * there are two easier ways to construct values: * + * A. AsVariant() can be used to construct a Variant value using type inference + * in contexts such as expressions or when returning values from functions. + * Because AsVariant() must copy or move the value into a temporary and this + * cannot necessarily be elided by the compiler, it's mostly appropriate only + * for use with primitive or very small types. * * Variant Foo() { return AsVariant('x'); } * // ... * Variant v1 = Foo(); // v1 holds char('x'). * + * B. Brace-construction with VariantType or VariantIndex; this also allows + * in-place construction with any number of arguments. + * + * struct AB { AB(int, int){...} }; + * static Variant foo() + * { + * return {VariantIndex<0>{}, 1, 2}; + * } + * // ... + * Variant v0 = Foo(); // v0 holds AB(1,2). + * * All access to the contained value goes through type-safe accessors. * Either the stored type, or the type index may be provided. * @@ -355,11 +375,13 @@ struct AsVariantTemporary * struct ResultOrError * { * Variant m; + * ResultOrError() : m(int(0)) {} // Error '0' by default + * ResultOrError(const T& r) : m(r) {} * bool IsResult() const { return m.is(); } * bool IsError() const { return m.is(); } * }; * // Now instantiante with the result being an int too: - * ResultOrError myResult; // Fail! + * ResultOrError myResult(123); // Fail! * // In Variant, which 'int' are we refering to, from inside * // ResultOrError functions? * @@ -368,11 +390,13 @@ struct AsVariantTemporary * struct ResultOrError * { * Variant m; + * ResultOrError() : m(VariantIndex<1>{}, 0) {} // Error '0' by default + * ResultOrError(const T& r) : m(VariantIndex<0>{}, r) {} * bool IsResult() const { return m.is<0>(); } // 0 -> T * bool IsError() const { return m.is<1>(); } // 1 -> int * }; * // Now instantiante with the result being an int too: - * ResultOrError myResult; // It now works! + * ResultOrError myResult(123); // It now works! * * Attempting to use the contained value as type `T1` when the `Variant` * instance contains a value of type `T2` causes an assertion failure. @@ -501,16 +525,44 @@ public: ::new (KnownNotNull, ptr()) T(Forward(aT)); } + /** + * Perfect forwarding construction for some variant type T, by + * explicitly giving the type. + * This is necessary to construct from any number of arguments, + * or to convert from a type that is not in the Variant's type list. + */ + template + MOZ_IMPLICIT Variant(const VariantType&, Args&&... aTs) + : tag(Impl::template tag()) + { + ::new (KnownNotNull, ptr()) T(Forward(aTs)...); + } + + /** + * Perfect forwarding construction for some variant type T, by + * explicitly giving the type index. + * This is necessary to construct from any number of arguments, + * or to convert from a type that is not in the Variant's type list, + * or to construct a type that is present more than once in the Variant. + */ + template + MOZ_IMPLICIT Variant(const VariantIndex&, Args&&... aTs) + : tag(N) + { + using T = typename detail::Nth::Type; + ::new (KnownNotNull, ptr()) T(Forward(aTs)...); + } + /** * Constructs this Variant from an AsVariantTemporary such that T can be * stored in one of the types allowable in this Variant. This is used in the * implementation of AsVariant(). */ - template::Type> + template MOZ_IMPLICIT Variant(detail::AsVariantTemporary&& aValue) - : tag(Impl::template tag()) + : tag(Impl::template tag::Type>()) { + using T = typename detail::SelectVariantType::Type; static_assert(detail::SelectVariantType::count == 1, "Variant can only be selected by type if that type is unique"); ::new (KnownNotNull, ptr()) T(Move(aValue.mValue)); diff --git a/mfbt/tests/TestVariant.cpp b/mfbt/tests/TestVariant.cpp index e3da3fd469f0..0f84c3ed56bf 100644 --- a/mfbt/tests/TestVariant.cpp +++ b/mfbt/tests/TestVariant.cpp @@ -58,6 +58,29 @@ testDuplicate() MOZ_RELEASE_ASSERT(v.extract<1>() == 1); } +static void +testConstructionWithVariantType() +{ + Variant v(mozilla::VariantType{}, 3); + MOZ_RELEASE_ASSERT(v.is()); + //MOZ_RELEASE_ASSERT(!v.is()); // uint32_t is not unique! + MOZ_RELEASE_ASSERT(v.as() == 3); +} + +static void +testConstructionWithVariantIndex() +{ + Variant v(mozilla::VariantIndex<2>{}, 2); + MOZ_RELEASE_ASSERT(!v.is()); + // Note: uint32_t is not unique, so `v.is()` is not allowed. + + MOZ_RELEASE_ASSERT(!v.is<1>()); + MOZ_RELEASE_ASSERT(!v.is<0>()); + MOZ_RELEASE_ASSERT(v.is<2>()); + MOZ_RELEASE_ASSERT(v.as<2>() == 2); + MOZ_RELEASE_ASSERT(v.extract<2>() == 2); +} + static void testCopy() { @@ -205,6 +228,8 @@ main() { testSimple(); testDuplicate(); + testConstructionWithVariantType(); + testConstructionWithVariantIndex(); testCopy(); testMove(); testDestructor();