function

mbsyncさんのを参考にしてboost::functionを作ってみました

p_stade - Boost.Functionの実装の概要



汎用関数オブジェクトです



ヘッダーファイル1本で提供できるようにはしてあります




サンプル1(基本的な使い方)

#include <iostream>
#include <shand/function.hpp>

using namespace std;
using namespace shand;

void disp_hello()
{
    cout << "Hello" << endl;
}

bool is_even(int value)
{
    return value % 2 == 0;
}

struct disp_obj {
    void operator()(int value, double pi) const
    {
        cout << "disp_obj" << value << "," << pi << endl;
    }
};

int main()
{
    // void (*)(void)
    function<void (void)> func0 = disp_hello;

    func0(); // disp_hello(); ... Hello


    // bool (*)(int)
    function<bool (int)> func1 = is_even;

    bool result = func1(2); // is_even(2);


    // 関数オブジェクト
    function<void (int, double)> func_obj = disp_obj();

    func_obj(5, 3.14); // disp_obj::operator(5, 3.14);

    return 0;
}

サンプル2(メンバ関数ポインタ)

#include <iostream>
#include <shand/function.hpp>

using namespace std;
using namespace shand;

struct hoge {
    void disp()
    {
        cout << "hoge::disp" << endl;
    }

    void disp_value(int value)
    {
        cout << "hoge::disp_value " << value << endl;
    }
};

int main()
{
    hoge h;

    // void (hoge::*)()
    function<void (hoge*)> func0 = &hoge::disp;

    func0(&h); // h.disp();


    // void (hoge::*)(int)
    function<void (hoge*, int)> func1 = &hoge::disp_value;

    func1(&h, 3); // h.disp_value(3);


    return 0;
}

サンプル3(Boost.Bind)

#include <iostream>
#include <shand/function.hpp>
#include <boost/bind.hpp>

using namespace std;
using namespace shand;
using namespace boost;

bool is_even(int value)
{
    return value % 2 == 0;
}

int main()
{
    // bool (*)(int)
    function<bool (int)> func = bind(is_even, _1);

    bool result = func(2); // is_even(2);

    return 0;
}

サンプル4(Boost.Lambda)

#include <iostream>
#include <shand/function.hpp>
#include <boost/lambda/lambda.hpp>

using namespace std;
using namespace shand;
using namespace boost::lambda;

int main()
{
    // bool (*)(int)
    function<bool (int)> func = _1 % 2 == 0;

    bool result = func(2); // 2 % 2 == 0;

    return 0;
}


※boost::functionalと違い、以下のコードは通らない

#include <iostream>
#include <shand/function.hpp>
#include <boost/function.hpp>
#include <boost/lambda/lambda.hpp>

using namespace std;
using namespace boost::lambda;

int main()
{
    shand::function<void (int)> func = cout << _1 << "\n"; // エラー!...void型なのに値を返しました
    boost::function<void (int)> func = cout << _1 << "\n"; // OK
    func(3);

    return 0;
}


こちらなら通る

#include <iostream>
#include <shand/function.hpp>
#include <boost/function.hpp>
#include <boost/lambda/lambda.hpp>

using namespace std;
using namespace boost::lambda;

int main()
{
    shand::function<ostream& (int)> func = cout << _1 << "\n"; // OK
    boost::function<ostream& (int)> func = cout << _1 << "\n"; // OK
    func(3);

    return 0;
}

以下ソース(※VC++6.0非対応)

#ifndef SHAND_FUNCTION_INCLUDE
#define SHAND_FUNCTION_INCLUDE

#include <functional> // mem_fun

