// 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_

#include "../algs.h"
#include "../noncopyable.h"

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 T1,
        typename T2 = _void,  // _void indicates parameter not used.
        typename T3 = _void,
        typename T4 = _void,
        typename T5 = _void, 
        typename T6 = _void,
        typename T7 = _void,
        typename T8 = _void,
        typename T9 = _void,
        typename T10 = _void,
        typename T11 = _void,
        typename T12 = _void,
        typename T13 = _void,
        typename T14 = _void,
        typename T15 = _void,
        typename T16 = _void,
        typename T17 = _void,
        typename T18 = _void,
        typename T19 = _void,
        typename T20 = _void
        >
    class type_safe_union : noncopyable
    {
        /*!
            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:

        typedef T1 type1;
        typedef T2 type2;
        typedef T3 type3;
        typedef T4 type4;
        typedef T5 type5;
        typedef T6 type6;
        typedef T7 type7;
        typedef T8 type8;
        typedef T9 type9;
        typedef T10 type10;
        typedef T11 type11;
        typedef T12 type12;
        typedef T13 type13;
        typedef T14 type14;
        typedef T15 type15;
        typedef T16 type16;
        typedef T17 type17;
        typedef T18 type18;
        typedef T19 type19;
        typedef T20 type20;

        type_safe_union(
        );
        /*!
            ensures
                - this object is properly initialized
        !*/

        template <typename T>
        type_safe_union (
            const T& item
        );
        /*!
            requires
                - T must be one of the types given to this object's template arguments
            ensures
                - this object is properly initialized
                - #get<T>() == item
                  (i.e. this object will contain a copy of item)
        !*/

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

        template <typename T>
        static 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.
                   (e.g. if T is the same type as T3 then this function returns 3)
               - 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
        !*/

        template <typename T>
        void apply_to_contents (
            T& obj
        );
        /*!
            requires
                - obj is a function object capable of operating on all the types contained
                  in this type_safe_union.  I.e.  obj(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 obj(this->get<U>())
                    - The object returned by this->get<U>() will be non-const
        !*/

        template <typename T>
        void apply_to_contents (
            const T& obj
        );
        /*!
            requires
                - obj is a function object capable of operating on all the types contained
                  in this type_safe_union.  I.e.  obj(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 obj(this->get<U>())
                    - The object returned by this->get<U>() will be non-const
        !*/

        template <typename T>
        void apply_to_contents (
            T& obj
        ) const;
        /*!
            requires
                - obj is a function object capable of operating on all the types contained
                  in this type_safe_union.  I.e.  obj(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 obj(this->get<U>())
                    - The object returned by this->get<U>() will be const
        !*/

        template <typename T>
        void apply_to_contents (
            const T& obj
        ) const;
        /*!
            requires
                - obj is a function object capable of operating on all the types contained
                  in this type_safe_union.  I.e.  obj(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 obj(this->get<U>())
                    - The object returned 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>
        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
        !*/

        template <typename T>
        type_safe_union& operator= (
            const T& item
        );
        /*!
            requires
                - T must be one of the types given to this object's template arguments
            ensures
                - #get<T>() == item
                  (i.e. this object will contain a copy of item)
                - returns *this
        !*/

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

    };

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

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

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

    template < ... >
    void serialize (
        const type_safe_union<...>& 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 < ...  >
    void deserialize (
        type_safe_union<...>& item, 
        std::istream& in
    );   
    /*!
        provides deserialization support 
    !*/

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

}

#endif // DLIB_TYPE_SAFE_UNION_KERNEl_ABSTRACT_