/* * 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/. * * Copyright 2018 Danny Robson */ #pragma once #include #include "../cast.hpp" #include "../maths.hpp" #include "../tuple/type.hpp" /////////////////////////////////////////////////////////////////////////////// namespace cruft { /// A tagged union of trivial types with a user-defined 'tag' type. /// /// This class supports far less functionality than does std::variant, but /// it simultaneously enforces far less complexity and allows for a known /// tag type. If you need something more flexible: /// don't extend this, just use std::variant. /// /// Storage is through a simple array of bytes (with suitable alignment). /// /// It should be impossible to construct an object in an uninitialised /// state. /// /// Requirements: /// * All member types must have a static constexpr 'tag' member of /// identical type that uniquely maps to a member type. /// /// \tparam ValueT A pack of possible member types to store. template class tagged { public: using tuple_t = std::tuple; using first_t = cruft::tuple::type::nth_t; using tag_t = std::decay_t; /// All child types should have identical tag types static_assert ((std::is_same_v> && ...)); /// All child types must be trivial, given we provide limited /// construction and destruction support. static_assert ((std::is_trivially_copyable_v && ...)); static_assert ((std::is_trivially_destructible_v && ...)); //--------------------------------------------------------------------- template tagged (InitialT &&initial) { set (std::forward (initial)); } ~tagged () = default; tagged (tagged const&) = default; tagged (tagged &&) = default; tagged& operator= (tagged const&) = default; tagged& operator= (tagged &&) = default; //--------------------------------------------------------------------- template NextT& operator= (NextT &&next) { return set (std::forward (next)); } ///-------------------------------------------------------------------- /// Return the type code associated with the stored value. auto tag (void) const { return m_tag; } auto tag (void) { return m_tag; } ///-------------------------------------------------------------------- /// Return a reference to a stored value of known type. /// /// Requesting a reference to an incorrect type has undefined /// behaviour, but trigger should an assertion if the build has been /// configured with them enabled. template InnerT& get (void)& { CHECK (InnerT::tag == m_tag); return *cruft::cast::alignment*> (m_data + 0); } template InnerT const& get (void) const& { CHECK (InnerT::tag == m_tag); return *cruft::cast::alignment const*> (m_data + 0); } /// Set the inner member to a supplied value and store the associated /// type code. template InnerT& set (InnerT &&inner)& { m_tag = inner.tag; return *cruft::cast::alignment*> (m_data + 0) = std::forward (inner); } private: /// The tag value for the currently active type tag_t m_tag; /// The storage area for all desired types. alignas (alignof (ValueT)...) std::byte m_data[ cruft::max (sizeof (ValueT)...) ]; }; }