#define SHAND_TEMPLATE_PARAMS_1 class T0
#define SHAND_TEMPLATE_PARAMS_2 class T0, class T1
#define SHAND_TEMPLATE_PARAMS_3 class T0, class T1, class T2
#define SHAND_TEMPLATE_PARAMS_4 class T0, class T1, class T2, class T3
#define SHAND_TEMPLATE_PARAMS_5 class T0, class T1, class T2, class T3, class T4
#define SHAND_TEMPLATE_PARAMS_6 class T0, class T1, class T2, class T3, class T4, class T5
#define SHAND_TEMPLATE_PARAMS_7 class T0, class T1, class T2, class T3, class T4, class T5, class T6
#define SHAND_TEMPLATE_PARAMS_8 class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7
#define SHAND_TEMPLATE_PARAMS_9 class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8
#define SHAND_TEMPLATE_PARAMS_10 class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9

#define SHAND_TEMPLATE_ARGS_1   T0
#define SHAND_TEMPLATE_ARGS_2   T0, T1
#define SHAND_TEMPLATE_ARGS_3   T0, T1, T2
#define SHAND_TEMPLATE_ARGS_4   T0, T1, T2, T3
#define SHAND_TEMPLATE_ARGS_5   T0, T1, T2, T3, T4
#define SHAND_TEMPLATE_ARGS_6   T0, T1, T2, T3, T4, T5
#define SHAND_TEMPLATE_ARGS_7   T0, T1, T2, T3, T4, T5, T6
#define SHAND_TEMPLATE_ARGS_8   T0, T1, T2, T3, T4, T5, T6, T7
#define SHAND_TEMPLATE_ARGS_9   T0, T1, T2, T3, T4, T5, T6, T7, T8
#define SHAND_TEMPLATE_ARGS_10  T0, T1, T2, T3, T4, T5, T6, T7, T8, T9

#define SHAND_FUNCTION_PARAMS_1 T0 t0
#define SHAND_FUNCTION_PARAMS_2 T0 t0, T1 t1
#define SHAND_FUNCTION_PARAMS_3 T0 t0, T1 t1, T2 t2
#define SHAND_FUNCTION_PARAMS_4 T0 t0, T1 t1, T2 t2, T3 t3
#define SHAND_FUNCTION_PARAMS_5 T0 t0, T1 t1, T2 t2, T3 t3, T4 t4
#define SHAND_FUNCTION_PARAMS_6 T0 t0, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5
#define SHAND_FUNCTION_PARAMS_7 T0 t0, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6
#define SHAND_FUNCTION_PARAMS_8 T0 t0, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7
#define SHAND_FUNCTION_PARAMS_9 T0 t0, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8
#define SHAND_FUNCTION_PARAMS_10 T0 t0, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9

#define SHAND_FUNCTION_ARGS_1   t0
#define SHAND_FUNCTION_ARGS_2   t0, t1
#define SHAND_FUNCTION_ARGS_3   t0, t1, t2
#define SHAND_FUNCTION_ARGS_4   t0, t1, t2, t3
#define SHAND_FUNCTION_ARGS_5   t0, t1, t2, t3, t4
#define SHAND_FUNCTION_ARGS_6   t0, t1, t2, t3, t4, t5
#define SHAND_FUNCTION_ARGS_7   t0, t1, t2, t3, t4, t5, t6
#define SHAND_FUNCTION_ARGS_8   t0, t1, t2, t3, t4, t5, t6, t7
#define SHAND_FUNCTION_ARGS_9   t0, t1, t2, t3, t4, t5, t6, t7, t8
#define SHAND_FUNCTION_ARGS_10  t0, t1, t2, t3, t4, t5, t6, t7, t8, t9


