スポンサーサイト

--年--月--日 --:--

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

静的なキャストを利用したテンプレート

2006年10月10日 16:29

Closure のこと書きたいんだけど、書くのが長くなりそうであまり気が進みません('A`)

ってことで、微妙に近いようで遠いい C++ 言語のトリックのこと書こうと思ってます。
トリックって言っても、テンプレートのトリックなんだけどね(・∀・)


んで、今日書くことは、「テンプレートで静的にスーパークラスへのキャストを行い、静的に呼び出す関数などを置換しちゃう方法」です。


・・・まぁ、上の一文で判っちゃう天才はいないと思うので具体的に例を挙げましょう。
通常、あるクラスのポインタを引数とする関数を呼ぶ時に、そのクラスから派生したクラスを渡すことも可能です。
(仮想継承や多重継承は除く)
たとえば、、、



class A
{
  ・・・
};


class B : public A
{
  ・・・
};


void TestFunc( A * )
{
}


・・・

B *p = new B();
TestFunc( p );  ← キャストしなくてもOK暗黙のキャストが適応される




ってことです。

TestFunc を呼ぶとき、引数として渡す値は A の派生クラスの実体なら別にAにキャストしなくてもOKなワケです。


しかし、特殊化したテンプレートで似たようなことをすると、キャストは明示的に行わないとダメです。

たとえば、、、



template< typename ReturnType >
struct TCFunctor
{
  virtual ReturnType operator()() const PURE;
};


template< typename ReturnType >
struct TCFuncA : public TCFunctor< ReturnType >
{
  virtual ReturnType operator()() const
  { return -1;  }
};



template< typename ReturnType >
struct TCFuncB : public TCFunctor< ReturnType >
{
  virtual ReturnType operator()() const
  {  return 1;  }
};


template< typename ReturnType >
struct PatternTest
{
  template< typename T >
  struct Absorber
  {
    static ReturnType Execute( const T &t )
    {  return 0;  }
  };


  template< typename T >
  struct Absorber< TCFunctor< T > >
  {
    static ReturnType Execute( const TCFunctor< T > &t )
    {  return t();  }
  };


  template< typename T >
  static ReturnType Test( const T &t )
  {  return Absorber< T >::Execute( t ); }
};


・・・

TCFuncA< int >  HogeA;
TCFuncB< int >  HogeB;

printf( "%d\n", PatternTest< int >::Test(HogeA) );
printf( "%d\n", PatternTest< int >::Test(HogeB) );



これを実行した時、表示されるのは


0
0



となります。

TCFuncA と TCFuncB で実装した operator () を呼んで欲しいのですが呼ばれていません。
明示的にキャストしていないため、TCFunctor< T > に特殊化した PatternTest::Absorber の方の Execute を呼んでくれないためです。


さて、ではどうするか?

一番簡単な答えは、 TCFuncA、TCFuncB それぞれの PatternTest::Absorber の特殊化を用意することです。
これなら間違いなく、それぞれが実装した operator () を呼んでくれます。


しかし、これだと TCFunctor の派生クラスが増えるごとに PatternTest クラスに特殊化を記述しないといけません。
これだとはっきり言って面倒な上、万が一追加し忘れた場合、バグの原因にもなりかねません。


実は呼ぶ方法自体はあります。



template< typename T >
static ReturnType Test( const T &t )
{  return Absorber< T >::Execute( (const < TCFunctor< ReturnType > &)t ); }


  
または


printf( "%d\n", PatternTest< int >::Test((TCFunctor< int > &)HogeA) );
printf( "%d\n", PatternTest< int >::Test((TCFunctor< int > &)HogeB) );



などのようにキャストしてやればOKです。
しかしそれぞれに問題点があります。

まず、上の方法では TCFunctor から派生したクラス以外の「型」が来た時、間違いなくエラーが起きます。
当たり前ですね。違う型のポインタを強引にキャストしてるんですもん。

下の方法は特に問題はありませんけど、いちいち明示的にキャストしてやらないといけなくなり面倒です。
また、キャストの記述を忘れちゃうとこれまた予想外の処理にまわされる可能性があります。
そんなわかりきったものは暗黙的で安全にやってもらったほうがいいに決まってます。


ってことで、暗黙的に(実際は明示的にやるしか方法はないんだけど、プログラマーが触る辞典では暗黙のうちに)処理される方法を書きます。


結局、明示的にキャストしてやるしか方法はありませんので、これを応用します。
要するに、実際に渡されたクラスが TCFunctor から派生されたのかどうか判別できればいい訳です。

C# とかと違って、普通は出来ないんですけどね。
(C# は確か super っていう this みたいなヤツあるよね?)
ここでトリックを使うと出来ちゃいます。
・・・ちょっと制約食らうけど('A`)


さてやり方は、、、



template< class TDST, class TSRC >
class CastCheck
{
private:
  typedef char (&yes)[1];
  typedef char (&no) [2];

  static yes isCast(TDST *);
  static no  isCast(...);

public:
  enum
  {  value = sizeof( isCast( (TSRC *)NULL ) ) == sizeof(yes)   };
};





こんなクラスを用意します。
あるけ が自作したクラスです('A`)

sizeof で出来るかどうかをチェックしてるところがポイントです。
sizeof で関数を呼び出すことにより、存在するかしないかをチェックしてるわけです。
sizeof はコンパイラが処理するので関数の実体が呼ばれることがないためです。


効果は、そのものズバリ!「キャストできるかどうかチェックするクラス」です。

ためしに


struct A
{
};


struct B : public A
{
};


struct C
{
};


・・・

printf( "%d\n", CastCheck< A, B >::value );
printf( "%d\n", CastCheck< A, C >::value );



ってやってみてください。


1
0


と表示されると思います。


このクラスを利用すれば「TCFunctor から派生したかどうか?」をチェックすることが出来ます。

あと、もう一つ、「フラグをみて静的に2つの型のうち1つを選択する」機能も必要ですね。
TCFunctor の派生クラスじゃ無い場合はそのまま T を渡して欲しい時に使用するためです。

このクラスは Boost などでもおなじみの Select テンプレートを使用します。


template < bool flag, typename T, typename U >
struct Select
{
private:
  template< bool >
  struct In
  { typedef T Result; };

  template< >
  struct In< false >
  { typedef U Result; };

public:
  typedef typename In< flag >::Result Result;
};




これで、上手く処理できそうです。
それじゃ本題に戻って、上手く動作するように変更してみましょう。




template< typename ReturnType >
struct PatternTest
{
  ・・・
  
  template< typename T >
  static ReturnType Test( const T &t )
  {  return Absorber
      <
        typename Select< CastCheck< TCFunctor< ReturnType >, T >::value, TCFunctor< ReturnType >, T >::Result
      >::Execute
      (
        (const typename Select< CastCheck< TCFunctor< ReturnType >, T >::value, TCFunctor< ReturnType >, T >::Result &)t
      );
  }
};




さて、上の部分を修正して実行してみましょう。
見事


-1
1



と表示されると思います。



blog_rank
スポンサーサイト


コメント

  1.  失礼いたします。
     ランキングサイト「ベストランキング」の管理人です。
     
     このたび、ランキングサイト「ベストランキング」を新規開設いたしました。
     
     新規ランキングサイトですので、今ご登録をいただければ、ランキングの上位に入るチャンスです。
     
     この機会にぜひともご登録いただけますよう、お願いを申し上げます。

    <ベストランキング・新規登録ページ>
    http://bestrank.bz/regist.cgi

     あなた様のサイトがランキングに参加していただき、相互発展できれば幸いでございます。
     それでは、よろしくお願い申し上げます。

    --------------------------------------
    総合ランキングサイト・ベストランキング
    URL:http://bestrank.bz/linkrank.cgi
    Mail:info@bestrank.bz

コメントの投稿

(コメント編集・削除に必要)
(管理者にだけ表示を許可する)

トラックバック

この記事のトラックバックURL
http://angra.blog31.fc2.com/tb.php/80-21c98e3f
この記事へのトラックバック


最近の記事


上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。