    // Copyright (C) 2011  Davis E. King (davis@dlib.net)
    // License: Boost Software License   See LICENSE.txt for the full license.
    #ifndef DLIB_AnY_FUNCTION_H__
    #define DLIB_AnY_FUNCTION_H__

    #include "any.h"
    #include "../smart_pointers.h"

    #include "any_function_abstract.h"

    namespace dlib
    {

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

        template <typename T>
        struct sig_traits {};

        template <
            typename T
            >
        struct sig_traits<T ()>
        {
            typedef T result_type;
            typedef void arg1_type;
            typedef void arg2_type;
            typedef void arg3_type;
            typedef void arg4_type;
            typedef void arg5_type;
            typedef void arg6_type;
            typedef void arg7_type;
            typedef void arg8_type;
            typedef void arg9_type;
            typedef void arg10_type;

            const static unsigned long num_args = 0;
        };

        template <
            typename T,
            typename A1
            >
        struct sig_traits<T (A1)>
        {
            typedef T result_type;
            typedef A1 arg1_type;
            typedef void arg2_type;
            typedef void arg3_type;
            typedef void arg4_type;
            typedef void arg5_type;
            typedef void arg6_type;
            typedef void arg7_type;
            typedef void arg8_type;
            typedef void arg9_type;
            typedef void arg10_type;

            const static unsigned long num_args = 1;
        };

        template <
            typename T,
            typename A1, typename A2
            >
        struct sig_traits<T (A1,A2)>
        {
            typedef T result_type;
            typedef A1 arg1_type;
            typedef A2 arg2_type;
            typedef void arg3_type;
            typedef void arg4_type;
            typedef void arg5_type;
            typedef void arg6_type;
            typedef void arg7_type;
            typedef void arg8_type;
            typedef void arg9_type;
            typedef void arg10_type;

            const static unsigned long num_args = 2;
        };

        template <
            typename T,
            typename A1, typename A2, typename A3
            >
        struct sig_traits<T (A1,A2,A3)>
        {
            typedef T result_type;
            typedef A1 arg1_type;
            typedef A2 arg2_type;
            typedef A3 arg3_type;
            typedef void arg4_type;
            typedef void arg5_type;
            typedef void arg6_type;
            typedef void arg7_type;
            typedef void arg8_type;
            typedef void arg9_type;
            typedef void arg10_type;

            const static unsigned long num_args = 3;
        };

        template <
            typename T,
            typename A1, typename A2, typename A3,
            typename A4
            >
        struct sig_traits<T (A1,A2,A3,A4)>
        {
            typedef T result_type;
            typedef A1 arg1_type;
            typedef A2 arg2_type;
            typedef A3 arg3_type;
            typedef A4 arg4_type;
            typedef void arg5_type;
            typedef void arg6_type;
            typedef void arg7_type;
            typedef void arg8_type;
            typedef void arg9_type;
            typedef void arg10_type;

            const static unsigned long num_args = 4;
        };

        template <
            typename T,
            typename A1, typename A2, typename A3,
            typename A4, typename A5
            >
        struct sig_traits<T (A1,A2,A3,A4,A5)>
        {
            typedef T result_type;
            typedef A1 arg1_type;
            typedef A2 arg2_type;
            typedef A3 arg3_type;
            typedef A4 arg4_type;
            typedef A5 arg5_type;
            typedef void arg6_type;
            typedef void arg7_type;
            typedef void arg8_type;
            typedef void arg9_type;
            typedef void arg10_type;

            const static unsigned long num_args = 5;
        };

        template <
            typename T,
            typename A1, typename A2, typename A3,
            typename A4, typename A5, typename A6
            >
        struct sig_traits<T (A1,A2,A3,A4,A5,A6)>
        {
            typedef T result_type;
            typedef A1 arg1_type;
            typedef A2 arg2_type;
            typedef A3 arg3_type;
            typedef A4 arg4_type;
            typedef A5 arg5_type;
            typedef A6 arg6_type;
            typedef void arg7_type;
            typedef void arg8_type;
            typedef void arg9_type;
            typedef void arg10_type;

            const static unsigned long num_args = 6;
        };

        template <
            typename T,
            typename A1, typename A2, typename A3,
            typename A4, typename A5, typename A6,
            typename A7
            >
        struct sig_traits<T (A1,A2,A3,A4,A5,A6,A7)>
        {
            typedef T result_type;
            typedef A1 arg1_type;
            typedef A2 arg2_type;
            typedef A3 arg3_type;
            typedef A4 arg4_type;
            typedef A5 arg5_type;
            typedef A6 arg6_type;
            typedef A7 arg7_type;
            typedef void arg8_type;
            typedef void arg9_type;
            typedef void arg10_type;

            const static unsigned long num_args = 7;
        };

        template <
            typename T,
            typename A1, typename A2, typename A3,
            typename A4, typename A5, typename A6,
            typename A7, typename A8
            >
        struct sig_traits<T (A1,A2,A3,A4,A5,A6,A7,A8)>
        {
            typedef T result_type;
            typedef A1 arg1_type;
            typedef A2 arg2_type;
            typedef A3 arg3_type;
            typedef A4 arg4_type;
            typedef A5 arg5_type;
            typedef A6 arg6_type;
            typedef A7 arg7_type;
            typedef A8 arg8_type;
            typedef void arg9_type;
            typedef void arg10_type;

            const static unsigned long num_args = 8;
        };

        template <
            typename T,
            typename A1, typename A2, typename A3,
            typename A4, typename A5, typename A6,
            typename A7, typename A8, typename A9
            >
        struct sig_traits<T (A1,A2,A3,A4,A5,A6,A7,A8,A9)>
        {
            typedef T result_type;
            typedef A1 arg1_type;
            typedef A2 arg2_type;
            typedef A3 arg3_type;
            typedef A4 arg4_type;
            typedef A5 arg5_type;
            typedef A6 arg6_type;
            typedef A7 arg7_type;
            typedef A8 arg8_type;
            typedef A9 arg9_type;
            typedef void arg10_type;

            const static unsigned long num_args = 9;
        };

        template <
            typename T,
            typename A1, typename A2, typename A3,
            typename A4, typename A5, typename A6,
            typename A7, typename A8, typename A9,
            typename A10
            >
        struct sig_traits<T (A1,A2,A3,A4,A5,A6,A7,A8,A9,A10)>
        {
            typedef T result_type;
            typedef A1 arg1_type;
            typedef A2 arg2_type;
            typedef A3 arg3_type;
            typedef A4 arg4_type;
            typedef A5 arg5_type;
            typedef A6 arg6_type;
            typedef A7 arg7_type;
            typedef A8 arg8_type;
            typedef A9 arg9_type;
            typedef A10 arg10_type;

            const static unsigned long num_args = 10;
        };

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

        template <
            typename function_type,
            // These arguments are used to control the overloading.  A user should
            // not mess with them.
            typename Enabled = void,
            unsigned long Num_args = sig_traits<function_type>::num_args
            >
        class any_function
        {
        private:
            any_function() {}
        /* !!!!!!!!    ERRORS ON THE ABOVE LINE    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            An error on this line means you are trying to use a function signature
            with more than the supported number of arguments.  The current version
            of dlib only supports up to 10 arguments.
        !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
        };

        // The following preprocessor commands build the various overloaded versions
        // of any_function for different numbers of commands and void vs. non-void return
        // types.

    //  0 arguments
    #define DLIB_ANY_FUNCTION_ARG_LIST
    #define DLIB_ANY_FUNCTION_ARGS
    #define DLIB_ANY_FUNCTION_NUM_ARGS 0
    #include "any_function_impl2.h"

    //  1 argument
    #define DLIB_ANY_FUNCTION_ARG_LIST arg1_type a1
    #define DLIB_ANY_FUNCTION_ARGS a1
    #define DLIB_ANY_FUNCTION_NUM_ARGS 1
    #include "any_function_impl2.h"

    //  2 arguments
    #define DLIB_ANY_FUNCTION_ARG_LIST arg1_type a1, arg2_type a2
    #define DLIB_ANY_FUNCTION_ARGS a1,a2
    #define DLIB_ANY_FUNCTION_NUM_ARGS 2
    #include "any_function_impl2.h"

    //  3 arguments
    #define DLIB_ANY_FUNCTION_ARG_LIST arg1_type a1, arg2_type a2, arg3_type a3
    #define DLIB_ANY_FUNCTION_ARGS a1,a2,a3
    #define DLIB_ANY_FUNCTION_NUM_ARGS 3
    #include "any_function_impl2.h"

    //  4 arguments
    #define DLIB_ANY_FUNCTION_ARG_LIST arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4
    #define DLIB_ANY_FUNCTION_ARGS a1,a2,a3,a4
    #define DLIB_ANY_FUNCTION_NUM_ARGS 4
    #include "any_function_impl2.h"

    //  5 arguments
    #define DLIB_ANY_FUNCTION_ARG_LIST arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, \
                                       arg5_type a5
    #define DLIB_ANY_FUNCTION_ARGS a1,a2,a3,a4,a5
    #define DLIB_ANY_FUNCTION_NUM_ARGS 5
    #include "any_function_impl2.h"

    //  6 arguments
    #define DLIB_ANY_FUNCTION_ARG_LIST arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, \
                                       arg5_type a5, arg6_type a6
    #define DLIB_ANY_FUNCTION_ARGS a1,a2,a3,a4,a5,a6
    #define DLIB_ANY_FUNCTION_NUM_ARGS 6
    #include "any_function_impl2.h"

    //  7 arguments
    #define DLIB_ANY_FUNCTION_ARG_LIST arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, \
                                       arg5_type a5, arg6_type a6, arg7_type a7
    #define DLIB_ANY_FUNCTION_ARGS a1,a2,a3,a4,a5,a6,a7
    #define DLIB_ANY_FUNCTION_NUM_ARGS 7
    #include "any_function_impl2.h"

    //  8 arguments
    #define DLIB_ANY_FUNCTION_ARG_LIST arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, \
                                       arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8
    #define DLIB_ANY_FUNCTION_ARGS a1,a2,a3,a4,a5,a6,a7,a8
    #define DLIB_ANY_FUNCTION_NUM_ARGS 8
    #include "any_function_impl2.h"

    //  9 arguments
    #define DLIB_ANY_FUNCTION_ARG_LIST arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, \
                                       arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8, \
                                       arg9_type a9
    #define DLIB_ANY_FUNCTION_ARGS a1,a2,a3,a4,a5,a6,a7,a8,a9
    #define DLIB_ANY_FUNCTION_NUM_ARGS 9
    #include "any_function_impl2.h"

    //  10 arguments
    #define DLIB_ANY_FUNCTION_ARG_LIST arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, \
                                       arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8, \
                                       arg9_type a9, arg10_type a10
    #define DLIB_ANY_FUNCTION_ARGS a1,a2,a3,a4,a5,a6,a7,a8,a9,a10
    #define DLIB_ANY_FUNCTION_NUM_ARGS 10
    #include "any_function_impl2.h"

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

        template <typename function_type>
        inline void swap (
            any_function<function_type>& a,
            any_function<function_type>& b
        ) { a.swap(b); }

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

        template <typename T, typename function_type>
        T& any_cast(any_function<function_type>& a) { return a.template cast_to<T>(); }

        template <typename T, typename function_type>
        const T& any_cast(const any_function<function_type>& a) { return a.template cast_to<T>(); }

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

    }

    #endif // DLIB_AnY_FUNCTION_H__