#define SHAND_FUNCTION_MANAGER_N(Count) \
template <class FunctionPtr, class R, SHAND_TEMPLATE_PARAMS_##Count##>                  \
struct function_ptr_manager##Count## {                                                  \
    static R invoke(any_pointer function_ptr, SHAND_FUNCTION_PARAMS_##Count##)          \
    {                                                                                   \
        FunctionPtr func = reinterpret_cast<FunctionPtr>(function_ptr.func_ptr);        \
        return func(SHAND_FUNCTION_ARGS_##Count##);                                     \
    }                                                                                   \
                                                                                        \
    static void destroy(any_pointer)                                                    \
    {                                                                                   \
    }                                                                                   \
};                                                                                      \
                                                                                        \
template <class FunctionObj, class R, SHAND_TEMPLATE_PARAMS_##Count##>                  \
struct function_obj_manager##Count## {                                                  \
    static R invoke(any_pointer function_obj_ptr, SHAND_FUNCTION_PARAMS_##Count##)      \
    {                                                                                   \
        FunctionObj* func = reinterpret_cast<FunctionObj*>(function_obj_ptr.obj_ptr);   \
        return (*func)(SHAND_FUNCTION_ARGS_##Count##);                                  \
    }                                                                                   \
                                                                                        \
    static void destroy(any_pointer function_obj_ptr)                                   \
    {                                                                                   \
        FunctionObj* func = reinterpret_cast<FunctionObj*>(function_obj_ptr.obj_ptr);   \
        delete func;                                                                    \
    }                                                                                   \
};


#define SHAND_FUNCTION_CLASS_N(Count)   \
template <class R, SHAND_TEMPLATE_PARAMS_##Count##>                                 \
class function<R (SHAND_TEMPLATE_ARGS_##Count##)>                                   \
    : public function_detail::function_base<R (SHAND_TEMPLATE_ARGS_##Count##)> {    \
public:                                                                             \
    typedef R result_type;                                                          \
                                                                                    \
    result_type (*invoke_)(any_pointer, SHAND_TEMPLATE_ARGS_##Count##);             \
                                                                                    \
    function()                                                                      \
        : function_base()                                                           \
    {                                                                               \
        invoke_ = 0;                                                                \
    }                                                                               \
                                                                                    \
    function(const function& func)                                                  \
    {                                                                               \
        assign_to_own(func);                                                        \
    }                                                                               \
                                                                                    \
    template <class FuncType>                                                       \
    function(FuncType func)                                                         \
    {                                                                               \
        assign_to(func);                                                            \
    }                                                                               \
                                                                                    \
    function& operator=(const function& func)                                       \
    {                                                                               \
        if (&func == this)                                                          \
            return *this;                                                           \
                                                                                    \
        assign_to_own(func);                                                        \
        return *this;                                                               \
    }                                                                               \
                                                                                    \
    template <class FuncType>                                                       \
    function& operator=(FuncType func)                                              \
    {                                                                               \
        clear();                                                                    \
        assign_to(func);                                                            \
        return *this;                                                               \
    }                                                                               \
                                                                                    \
    result_type operator()(SHAND_FUNCTION_PARAMS_##Count##) const                   \
    {                                                                               \
        return invoke_(functor_, SHAND_FUNCTION_ARGS_##Count##);                    \
    }                                                                               \
                                                                                    \
    void clear()                                                                    \
    {                                                                               \
        if (!empty()) {                                                             \
            destroy_(functor_);                                                     \
                                                                                    \
            invoke_     = 0;                                                        \
            destroy_    = 0;                                                        \
        }                                                                           \
    }                                                                               \
                                                                                    \
    bool empty() const                                                              \
    {                                                                               \
        return !(invoke_ && destroy_);                                              \
    }                                                                               \
                                                                                    \
    operator bool()                                                                 \
    {                                                                               \
        return !empty();                                                            \
    }                                                                               \
                                                                                    \
    bool operator!()                                                                \
    {                                                                               \
        return empty();                                                             \
    }                                                                               \
                                                                                    \
private:                                                                            \
    template <class FuncType>                                                       \
    void assign_to(FuncType func)                                                   \
    {                                                                               \
        typedef typename function_detail::get_function_tag<FuncType>::type tag;     \
        assign_to(func, tag());                                                     \
    }                                                                               \
                                                                                    \
    template <class FunctionPtr>                                                    \
    void assign_to(FunctionPtr func, function_ptr_tag)                              \
    {                                                                               \
        typedef                                                                     \
            function_detail::function_ptr_manager##Count##<FunctionPtr, R, SHAND_TEMPLATE_ARGS_##Count##>   \
        actual_manager;                                                             \
                                                                                    \
        invoke_ = &actual_manager::invoke;                                          \
        destroy_ = &actual_manager::destroy;                                        \
                                                                                    \
        functor_.func_ptr = reinterpret_cast<void (*)()>(func);                     \
    }                                                                               \
                                                                                    \
    template <class FunctionObj>                                                    \
    void assign_to(FunctionObj func, function_obj_tag)                              \
    {                                                                               \
        typedef                                                                     \
            function_detail::function_obj_manager##Count##<FunctionObj, R, SHAND_TEMPLATE_ARGS_##Count##>   \
        actual_manager;                                                             \
                                                                                    \
        invoke_ = &actual_manager::invoke;                                          \
        destroy_ = &actual_manager::destroy;                                        \
                                                                                    \
        FunctionObj* new_func = new FunctionObj(func);                              \
        functor_.obj_ptr = static_cast<void*>(new_func);                            \
    }                                                                               \
                                                                                    \
    template <class MemberPtr>                                                      \
    void assign_to(MemberPtr func, member_ptr_tag)                                  \
    {                                                                               \
        assign_to(mem_fun(func));                                                   \
    }                                                                               \
                                                                                    \
    void assign_to_own(const function& func)                                        \
    {                                                                               \
        clear();                                                                    \
        invoke_     = func.invoke_;                                                 \
        destroy_    = func.destroy_;                                                \
        functor_    = func.functor_;                                                \
    }                                                                               \
};



namespace shand {

namespace function_detail {

namespace shand_work {
    // if_c
    template <bool Cond, class Then, class Else>
    struct if_c;

    template <class Then, class Else>
    struct if_c<true, Then, Else> {
        typedef Then type;
    };

    template <class Then, class Else>
    struct if_c<false, Then, Else> {
        typedef Else type;
    };


    // if_
    template <class Cond, class Then, class Else>
    struct if_ {
        typedef typename if_c<Cond::value, Then, Else>::type type;
    };

    // is_pointer
    template <class Type>
    struct is_pointer {
        static const bool value = false;
    };

    template <class Type>
    struct is_pointer<Type*> {
        static const bool value = true;
    };

    // is_member_pointer
    template <class Type>
    struct is_member_pointer {
        static const bool value = false;
    };

    template <class Type, class ClassName>
    struct is_member_pointer<Type ClassName::*> {
        static const bool value = true;
    };

    template <class Type, class ClassName>
    struct is_member_pointer<Type ClassName::* const> {
        static const bool value = true;
    };

    template <class Type, class ClassName>
    struct is_member_pointer<Type ClassName::* volatile> {
        static const bool value = true;
    };

    template <class Type, class ClassName>
    struct is_member_pointer<Type ClassName::* const volatile> {
        static const bool value = true;
    };

    // is_same
    template <class, class>
    struct is_same {
        static const bool value = false;
    };

    template <class Type>
    struct is_same<Type, Type> {
        static const bool value = true;
    };

} // namespace shand_work

    union any_pointer
    {
        void* obj_ptr;
        void (*func_ptr)(); // 関数ポインタとvoid*間のキャストは許されないので必要
    };

    // 関数判別タグ
    struct function_ptr_tag {}; // 関数ポインタ
    struct function_obj_tag {}; // 関数オブジェクト
    struct member_ptr_tag {};   // メンバ関数ポインタ

    template <class FuncType>
    struct get_function_tag {
    private:
        typedef
            typename shand_work::if_<shand_work::is_pointer<FuncType>, function_ptr_tag, FuncType>::type
        func_ptr_tag;

        typedef
            typename shand_work::if_<shand_work::is_member_pointer<func_ptr_tag>, member_ptr_tag, func_ptr_tag>::type
        function_or_member_ptr_tag;

        typedef
            typename shand_work::if_<shand_work::is_same<FuncType, function_or_member_ptr_tag>,
                function_obj_tag, function_or_member_ptr_tag>::type
        ret_type;
    public:
        typedef ret_type type;
    };

    template <class FunctionPtr, class R>
    struct function_ptr_manager0 {
        static R invoke(any_pointer function_ptr)
        {
            FunctionPtr func = reinterpret_cast<FunctionPtr>(function_ptr.func_ptr);
            return func();
        }

        static void destroy(any_pointer)
        {
        }
    };

    template <class FunctionObj, class R>
    struct function_obj_manager0 {
        static R invoke(any_pointer function_obj_ptr)
        {
            FunctionObj* func = reinterpret_cast<FunctionObj*>(function_obj_ptr.obj_ptr);
            return (*func)();
        }

        static void destroy(any_pointer function_obj_ptr)
        {
            FunctionObj* func = reinterpret_cast<FunctionObj*>(function_obj_ptr.obj_ptr);
            delete func;
            func = 0;
        }
    };

    SHAND_FUNCTION_MANAGER_N(1);
    SHAND_FUNCTION_MANAGER_N(2);
    SHAND_FUNCTION_MANAGER_N(3);
    SHAND_FUNCTION_MANAGER_N(4);
    SHAND_FUNCTION_MANAGER_N(5);
    SHAND_FUNCTION_MANAGER_N(6);
    SHAND_FUNCTION_MANAGER_N(7);
    SHAND_FUNCTION_MANAGER_N(8);
    SHAND_FUNCTION_MANAGER_N(9);
    SHAND_FUNCTION_MANAGER_N(10);


    template <class Signature>
    class function_base {
    protected:
        typedef function_detail::any_pointer        any_pointer;
        typedef function_detail::function_ptr_tag   function_ptr_tag;
        typedef function_detail::function_obj_tag   function_obj_tag;
        typedef function_detail::member_ptr_tag     member_ptr_tag;

    public:
        void (*destroy_)(any_pointer);
        any_pointer functor_;

        function_base()
        {
            destroy_ = 0;
        }

        virtual ~function_base()
        {
            destroy_(functor_);
        }
    };

} // namespace shand::function_detail



template <class Signature>
class function;

template <class R>
class function<R (void)>
    : public function_detail::function_base<R (void)> {
public:
    typedef R result_type;

    result_type (*invoke_)(any_pointer);

    function()
        : function_base()
    {
        invoke_ = 0;
    }

    function(const function& func)
    {
        assign_to_own(func);
    }

    template <class FuncType>
    function(FuncType func)
    {
        assign_to(func);
    }

    function& operator=(const function& func)
    {
        if (&func == this)
            return *this;

        assign_to_own(func);
        return *this;
    }

    template <class FuncType>
    function& operator=(FuncType func)
    {
        clear();
        assign_to(func);

        return *this;
    }

    result_type operator()() const
    {
        return invoke_(functor_);
    }

    void clear()
    {
        if (!empty()) {
            destroy_(functor_);

            invoke_     = 0;
            destroy_    = 0;
        }
    }

    bool empty() const
    {
        return !(invoke_ && destroy_);
    }

    operator bool()
    {
        return !empty();
    }

    bool operator!()
    {
        return empty();
    }

private:
    template <class FuncType>
    void assign_to(FuncType func)
    {
        typedef typename function_detail::get_function_tag<FuncType>::type tag;
        assign_to(func, tag());
    }

    template <class FunctionPtr>
    void assign_to(FunctionPtr func, function_ptr_tag)
    {
        typedef function_detail::function_ptr_manager0<FunctionPtr, R> actual_manager;

        invoke_ = &actual_manager::invoke;
        destroy_ = &actual_manager::destroy;

        functor_.func_ptr = reinterpret_cast<void (*)()>(func);
    }

    template <class FunctionObj>
    void assign_to(FunctionObj func, function_obj_tag)
    {
        typedef function_detail::function_obj_manager0<FunctionObj, R> actual_manager;

        invoke_ = &actual_manager::invoke;
        destroy_ = &actual_manager::destroy;

        FunctionObj* new_func = new FunctionObj(func);
        functor_.obj_ptr = static_cast<void*>(new_func);
    }

    void assign_to_own(const function& func)
    {
        clear();
        invoke_     = func.invoke_;
        destroy_    = func.destroy_;
        functor_    = func.functor_;
    }
};


SHAND_FUNCTION_CLASS_N(1)
SHAND_FUNCTION_CLASS_N(2)
SHAND_FUNCTION_CLASS_N(3)
SHAND_FUNCTION_CLASS_N(4)
SHAND_FUNCTION_CLASS_N(5)
SHAND_FUNCTION_CLASS_N(6)
SHAND_FUNCTION_CLASS_N(7)
SHAND_FUNCTION_CLASS_N(8)
SHAND_FUNCTION_CLASS_N(9)
SHAND_FUNCTION_CLASS_N(10)

} // namespace shand


// delete macro
#undef SHAND_TEMPLATE_PARAMS_1
#undef SHAND_TEMPLATE_PARAMS_2
#undef SHAND_TEMPLATE_PARAMS_3
#undef SHAND_TEMPLATE_PARAMS_4
#undef SHAND_TEMPLATE_PARAMS_5
#undef SHAND_TEMPLATE_PARAMS_6
#undef SHAND_TEMPLATE_PARAMS_7
#undef SHAND_TEMPLATE_PARAMS_8
#undef SHAND_TEMPLATE_PARAMS_9
#undef SHAND_TEMPLATE_PARAMS_10

#undef SHAND_TEMPLATE_ARGS_1
#undef SHAND_TEMPLATE_ARGS_2
#undef SHAND_TEMPLATE_ARGS_3
#undef SHAND_TEMPLATE_ARGS_4
#undef SHAND_TEMPLATE_ARGS_5
#undef SHAND_TEMPLATE_ARGS_6
#undef SHAND_TEMPLATE_ARGS_7
#undef SHAND_TEMPLATE_ARGS_8
#undef SHAND_TEMPLATE_ARGS_9
#undef SHAND_TEMPLATE_ARGS_10

#undef SHAND_FUNCTION_PARAMS_1
#undef SHAND_FUNCTION_PARAMS_2
#undef SHAND_FUNCTION_PARAMS_3
#undef SHAND_FUNCTION_PARAMS_4
#undef SHAND_FUNCTION_PARAMS_5
#undef SHAND_FUNCTION_PARAMS_6
#undef SHAND_FUNCTION_PARAMS_7
#undef SHAND_FUNCTION_PARAMS_8
#undef SHAND_FUNCTION_PARAMS_9
#undef SHAND_FUNCTION_PARAMS_10

#undef SHAND_FUNCTION_ARGS_1
#undef SHAND_FUNCTION_ARGS_2
#undef SHAND_FUNCTION_ARGS_3
#undef SHAND_FUNCTION_ARGS_4
#undef SHAND_FUNCTION_ARGS_5
#undef SHAND_FUNCTION_ARGS_6
#undef SHAND_FUNCTION_ARGS_7
#undef SHAND_FUNCTION_ARGS_8
#undef SHAND_FUNCTION_ARGS_9
#undef SHAND_FUNCTION_ARGS_10

#undef SHAND_FUNCTION_MANAGER_N
#undef SHAND_FUNCTION_CLASS_N


#endif // SHAND_FUNCTION_INCLUDE

※2008/01/15 functionのインスタンス未使用時に落ちるバグ修正
※2008/01/18 こちらのバグ修正



ライブラリまとめ