読者です 読者をやめる 読者になる 読者になる

C++と色々

主にC++やプログラムに関する記事を投稿します。

マルチメソッド1 自力編

Modern C++ Designに載っている話です。マルチメソッドとは多重ディスパッチとも言い、関数を複数の引数の動的な型によって呼び分けることです。ゲームで、2つのオブジェクトによって最適な衝突判定関数を呼び分ける時などに使えます。*1

どうにかして擬似的に、スマートにマルチメソッドが実現できないか考えていきます。最初はタグとマクロを使用した実装です。

#include <iostream>
#include <string>

//タグ
struct baka_tag {};
struct aho_tag {};
struct boke_tag {};

//基底クラス
template <class Tag>
struct base
{
    typedef Tag tag;
    virtual std::string get() const = 0;
};

class baka
    : public base<baka_tag>
{
public:
    std::string get() const override;
};

class aho
    : public base<aho_tag>
{
public:
    std::string get() const override;
};

class boke
    : public base<boke_tag>
{
public:
    std::string get() const override;
};

//2つの引数に最適な処理になっている関数
void func_baka_and_aho(const baka& b, const aho& a);
void func_baka_and_boke(const baka& ba, const boke& bo);
void func_aho_and_boke(const aho& a, const boke& b);

//自動で対称の関数も定義
#define DEFINE_MULTI_FUNCTION(func, callback_func, tag1, tag2)\
template <class T, class U>\
void func(const T& t, const U& u, tag1, tag2)\
{\
    callback_func(t, u);\
}\
\
template <class T, class U>\
void func(const T& t, const U& u, tag2, tag1)\
{\
    callback_func(u, t);\
}\

DEFINE_MULTI_FUNCTION(func_, func_baka_and_aho, baka_tag, aho_tag);
DEFINE_MULTI_FUNCTION(func_, func_baka_and_boke, baka_tag, boke_tag);
DEFINE_MULTI_FUNCTION(func_, func_aho_and_boke, aho_tag, boke_tag);

#undef DEFINE_MULTI_FUNCTION

template <class T, class U>
void func(const T& t, const U& u)
{
    func_(t, u, typename T::tag(), typename U::tag());
}

int main()
{
    func(baka(), aho());
    func(aho(), baka());
    func(baka(), boke());
    func(boke(), baka());
    func(aho(), boke());
    func(boke(), aho());
}

//各クラスの実装の詳細
std::string baka::get() const
{
    return "baka";
}

std::string aho::get() const
{
    return "aho";
}

std::string boke::get() const
{
    return "boke";
}

void func_baka_and_aho(const baka& b, const aho& a)
{
    std::cout << b.get() << " and " << a.get() << std::endl;
}

void func_baka_and_boke(const baka& ba, const boke& bo)
{
    std::cout << ba.get() << " and " << bo.get() << std::endl;
}

void func_aho_and_boke(const aho& a, const boke& b)
{
    std::cout << a.get() << " and " << b.get() << std::endl;
}

実行結果

baka and aho
baka and aho
baka and boke
baka and boke
aho and boke
aho and boke
続行するには何かキーを押してください . . .

2つの引数が異なれば問題なく動きますが、引数の型が同じ時正しく動きません。引数の型が同じ時にこのマクロを使用することができないので、この例で言うとfunc_を手書きで書く必要があります。これではとても効率が悪いです。

次回以降、MC++Dに載っている方法や、Boost.Variantを使用した方法などについて考えて行きたいです。

*1:と言うかむしろこの為だけにマルチメソッド詳しくやろうと思いました…