# dlib C++ Library - type_safe_union_kernel_abstract.h

    // Copyright (C) 2009  Davis E. King (davis@dlib.net)
    // License: Boost Software License   See LICENSE.txt for the full license.
    #undef DLIB_TYPE_SAFE_UNION_KERNEl_ABSTRACT_
    #ifdef DLIB_TYPE_SAFE_UNION_KERNEl_ABSTRACT_

    namespace dlib
    {

    // ----------------------------------------------------------------------------------------

        class bad_type_safe_union_cast : public std::bad_cast
        {
            /*!
                This is the exception object thrown by type_safe_union::cast_to() if the
                type_safe_union does not contain the type of object being requested.
            !*/
        };

    // ----------------------------------------------------------------------------------------

        template<typename T>
        struct in_place_tag { using type = T; };
        /*!
            This is an empty class type used as a special disambiguation tag to be
            passed as the first argument to the constructor of type_safe_union that performs
            in-place construction of an object.

            Here is an example of its usage:

                struct A
                {
                    int i = 0;
                    int j = 0;

                    A(int i_, int j_) : i(i_), j(j_) {}
                };

                using tsu = type_safe_union<A,std::string>;
                tsu a(in_place_tag<A>{}, 0, 1); // a now contains an object of type A

            It is also used with type_safe_union::for_each() to disambiguate types.
        !*/

    // ----------------------------------------------------------------------------------------

        template<typename TSU>
        struct type_safe_union_size
        {
            static constexpr size_t value = The number of types in the TSU.
        };
        /*!
            requires
                - TSU must be of type type_safe_union<Types...> with possible cv qualification
            ensures
                - value contains the number of types in TSU, i.e. sizeof...(Types...)
        !*/

    // ----------------------------------------------------------------------------------------

        template <size_t I, typename TSU>
        struct type_safe_union_alternative;
        /*!
            requires
                - TSU is a type_safe_union
            ensures
                - type_safe_union_alternative<I, TSU>::type is the Ith type in the TSU.
                - TSU::get_type_id<typename type_safe_union_alternative<I, TSU>::type>() == I
        !*/

        template<size_t I, typename TSU>
        using type_safe_union_alternative_t = type_safe_union_alternative<I,TSU>::type;
        /*!
            ensures
                - provides template alias for type_safe_union_alternative
        !*/

    // ----------------------------------------------------------------------------------------

        template <typename... Types>
        class type_safe_union
        {
            /*!
                REQUIREMENTS ON ALL TEMPLATE ARGUMENTS
                    All template arguments must be default constructable and have
                    a global swap.

                INITIAL VALUE
                    - is_empty() == true
                    - contains<U>() == false, for all possible values of U

                WHAT THIS OBJECT REPRESENTS
                    This object is a type safe analogue of the classic C union object.
                    The type_safe_union, unlike a union, can contain non-POD types such
                    as std::string.

                    For example:
                        union my_union
                        {
                            int a;
                            std::string b;   // Error, std::string isn't a POD
                        };

                        type_safe_union<int,std::string> my_type_safe_union;  // No error
            !*/

        public:

            type_safe_union(
            ) = default;
            /*!
                ensures
                    - #is_empty() == true
            !*/

            type_safe_union (
                const type_safe_union& item
            )
            /*!
                ensures
                    - copy constructs *this from item
            !*/

            type_safe_union& operator=(
                const type_safe_union& item
            );
            /*!
                ensures
                    - copy assigns *this from item
            !*/

            type_safe_union (
                type_safe_union&& item
            );
            /*!
                ensures
                    - move constructs *this from item.
            !*/

            type_safe_union& operator= (
                type_safe_union&& item
            );
            /*!
                ensures
                    - move assigns *this from item.
            !*/

            template <
                typename T
            >
            type_safe_union (
                T&& item
            );
            /*!
                requires
                    - std::decay_t<T> must be one of the types given to this object's template arguments
                ensures
                    - constructs *this from item using perfect forwarding (converting constructor)
                    - #get<T>() == std::forward<T>(item)
                      (i.e. this object will either contain a copy of item or will have moved item into *this
                       depending on the reference type)
            !*/

            template <
                typename T
            >
            type_safe_union& operator= (
                T&& item
            );
            /*!
                requires
                    - std::decay_t<T> must be one of the types given to this object's template arguments
                ensures
                    - assigns *this from item using perfect forwarding (converting assignment)
                    - #get<T> == std::forward<T>(item)
                      (i.e. this object will either contain a copy of item or will have moved item into *this
                       depending on the reference type)
            !*/

            template <
                typename T,
                typename... Args
            >
            type_safe_union (
                in_place_tag<T>,
                Args&&... args
            );
            /*!
                requires
                    - T must be one of the types given to this object's template arguments
                ensures
                    - constructs *this with type T using constructor-arguments args...
                      (i.e. efficiently performs *this = T(args...))
            !*/

            ~type_safe_union(
            );
            /*!
                ensures
                    - all resources associated with this object have been freed
            !*/

            void clear();
            /*!
                ensures
                    - all resources associated with this object have been freed
                    - #is_empty() == true
            !*/

            template <
                typename T,
                typename... Args
            >
            void emplace(
                Args&&... args
            );
            /*!
                requires
                    - T must be one of the types given to this object's template arguments
                ensures
                    - re-assigns *this with type T using constructor-arguments args...
                      (i.e. efficiently performs *this = T(args...))
            !*/

            template <typename T>
            static constexpr int get_type_id (
            );
            /*!
               ensures
                    - if (T is the same type as one of the template arguments) then
                        - returns a number indicating which template argument it is. In particular,
                          if it's the first template argument it returns 1, if the second then 2, and so on.
                    - else if (T is in_place_tag<U>) then
                        - equivalent to returning get_type_id<U>())
                    - else
                        - returns -1
            !*/

            template <typename T>
            bool contains (
            ) const;
            /*!
                ensures
                    - if (this type_safe_union currently contains an object of type T) then
                        - returns true
                    - else
                        - returns false
            !*/

            bool is_empty (
            ) const;
            /*!
                ensures
                    - if (this type_safe_union currently contains any object at all) then
                        - returns true
                    - else
                        - returns false
            !*/

            int get_current_type_id(
            ) const;
            /*!
                ensures
                    - if (is_empty()) then
                        - returns 0
                    - else
                        - Returns the type id of the currently held type.  This is the same as
                          get_type_id<WhateverTypeIsCurrentlyHeld>().  Therefore, if the current type is
                          the first template argument it returns 1, if it's the second then 2, and so on.
            !*/

            template <typename F>
            void apply_to_contents(
                F&& f
            );
            /*!
                requires
                    - f is a callable object capable of operating on all the types contained
                      in this type_safe_union.  I.e.  std::forward<F>(f)(this->get<U>()) must be a valid
                      expression for all the possible U types.
                ensures
                    - if (is_empty() == false) then
                        - Let U denote the type of object currently contained in this type_safe_union
                        - calls std::forward<F>(f)(this->get<U>())
                        - The object passed to f() (i.e. by this->get<U>()) will be non-const.
            !*/

            template <typename F>
            void apply_to_contents(
                F&& f
            ) const;
            /*!
                requires
                    - f is a callable object capable of operating on all the types contained
                      in this type_safe_union.  I.e.  std::forward<F>(f)(this->get<U>()) must be a valid
                      expression for all the possible U types.
                ensures
                    - if (is_empty() == false) then
                        - Let U denote the type of object currently contained in this type_safe_union
                        - calls std::forward<F>(f)(this->get<U>())
                        - The object passed to f() (i.e. by this->get<U>()) will be const.
            !*/

            template <typename T>
            T& get(
            );
            /*!
                requires
                    - T must be one of the types given to this object's template arguments
                ensures
                    - #is_empty() == false
                    - #contains<T>() == true
                    - if (contains<T>() == true)
                        - returns a non-const reference to the object contained in this type_safe_union.
                    - else
                        - Constructs an object of type T inside *this
                        - Any previous object stored in this type_safe_union is destructed and its
                          state is lost.
                        - returns a non-const reference to the newly created T object.
            !*/

            template <typename T>
            T& get(
                const in_place_tag<T>& tag
            );
            /*!
                ensures
                    - equivalent to calling get<T>()
            !*/

            template <typename T>
            const T& cast_to (
            ) const;
            /*!
                requires
                    - T must be one of the types given to this object's template arguments
                ensures
                    - if (contains<T>() == true) then
                        - returns a const reference to the object contained in this type_safe_union.
                    - else
                        - throws bad_type_safe_union_cast
            !*/

            template <typename T>
            T& cast_to (
            );
            /*!
                requires
                    - T must be one of the types given to this object's template arguments
                ensures
                    - if (contains<T>() == true) then
                        - returns a non-const reference to the object contained in this type_safe_union.
                    - else
                        - throws bad_type_safe_union_cast
            !*/

            void swap (
                type_safe_union& item
            );
            /*!
                ensures
                    - swaps *this and item
            !*/

        };

    // ----------------------------------------------------------------------------------------

        template<typename... Types>
        inline void swap (
            type_safe_union<Types...>& a,
            type_safe_union<Types...>& b
        ) { a.swap(b); }
        /*!
            provides a global swap function
        !*/

    // ----------------------------------------------------------------------------------------

        template<
            typename TSU,
            typename F
        >
        void for_each_type(
            F&& f,
            TSU&& tsu
        );
        /*!
            requires
                - tsu is an object of type type_safe_union<Types...> for some types Types...
                - f is a callable object such that the following expression is valid for
                  all types U in Types...:
                    std::forward<F>(f)(in_place_tag<U>{}, std::forward<TSU>(tsu))
            ensures
                - This function iterates over all types U in Types... and calls:
                    std::forward<F>(f)(in_place_tag<U>{}, std::forward<TSU>(tsu))
        !*/

    // ----------------------------------------------------------------------------------------

        template<
            typename F,
            typename TSU
        >
        auto visit(
            F&& f,
            TSU&& tsu
        );
        /*!
            requires
                - tsu is an object of type type_safe_union<Types...> for some types Types...
                - f is a callable object capable of operating on all the types contained
                  in tsu.  I.e.  std::forward<F>(f)(this->get<U>()) must be a valid
                  expression for all the possible U types.
            ensures
                - if (tsu.is_empty() == false) then
                    - Let U denote the type of object currently contained in tsu.
                    - returns std::forward<F>(f)(this->get<U>())
                    - The object passed to f() (i.e. by this->get<U>()) will have the same reference
                      type as TSU.
        !*/

    // ----------------------------------------------------------------------------------------

        template<typename... Types>
        void serialize (
            const type_safe_union<Types...>& item,
            std::ostream& out
        );
        /*!
            provides serialization support

            Note that type_safe_union objects are serialized as follows:
             - if (item.is_empty()) then
                - perform: serialize(0, out)
             - else
                - perform: serialize(item.get_type_id<type_of_object_in_item>(), out);
                           serialize(item.get<type_of_object_in_item>(), out);
        !*/

        template<typename... Types>
        void deserialize (
            type_safe_union<Types...>& item,
            std::istream& in
        );
        /*!
            provides deserialization support
        !*/

    // ----------------------------------------------------------------------------------------

        template<typename... T>
        overloaded_helper<typename std::decay<T>::type...> overloaded(T&&... t)
        {
            return overloaded_helper<typename std::decay<T>::type...>{std::forward<T>(t)...};
        }
        /*!
            This is a helper function for passing many callable objects (usually lambdas)
            to either apply_to_contents(), visit() or for_each(), that combine to make a complete
            visitor. A picture paints a thousand words:

            using tsu = type_safe_union<int,float,std::string>;

            tsu a = std::string("hello there");

            std::string result;

            a.apply_to_contents(overloaded(
                [&result](int) {
                    result = std::string("int");
                },
                [&result](float) {
                    result = std::string("float");
                },
                [&result](const std::string& item) {
                    result = item;
                }
            ));

            assert(result == "hello there");
            result = "";

            result = visit(overloaded(
                [](int) {
                    return std::string("int");
                },
                [](float) {
                    return std::string("float");
                },
                [](const std::string& item) {
                    return item;
                }
            ), a);

            assert(result == "hello there");

            std::vector<int> type_ids;

            for_each_type(a, overloaded(
                [&type_ids](in_place_tag<int>, tsu& me) {
                    type_ids.push_back(me.get_type_id<int>());
                },
                [&type_ids](in_place_tag<float>, tsu& me) {
                    type_ids.push_back(me.get_type_id<float>());
                },
                [&type_ids](in_place_tag<std::string>, tsu& me) {
                    type_ids.push_back(me.get_type_id<std::string>());
                }
            ));

            assert(type_ids == vector<int>({0,1,2}));
        !*/
    }

    #endif // DLIB_TYPE_SAFE_UNION_KERNEl_ABSTRACT_