| // Copyright (C) 2020 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| #pragma once |
| #include "dctv.h" |
| |
| #include <assert.h> |
| |
| #include <algorithm> |
| #include <cstddef> |
| #include <tuple> |
| #include <type_traits> |
| #include <utility> |
| |
| namespace dctv { |
| |
| // Very simple switch-based variant-ish object, unlike the libstdc++ |
| // and c++ variant implementations, is simple enough that the compiler |
| // can optimize it. |
| // |
| // Unlike std::variant, we require that all contained types are |
| // nothrow-moveable and nothrow-constructible. We never construct in |
| // place, so we can never be valueless by exception. |
| |
| template<typename T> |
| struct TypeNotInSimpleVariant; |
| |
| template<std::size_t Index, typename Needle, typename... Haystack> |
| struct index_of_v_impl : TypeNotInSimpleVariant<Needle> {}; |
| |
| struct monostate {}; |
| |
| template<typename Needle, typename... Haystack> |
| static constexpr std::size_t index_of_v = |
| index_of_v_impl<0, Needle, Haystack...>::value; |
| |
| template<std::size_t N, typename enable = void> |
| struct ArmNumberBase; |
| |
| template<typename... T> |
| struct SimpleVariant : ArmNumberBase<sizeof...(T)> { |
| private: |
| using Base = ArmNumberBase<sizeof...(T)>; |
| using ArmsTuple = typename std::tuple<T...>; |
| template<std::size_t Index> |
| using ArmAtIndex = typename std::tuple_element_t<Index, ArmsTuple>; |
| using FirstType = ArmAtIndex<0>; |
| static constexpr bool can_default_construct = |
| std::is_default_constructible_v<FirstType>; |
| static constexpr bool is_nothrow_copy_constructible = |
| std::conjunction_v<std::is_nothrow_copy_constructible<T>...>; |
| static constexpr bool is_nothrow_copy_assignable = |
| std::conjunction_v<std::is_nothrow_copy_assignable<T>...>; |
| |
| public: |
| static_assert( |
| std::conjunction_v<std::is_nothrow_destructible<T>...>); |
| static_assert( |
| std::conjunction_v<std::is_nothrow_move_constructible<T>...>); |
| static_assert( |
| std::conjunction_v<std::is_nothrow_move_assignable<T>...>); |
| |
| template<typename R> inline SimpleVariant(R r) noexcept; // NOLINT |
| inline SimpleVariant(SimpleVariant&& other) noexcept; |
| inline SimpleVariant& operator=(SimpleVariant&& other) noexcept; |
| inline SimpleVariant(const SimpleVariant& other) |
| noexcept(is_nothrow_copy_constructible); |
| inline SimpleVariant& operator=(const SimpleVariant& other) |
| noexcept(is_nothrow_copy_assignable); |
| inline ~SimpleVariant() noexcept; |
| |
| inline bool operator==(const SimpleVariant& other) const; |
| inline bool operator!=(const SimpleVariant& other) const; |
| |
| template<typename Dummy = void> |
| inline SimpleVariant( // NOLINT |
| typename std::enable_if_t<can_default_construct, Dummy>* = 0) |
| : SimpleVariant(make_default_type()) |
| {} |
| |
| template<typename Functor> |
| inline auto visit(Functor&& functor) &; |
| |
| template<typename Functor> |
| inline auto visit(Functor&& functor) &&; |
| |
| template<typename Functor> |
| inline auto visit(Functor&& functor) const &; |
| |
| template<typename Functor> |
| inline auto visit(Functor&& functor) const &&; |
| |
| template<typename R> inline R* get_if() noexcept; |
| template<typename R> inline const R* get_if() const noexcept; |
| |
| private: |
| static constexpr std::size_t needed_size = std::max({sizeof (T)...}); |
| static constexpr std::size_t needed_alignment = std::max({alignof(T)...}); |
| using Buf = std::aligned_storage_t<needed_size, needed_alignment>; |
| |
| template<std::size_t Index, |
| typename R, |
| typename This, |
| typename Functor> |
| inline static R visit_impl(This&& this_, Functor&& functor); |
| |
| static inline FirstType make_default_type(); |
| |
| void destroy() noexcept; |
| |
| Buf storage; |
| }; |
| |
| // Do signature checking, but only in debug builds. The virtual-ness |
| // doesn't affect the actual called code because VARIANT_FORWARD |
| // always does a non-virtual call to a concrete function, even in |
| // debug builds. |
| #if !defined(NDEBUG) |
| # define VARIANT_WHEN_DEBUG(x) x |
| #else |
| # define VARIANT_WHEN_DEBUG(x) |
| #endif |
| |
| #define VARIANT_VIRTUAL VARIANT_WHEN_DEBUG(virtual) |
| #define VARIANT_OVERRIDE VARIANT_WHEN_DEBUG(override) |
| #define VARIANT_ABSTRACT VARIANT_WHEN_DEBUG(= 0) |
| |
| #define VARIANT_FORWARD(name) \ |
| template<typename... Args> \ |
| inline auto name(Args&&... args) { \ |
| return this->visit([&](auto& value) { \ |
| using Variant = std::decay_t<decltype(value)>; \ |
| VARIANT_WHEN_DEBUG( \ |
| static_assert(std::is_base_of_v<variant_base, Variant>)); \ |
| return value.Variant::name(std::forward<Args>(args)...); \ |
| }); \ |
| } \ |
| template<typename... Args> \ |
| inline auto name(Args&&... args) const { \ |
| return this->visit([&](auto& value) { \ |
| using Variant = std::decay_t<decltype(value)>; \ |
| VARIANT_WHEN_DEBUG( \ |
| static_assert(std::is_base_of_v<variant_base, Variant>)); \ |
| return value.Variant::name(std::forward<Args>(args)...); \ |
| }); \ |
| } |
| |
| void test_simple_variant(); |
| |
| } // namespace dctv |
| |
| #include "simple_variant-inl.h" |