Boost.Functionの実装技術(2) - タグディスパッチ

前回の続きです


=演算子で、引数の型が関数ポインタか関数オブジェクトかを判別して処理を分けるにはどうすればいいだろうか


ここで使われているのがタグディスパッチという手法である

タグディスパッチのくわしい解説はここで行っているので参照してもらいたい(くわしくはないかも)


まず、関数ポインタ・関数オブジェクトそれぞれのタグを定義する

struct function_ptr_tag {};
struct function_obj_tag {};


次に指定された型が関数ポインタか関数オブジェクトか判別するメタ関数を定義する

#include <boost/type_traits.hpp>
#include <boost/mpl/if.hpp>

template <class FuncType>
struct get_function_tag {
private:
    // 関数ポインタ?
    typedef typename if_<is_pointer<FuncType>, function_ptr_tag, FuncType>::type func_ptr_tag;

    // 関数オブジェクト?
    typedef typename if_<is_same<func_ptr_tag, FuncType>, function_obj_tag, function_ptr_tag>::type ret_type;
public:
    typedef ret_type type;
};




なんて持ってねーよ!という方にはこれを使っていただきたい<shand/type_traits.hpp><shand/mpl/if.hpp>



これで準備はできた
functionの=演算子を定義しよう

template <class R>
class function {
    R (*invoke_)(any_pointer);
    any_pointer functor_;

public:
    template <class FuncType>
    function& operator=(FuncType func)
    {
        // FuncTypeが関数ポインタか関数オブジェクトか判別するタグ取得
        typedef typename get_function_tag<FuncType>::type func_tag;

        assign_to(func, func_tag()); // 関数ポインタの場合はassign_to(... function_ptr_tag);が呼ばれる
                                     // 関数オブジェクトの場合はassign_to(... function_obj_tag);が呼ばれる

        return *this;
    }

    R operator()()
    {
        return invoke_(functor_);
    }

private:
    // 前回作成したset_function_ptr/set_function_objにタグ引数を追加しただけのもの
    template <class FuncPtr>
    void assign_to(FuncPtr func_ptr, function_ptr_tag)
    {
        invoke_ = &function_ptr_manager<FuncPtr, R>::invoke;
        functor_.func_ptr = reinterpret_cast<void(*)()>(func_ptr);
    }

    template <class FuncObj>
    void assign_to(FuncObj func_obj, function_obj_tag)
    {
        invoke_ = &function_obj_manager<FuncObj, R>::invoke;
        functor_.obj_ptr = reinterpret_cast<void*>(new FuncObj(func_obj));
    }
};

前回作成したものと合わせることでこれで以下のようなプログラムが書ける

int func() { return 1; }
struct func_obj { int operator()() { return 2; } }


function<int> f;

// 関数ポインタ
f = &func;
int result = f();

// 関数オブジェクト
f = func_obj();
int result = f();

完成したfunctionはこちら