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

namespace dlib
{

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

    class bound_function_pointer
    {
        /*!
            INITIAL VALUE
                is_set() == false

            WHAT THIS OBJECT REPRESENTS
                This object represents a function with all its arguments bound to 
                specific objects.  For example:

                    void test(int& var) { var = var+1; }

                    bound_function_pointer funct;

                    int a = 4; 
                    funct.set(test,a); // bind the variable a to the first argument of the test() function 

                    // at this point a == 4
                    funct();
                    // after funct() is called a == 5
        !*/

    public:

        bound_function_pointer (  
        );
        /*!
            ensures                
                - #*this is properly initialized
        !*/

        bound_function_pointer(
            const bound_function_pointer& item
        );
        /*!
            ensures
                - *this == item
        !*/

        ~bound_function_pointer (
        );
        /*!
            ensures
                - any resources associated with *this have been released
        !*/

        bound_function_pointer& operator=(
            const bound_function_pointer& item
        );
        /*!
            ensures
                - *this == item
        !*/

        void clear(
        );
        /*!
            ensures
                - #*this has its initial value
        !*/

        bool is_set (
        ) const;
        /*!
            ensures
                - if (this->set() has been called) then
                    - returns true
                - else
                    - returns false
        !*/

        operator some_undefined_pointer_type (
        ) const;
        /*!
            ensures
                - if (is_set()) then
                    - returns a non 0 value
                - else
                    - returns a 0 value
        !*/

        bool operator! (
        ) const;
        /*!
            ensures
                - returns !is_set()
        !*/

        void operator () (
        ) const;
        /*!
            requires
                - is_set() == true
            ensures
                - calls the bound function on the object(s) specified by the last 
                  call to this->set()
            throws
                - any exception thrown by the function specified by
                  the previous call to this->set().
                    If any of these exceptions are thrown then the call to this 
                    function will have no effect on *this.                  
        !*/

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

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

        template <typename F>
        void set (
            F& function_object
        );
        /*!
            requires
                - function_object() is a valid expression 
            ensures
                - #is_set() == true
                - calls to this->operator() will call function_object()
                  (This seems pointless but it is a useful base case)
        !*/

        template < typename T>
        void set (
            T& object,
            void (T::*funct)()
        );
        /*!
            requires
                - funct == a valid member function pointer for class T
            ensures
                - #is_set() == true
                - calls to this->operator() will call (object.*funct)()
        !*/

        template < typename T>
        void set (
            const T& object,
            void (T::*funct)()const
        );
        /*!
            requires
                - funct == a valid bound function pointer for class T
            ensures
                - #is_set() == true
                - calls to this->operator() will call (object.*funct)()
        !*/

        void set (
            void (*funct)()
        );
        /*!
            requires
                - funct == a valid function pointer 
            ensures
                - #is_set() == true
                - calls to this->operator() will call funct()
        !*/

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

        template <typename F, typename A1 >
        void set (
            F& function_object,
            A1& arg1
        );
        /*!
            requires
                - function_object(arg1) is a valid expression 
            ensures
                - #is_set() == true
                - calls to this->operator() will call function_object(arg1)
        !*/

        template < typename T, typename T1, typename A1 >
        void set (
            T& object,
            void (T::*funct)(T1),
            A1& arg1
        );
        /*!
            requires
                - funct == a valid member function pointer for class T
            ensures
                - #is_set() == true
                - calls to this->operator() will call (object.*funct)(arg1)
        !*/

        template < typename T, typename T1, typename A1 >
        void set (
            const T& object,
            void (T::*funct)(T1)const,
            A1& arg1
        );
        /*!
            requires
                - funct == a valid bound function pointer for class T
            ensures
                - #is_set() == true
                - calls to this->operator() will call (object.*funct)(arg1)
        !*/

        template <typename T1, typename A1>
        void set (
            void (*funct)(T1),
            A1& arg1
        );
        /*!
            requires
                - funct == a valid function pointer 
            ensures
                - #is_set() == true
                - calls to this->operator() will call funct(arg1)
        !*/

        // ----------------------
        template <typename F, typename A1, typename A2 >
        void set (
            F& function_object,
            A1& arg1,
            A2& arg2
        );
        /*!
            requires
                - function_object(arg1,arg2) is a valid expression 
            ensures
                - #is_set() == true
                - calls to this->operator() will call function_object(arg1,arg2)
        !*/

        template < typename T, typename T1, typename A1,
                               typename T2, typename A2>
        void set (
            T& object,
            void (T::*funct)(T1,T2),
            A1& arg1,
            A2& arg2
        );
        /*!
            requires
                - funct == a valid member function pointer for class T
            ensures
                - #is_set() == true
                - calls to this->operator() will call (object.*funct)(arg1,arg2)
        !*/

