squirrel_code @ ウィキ

続続続 template × mix-in

最終更新:

squirrel_code

- view
管理者のみ編集可

続続続 template × mix-in

last update: 2010/12/28 (Tue)

さて, 前回 でテンプレートを用いた Mix-in についてはほぼカタがついたはずだ.なに?ソースが長い? Mix-in を定義するたびに IMix だの Mix だのを定義するのがめんどい? なんとかならんのか,だって?

というわけで,なんとかしてみる^^.

その6


まず, template を使ったテクニックではある程度長くなるのは仕方がない.というか,前回のコードでも裏で結構頑張って短くしているのだ.なので,これを越えるためにはいろいろと禁断の秘儀^^を用いる必要がある.

こういう場面で C のプログラマが真っ先に思いつくのはプリプロセッサマクロだろう.私もそう思う.だが,今回のコードを展開するマクロを作るには,マクロが Mix-in するメソッド名のリスト(add と remove)を取り扱える必要がある.

そこでまたまたいろいろ調べていたら, boost/preprocessor という秘法を見つけた(たぶんインドの奥地かどっかで)

書いてみる.

// mixin.hpp
#include <list>      // for std::list
#include <algorithm> // for std::find

#include <boost/mpl/list.hpp>
#include <boost/mpl/iter_fold.hpp>
#include <boost/mpl/deref.hpp>
#include <boost/type.hpp>
#include <boost/preprocessor/seq/for_each.hpp>

// === ここから汎用 Mix-in フレームワーク
#define _DEF_DUMMYBASE_METHOD(r, data, elem) \
    void elem();

#define _DEF_INTERFACE_USING(r, data, elem) \
    using _BASE::elem; using _MIXIN::elem;

#define MIXIN(ns, seq) \
namespace mixin { \
namespace ns { \
class idummy {}; \
template <typename _BASE> \
class ibase : public _BASE \
{ \
public: \
BOOST_PP_SEQ_FOR_EACH(_DEF_DUMMYBASE_METHOD, _, seq) \
}; \
template <typename _MITER, typename _BASE> \
class _IMIX1 \
    : public _BASE, public boost::mpl::deref<_MITER>::type \
{ \
    public: \
    typedef typename boost::mpl::deref<_MITER>::type _MIXIN; \
BOOST_PP_SEQ_FOR_EACH(_DEF_INTERFACE_USING, _, seq) \
}; \
template <typename _TLIST, typename _BASE = idummy> \
class imix \
    : public boost::mpl::iter_fold \
        <_TLIST, ibase<_BASE>, \
            _IMIX1<boost::mpl::_2, boost::mpl::_1> >::type \
{}; \
}}

#define _DEF_CLASS_USING(r, data, elem) \
    using _BASE::elem;

#define DEF_MIXIN_CALL_BEGIN(ns, tname, seq) \
namespace mixin { \
namespace ns { \
class dummy {}; \
template <typename _BASE> \
class base : public _BASE \
{ \
public: \
BOOST_PP_SEQ_FOR_EACH(_DEF_DUMMYBASE_METHOD, _, seq) \
}; \
template <typename _MITER, typename _BASE> class _MIX1; \
template <typename _TLIST, typename _BASE = dummy> \
class mix \
    : public boost::mpl::iter_fold \
        <_TLIST, base<_BASE>, \
            _MIX1<boost::mpl::_2, boost::mpl::_1> >::type \
{}; \
template <typename _MITER, typename _BASE> \
class mix1 \
    : public _BASE, private boost::mpl::deref<_MITER>::type \
{ \
public: \
    typedef typename boost::mpl::deref<_MITER>::type tname; \
BOOST_PP_SEQ_FOR_EACH(_DEF_CLASS_USING, _, seq)

#define DEF_MIXIN_CALL_END \
}; \
}}
// === 汎用 Mix-in フレームワークここまで ===

class A { /* 省略 */ };
class B { /* 省略 */ };

/* Mix-in インターフェイスの定義.
 * 最初に MIXIN(名前空間名, メソッド名リスト) を宣言する.
 * 名前空間名は,同名メソッドを持つ Mix-in の系列に対し,一意に決める.
 */
MIXIN(c, (add) (remove))
template <typename T>
class MI
{
public:
    virtual ~MI() {}
    virtual void add(T* t) = 0;
    virtual void remove(T* t) = 0;
};

/** Mix-in の実装の定義.
 * 最初に MIXIN_CALL_BEGIN(名前空間名, 型名, メソッド名リスト)を宣言
 * 次に private 継承されている Mix-in のメソッドを
 * public スコープに輸出するコードを書く.
 * Mix-in した実装は型名で参照できる.
 * 輸出コードの終わりに MIXIN_CALL_END を宣言
 */
DEF_MIXIN_CALL_BEGIN(c, mixin_t, (add) (remove))
    void add(typename mixin_t::t_type* t) { mixin_t::add(t); }
    void remove(typename mixin_t::t_type* t) { mixin_t::remove(t); }
