「続続続 template × mix-in」の編集履歴(バックアップ)一覧に戻る

続続続 template × mix-in - (2010/12/28 (火) 17:21:15) のソース

* 続続続 template と mix-in
#right{last update: &update(format=Y/m/d (D))}

さて, [[前回>続続 template と mix-in]] でテンプレートを用いた 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 { \
 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, dummy, _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 { \
 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, _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 の宣言だけはしておき,インターフェイスの定義は必要ない. mixin::c::mix の第2引数はデフォルトで mixin::c::dummy なので,単に第2引数を省略してやればよい.

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

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

 #define DEF_MIXIN_CALL_BEGIN_WITH_PROTECTED(ns, tname, seq, pseq) \
 namespace mixin { \
 namespace ns { \
 class dummy \
 { \
 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, _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

だ. Interface の protected スコープに

 void pmethod();

を書いておくのを忘れずに. Container クラスで

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

みたいに使ってくれ.

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

&trackback()
----
**参考
-[[C++でMix-inもどき>http://sato-www.cs.titech.ac.jp/isobe/mix-in.html]]
-[[template で Mix-in>http://blogs.wankuma.com/episteme/archive/2006/10/08/40928.aspx]]

----
**コメント
#comment(title_name=name,title_msg=comment)

----
**関連ページ
#related()

----
**関連ブログ
#blogsearch(mix-in)
記事メニュー
目安箱バナー