        template < typename T, typename T1, typename A1, 
                               typename T2, typename A2>
        void set (
            const T& object,
            void (T::*funct)(T1,T2)const,
            A1& arg1,
            A2& arg2
        );
        /*!
            requires
                - funct == a valid bound function pointer for class T
            ensures
                - #is_set() == true
                - calls to this->operator() will call (object.*funct)(arg1,arg2)
        !*/

        template <typename T1, typename A1,
                  typename T2, typename A2>
        void set (
            void (*funct)(T1,T2),
            A1& arg1,
            A2& arg2
        );
        /*!
            requires
                - funct == a valid function pointer 
            ensures
                - #is_set() == true
                - calls to this->operator() will call funct(arg1,arg2)
        !*/

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

        template <typename F, typename A1, typename A2, typename A3 >
        void set (
            F& function_object,
            A1& arg1,
            A2& arg2,
            A3& arg3
        );
        /*!
            requires
                - function_object(arg1,arg2,arg3) is a valid expression 
            ensures
                - #is_set() == true
                - calls to this->operator() will call function_object(arg1,arg2,arg3)
        !*/

        template < typename T, typename T1, typename A1,
                               typename T2, typename A2,
                               typename T3, typename A3>
        void set (
            T& object,
            void (T::*funct)(T1,T2,T3),
            A1& arg1,
            A2& arg2,
            A3& arg3
        );
        /*!
            requires
                - funct == a valid member function pointer for class T
            ensures
                - #is_set() == true
                - calls to this->operator() will call (object.*funct)(arg1,arg2,arg3)
        !*/

        template < typename T, typename T1, typename A1,
                               typename T2, typename A2,
                               typename T3, typename A3>
        void set (
            const T& object,
            void (T::*funct)(T1,T2,T3)const,
            A1& arg1,
            A2& arg2,
            A3& arg3
        );
        /*!
            requires
                - funct == a valid bound function pointer for class T
            ensures
                - #is_set() == true
                - calls to this->operator() will call (object.*funct)(arg1,arg2,arg3)
        !*/

        template <typename T1, typename A1,
                  typename T2, typename A2,
                  typename T3, typename A3>
        void set (
            void (*funct)(T1,T2,T3),
            A1& arg1,
            A2& arg2,
            A3& arg3
        );
        /*!
            requires
                - funct == a valid function pointer 
            ensures
                - #is_set() == true
                - calls to this->operator() will call funct(arg1,arg2,arg3)
        !*/

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

        template <typename F, typename A1, typename A2, typename A3, typename A4>
        void set (
            F& function_object,
            A1& arg1,
            A2& arg2,
            A3& arg3,
            A4& arg4
        );
        /*!
            requires
                - function_object(arg1,arg2,arg3,arg4) is a valid expression 
            ensures
                - #is_set() == true
                - calls to this->operator() will call function_object(arg1,arg2,arg3,arg4)
        !*/

        template < typename T, typename T1, typename A1,
                               typename T2, typename A2,
                               typename T3, typename A3,
                               typename T4, typename A4>
        void set (
            T& object,
            void (T::*funct)(T1,T2,T3,T4),
            A1& arg1,
            A2& arg2,
            A3& arg3,
            A4& arg4
        );
        /*!
            requires
                - funct == a valid member function pointer for class T
            ensures
                - #is_set() == true
                - calls to this->operator() will call (object.*funct)(arg1,arg2,arg3,arg4)
        !*/

        template < typename T, typename T1, typename A1,
                               typename T2, typename A2,
                               typename T3, typename A3,
                               typename T4, typename A4>
        void set (
            const T& object,
            void (T::*funct)(T1,T2,T3,T4)const,
            A1& arg1,
            A2& arg2,
            A3& arg3,
            A4& arg4
        );
        /*!
            requires
                - funct == a valid bound function pointer for class T
            ensures
                - #is_set() == true
                - calls to this->operator() will call (object.*funct)(arg1,arg2,arg3,arg4)
        !*/

        template <typename T1, typename A1,
                  typename T2, typename A2,
                  typename T3, typename A3,
                  typename T4, typename A4>
        void set (
            void (*funct)(T1,T2,T3,T4),
            A1& arg1,
            A2& arg2,
            A3& arg3,
            A4& arg4
        );
        /*!
            requires
                - funct == a valid function pointer 
            ensures
                - #is_set() == true
                - calls to this->operator() will call funct(arg1,arg2,arg3,arg4)
        !*/

    };    

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

    inline void swap (
        bound_function_pointer& a,
        bound_function_pointer& b
    ) { a.swap(b); }
    /*!
        provides a global swap function
    !*/

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

}

#endif // DLIB_BOUND_FUNCTION_POINTER_KERNEl_ABSTRACT_