スポンサーサイト

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

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

シェーダー開発ツール 7

2006年11月17日 22:27

今日も進捗報告(・∀・)

シーンパーツ表示ツリービューを追加しました。

ShaderSmith7.jpg


これを元に描画順序を作成するわけですね。
あと、面倒なのでここでレンダリングターゲットを作成できるようにしました(´ω`)


これで後追加すればいい機能は、、、
・ディスプレイリストのエディット機能
シェーダー定数設定機能
かな。

下の方は大まかなモノはほとんど出来てます。
ってことで、上のディスプレイリストのエディット機能を追加すれば、大体完成!!


ディスプレイリストのエディット機能は Maya でいうハイパーグラフみたいなモンを作ろうと思ってます。
視覚的にもわかりやすいしね(・∀・)

・・・まだまだ先が長そうだ('A`)
今月中に出来るといいなー(´ー`)y─┛~~


blog_rank
スポンサーサイト

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

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

C++言語で関数型プログラミング 序章

2006年08月07日 17:33

以前書いたブログに Closure のこと書いてて、その記事の技術、全然公開してなかったから説明しながら公開していこうかと思います。


っていっても、Boost の Lambda みたいに綺麗になってないんだけどね。
けど機能はこっちの方が上だ!って自負がありますよこれ( ・`ω・´)

無名関数だって使えるし、遅延評価だってできます。
Haskell みたいに完全遅延評価(グラフリダクションっていうらしい。並列で遅延評価するって意味なのかな?)は出来ないけどね。
(出来たら恐ろしいけどw)



んで、本題。

やりたかったことはっていうと、

 1:動的に関数を生成する
 2:1で生成された関数も同じように動的に生成する関数の部品として再利用できちゃう
 3:無名関数みたいに、値が決まって無くてもあとで値を入れれば動いちゃう。
 
っていうことですね。
まぁ、要するに「式」をオブジェクト化したかったワケです。
「式」同士を演算子で結合できたりね。


Lambda の実装に触発されて出来たわけですけど機能は大分違いますね。

また、冒頭で Lambda みたいに綺麗になってないといいましたが、全て Template が生成する「実態」だけで済ませている Lambda と違い、このクラス(TCClosure と呼んでます)は new や malloc などで動的に確保したメモリを使用することでC言語特有の「汚い」実装になってるからです。
その代わり汎用的に出来るわけです。

実際、Lambda を有機的に使う場合、必ず Template 関数なりにしなければいけなかったところ、
Lambda の場合、型が自動的に作られるのと生成される型が複雑なので、
 とてもじゃないが指定して使うなんてことができない)
TCClosuer は戻り値だけ特定すれば、どんな関数やクラスでも利用可能です。
要するに、テンプレートの引数として必要なのは戻り値の型だけなんですね。


また、違う戻り値の TCClosure も型キャストさえ出来れば組み合わせて使う(組み合わせることで新しい TCClosure を生成する)ことが出来ます。



んで、どうやって実装しているか?
いきなり核心の部分を説明してもかなりわかりにくいです。


なんで、まずは簡単なことを考えましょう。

どうやって式をオブジェクト化するか?

これだけでもかなり抽象的ですねw

まず単純に考えて、
 a + b = f
において、
 「f の値を動的(必要な時)に求める」
が出来ればよさそうな気がします。

それじゃこれをクラス化してみましょう。


a を項クラス term とします。
そうすると、b は term クラスの保持する値が違うだけなので、同じ term クラスとして扱えます。
また、f は結果なので result クラスとします。

そうすると、a + b = f の式は
 term + term = result
と書けますね。


さて、ここで式の再利用を考えると result クラスの部分を term と置き換えれば上手く行きそうな気がします。
result = term とすれば term と term を演算子でつなぐと term を返す
これなら出来上がった term を使ってまた新しい「式」を定義できそうです。

そうです、これが Lambda で使ってる技術なんです。
Lambda の場合、全て「一つの部分」でやっちゃう
(たとえば
  ((_1 * 100 + _2 + 10)(1,2))
 など、式を変数として保存するのではなく、関数の引数として書いたり一箇所にまとめて書く)
ので、ワザワザ「式」を保存するオブジェクトを定義する必要がありません。
ですから、(Lambda の場合は)戻り値を再帰的に使って実現してるわけです。

(上の例では
 _1 * 100 で一つ戻り値が出来て(これを term1 とする)
 term1 + _2 でもう一つ戻り値が出来る(これを term2 とする)
 さらに term2 + 10 でもう一つ戻り値が出来る(これを term3 とする)
 よって、結局 term3(1,2) が呼ばれるわけです)



とりあえず今日はここまで。

次回は(いつになるかわからんけど)これの実装方法とか必要になるクラスのこととか書きます。




blog_rank



最近の記事


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