多相型のアプローチ多相型を扱う関数のコンパイルはいつ行うか?? データサイズは分からない 使用するレジスタは?? 適用されるところで展開するとコードサイズが大きくなる 分割コンパイルとの相性 分割するとなると、多相型をどうするか? (boxing 戦略は??) いっそまとめてコンパイル(コンパイラ自身のメモリ使用量の問題) 速いコード生成にはまとめてコンパイル単相化が一番 データの表現 universalなデータ表現だとデータ表現が制限される。変換が必要 taggingのアプローチ Traditional Tagging Lifted Tagging Separated Tagging
リンクad hoc overloadingや、型を調べる関数(schemeのinteger? など)を使って型にあった コードを実行する。型ごとにコードを書くので型によって動作は異なりうる。parametric 型によらずに一意な動作をする。
テンプレートの特別化された定義を一回のみにする際のアプローチC++テンプレート関数を分割コンパイルするための変換アプローチBorland model 各々のオブジェクトファイルにテンプレートを特化したコードをいれる。リンカで 削除。
利点簡単。 欠点コンパイル時間が長い(テンプレート関数は何度もコンパイルされるが、リンクで 破棄される) リンカがこの機能に対応しなければならない Cfront model (AT&T) g++ではテンプレート関数は適用されている型に特化したコードを生成している。 例えばchar型のid関数がファイルa.cc,b.ccで適用されている場合、これらを 分割してコンパイルすると、a.o,b.oにid関数のchar型特化の関数が生成されて いる。a.oとb.oをリンクするときにid関数のchar型特化の関数は2度定義されて いるが、この関数の重複を避けるのはリンカの仕事である。weakなシンボルと いうものがあって、これは既に定義されていなければ普通にリンクし、そうで なければ無視されるシンボルである。(Borland modelを採用 ld 2.8以降)repositoryを使ってどこでテンプレート関数が使われたかを管理しておく。
利点無駄なコンパイルが起こらない分速い リンカは低機能でよい 欠点repositoryの管理が複雑 コードのディレクトリ構造などが制限される(coding convension)
目指すもの参考文献失うものどれだけコンパイル、リンク時間が短縮できたか?? どれだけオブジェクトファイルのサイズを縮小できたか?? 具体的な型について特化したコードを生成するのに比べて どれだけプログラム全体のサイズが小さくなったか?? 課題実行効率は落ちる 適用する側にコードをはさむので、ひょっとしたらコードは 大きくなるかもしれない 考え方多相型関数(関数テンプレート、クラステンプレート)をそれ自身独立したコード として分割コンパイルできるようにする boxingのアプローチ C++の関数の呼び出し順序!? メソッド起動 overloadにも対応 多相型をもつものを定義する側は、1 wordのboxedデータを受け取るものとして、 コンパイルする。適用する側で型の情報を使ってboxing,unboxingをするコードを いれる。実装これからboxingはオブジェクトをヒープに割り当て、それへのポインタをwrapped_t (= void*) にキャストする。unboxingはwrapped_tが指すオブジェクトをターゲットとなる 型のオブジェクトにキャストしその中身をかえす(dereference) メソッド起動は純粋仮想クラスと多重継承により解決する予定 ->overloadは関数を引数として受渡し。-> 多相関数内部から型多相なオブジェクトに対して定義されている関数が 見えない。つまり型多相なオブジェクトについては使われ方を解析して必要な情報を 受け取る必要がある。(「overloadならば」ではダメ)
-> 引数として渡すと、ある型多相なオブジェクトobjに対して、実装の なかで今まで使われていなかった関数 funcを呼ぶとそれを引数に付け加える必要が ある。そうなると、実装が(変換後の)インターフェイスに影響し、そのテンプレート 関数を呼ぶ側に波及し、好ましくない。この波及は引数の数の問題というだけでな く、on needで必要な関数のみを受渡ししていることから生じている。テンプレートの 実装である関数 Fを新たに必要とするようになったので使用する側でもそれを、 渡してくださいという、定義 - 使用での約束が問題をうむ。この問題を解消するには テンプレート関数内部から「ある引数の型リストと関数名を渡すとそれに対応する 関数本体(関数へのポインタ)をくれるもの」が必要である。表の形にして、テンプレート 関数内部でひくことにする。その表に全ての関数を(テンプレート関数内部で実際に 使われる関数で十分であるが)登録しておく。フィールド参照はメソッド起動に置き換える。ここで欲しかったものはフィールドの インターフェイス。「xxという名前のフィールドのあるクラス(レコード)」でそれ 自身は実体を持たないというものが欲しいが、C++でそんなことができるかどうかは 分からないので。 思うことクラステンプレートでメンバ変数が型多相である場合 (コンストラクタ、デストラクタ(関数と同様!?)) メモリ管理(返り値が型多相な場合newする??) .で呼び出すメソッド(これは -> で呼び出すのが良いがオブジェクトはコピー するべき。副作用は?) そもそもオブジェクトそのものを引数として渡すとどうなる?? 返り値として返すとどうなる??e コードとオブジェクトファイルをデータベースで管理して、どこにどの関数が 定義されているかをチェック。リンク情報をデータベースに与えてやれば、小さい コードを作りつつ、無駄なコンパイル作業も少なくなる開発環境が得られるのでは ないか。 関数に構造体を渡す場合にリファレンス(pointer)を渡すのがよいか、構造体自身を スタックに積んで渡すのが良いかは構造体のサイズやメンバの使われ方によりそうで ある。構造体のサイズが大きい、使用するメンバは少ない、構造体に副作用を起こし たいときはリファレンスを使う方がよい。副作用を起こさない、メンバをほぼ全て 使用する場合はスタックに構造体をコピーした方がよい(かもしれない)
メモ
GNU C extensionで可変長の配列が使えるらしい。
function(...;...) forward declaration
後になって思い付いたことのメモgccのinfo (The GNU Compiler Collection.) Greg Morrisett Thesis Proposal: Data Representations and Polymorphic Languages
デフォルトの分岐関数を用意.テンプレート関数からはそれを呼ぶ.リンク時に関数 生成.同じ名前の関数をかき集め分岐関数から分岐.分岐には型情報が必要.
名前ごとに表があるようなもの.リンカに機能追加必要
x = max(y,z); -> substitute(x,max(y,z))
テンプレート関数内部で同じ名前であり実体の違う多重定義された関数をよんでいる ことはありうるので,引数に渡す関数はリネームしなければならない.そのリネームは関数の 本体から呼び出されていない関数にリネーム.
きめうち +--リンク時に生成----------------+ +--------> | void f(wrapped_t a) { | +-------------------+ | type_t t = runtime_type(a); |---> f_int(int) | template <class T>| | switch(t) | | void call(T a) { | | case INT: |---> f_float(float) etc... | f(a); | | f_int(a); break; | | } | | case FLOAT: | +-------------------+ | f_float(a); break; | | //... | | } | +--------------------------------+