DEF_MIXIN_CALL_END
template <typename T>
class MixIn
{
    std::list<T*> ts;
public:
    typedef T t_type;
    MixIn() {}
    ~MixIn() {}
    void add(T* t);
    void remove(T* t);
};
template <typename T>
void MixIn<T>::add(T* t)
    { ts.push_back(t); }
template <typename T>
void MixIn<T>::remove(T* t)
{
    typedef typename std::list<T*>::iterator iterator;
    iterator iter = std::find(ts.begin(), ts.end(), t);
    if (iter != ts.end()) ts.erase(iter++);
}

/** Mix-in フレームワークにより,
 * mixin::名前空間名::imix と mixin::名前空間名::mix が定義されている.
 * これに Mix-in の mpl::list を渡して public 継承すればよい.
 */
using namespace mixin::c;
namespace mpl = boost::mpl;

class Interface
    : public imix<mpl::list<MI<A>, MI<B> > >
{
public:
    virtual ~Interface() {}
};

class Container
    : public mix<mpl::list<MixIn<A>, MixIn<B> >, Interface>
{
public:
    Container() {}
    ~Container() {}
};

// mixin.cpp
#include "mixin.h"

いきなりコードの変態度が上がってしまった^^.上のコードが何言ってるのかわからねーと思うが,俺も何書いてるのかわからねー.頭がどうかなりそうなんだが,使うだけなら「汎用 Mix-in フレームワーク」 としてあるところはそのままコピってヘッダファイルか何かに放り込んでしまえば良い.やるべきことは,それ以外の部分をがりがり書くだけ.

BOOST.PP により,任意個数のメソッドを持つ Mix-in に対応している.インターフェイスが別のインターフェイスを継承する場合は,mixin::c::imix の第2引数に基底インターフェイスを指定すればよい.

インターフェイスがいらなければ MIXIN の宣言もインターフェイスの定義も必要ない. mixin::c::mix の第2引数はデフォルトで mixin::c::dummy なので,単に第2引数を省略してやればよい.

注意点としては, mixin::c 名前空間の中にクラス名 idummy, dummy, 及びクラステンプレート名 ibase, imix, base, mix が展開される.ここで名前の輸出コードは mixin::c 中に展開されるので,この中で他で定義した idummy とかを使っているとおかしなことになる.問題になる場合はよそで定義したほうのクラスを名前空間に入れて,修飾名で指定してほしい.

また引数を取らないメソッドには対応していない.mix の中で各 Mix-in の完全修飾名を typedef することも考えたんだが,ちょっとめんどそうなので,ダミーの引数を取るとかで回避してくれ.

ついでに, Mix-in が protected なメソッドを提供する場合のフレームワークも提供しておこうか(あまり使わないと思うが).

#define DEF_MIXIN_CALL_BEGIN_WITH_PROTECTED(ns, tname, seq, pseq) \
namespace mixin { \
namespace ns { \
class dummy {}; \
template <typename _BASE> \
class base : public _BASE \
{ \
public: \
BOOST_PP_SEQ_FOR_EACH(_DEF_DUMMYBASE_METHOD, _, seq) \
protected: \
BOOST_PP_SEQ_FOR_EACH(_DEF_DUMMYBASE_METHOD, _, pseq) \
}; \
template <typename _MITER, typename _BASE> class _MIX1; \
template <typename _TLIST, typename _BASE = dummy> \
class mix \
    : public boost::mpl::iter_fold \
        <_TLIST, base<_BASE>, \
            _MIX1<boost::mpl::_2, boost::mpl::_1> >::type \
{}; \
template <typename _MITER, typename _BASE> \
class _MIX1 \
: public _BASE, \
  private boost::mpl::deref<_MITER>::type \
{ \
public: \
    typedef typename boost::mpl::deref<_MITER>::type tname; \
protected: \
BOOST_PP_SEQ_FOR_EACH(_DEF_CLASS_USING, _, pseq) \
public : \
BOOST_PP_SEQ_FOR_EACH(_DEF_CLASS_USING, _, seq)

メソッド void pmethod(T* t) が protected なメソッドとして,使い方は

DEF_MIXIN_CALL_BEGIN_WITH_PROTECTED(c, mixin_t, (add) (remove), (pmethod))
    void add(typename mixin_t::t_type* t) { mixin_t::add(t); }
    void remove(typename mixin_t::t_type* t) { mixin_t::remove(t); }
protected:
    void pmethod(typename mixin_t::t_type* t) { mixin_t::pmethod(t); }
DEF_MIXIN_CALL_END

だ. Container クラスで

template <typename T>
void method(T* t) { pmethod(t); }

みたいに使ってくれ.

もう問題は残ってない・・・よな?

&trackback()

参考



コメント

  • コメントの投稿テスト -- (tossy_squirrel) 2010-12-29 03:35:18
名前:
コメント:

すべてのコメントを見る


関連ページ



関連ブログ

#blogsearch
記事メニュー
人気記事ランキング
目安箱バナー