ライブラリ初期化

1 ライブラリ形式
ライブラリはダイナミックリンクライブラリ(*.dll, *.so, …)として生成します。
ライブラリ自体の開発はC++を利用しますが、なるべく多くの言語から利用可能とするため、ダイナミックリンクライブラリからエクスポートする関数は C言語 の形式とします。
ただし、デメリットとして マイクロソフトが公開しているCランタイムをインストールしていない環境では実行できないモジュール になってしまいます。
特に今回のライブラリでは前述のメリットが活きてくることはないため、ポータビリティを優先して 静的リンク します。
2 例外
C++言語はエラー伝達の仕組みとして例外が存在します。
ただしC言語形式のAPIとするため、この例外を外に伝播させてはいけません。
C++を使う以上、「気合で例外なんて使わないつもりです!!」みたいなものは基本的に不可能です…標準ライブラリ使う以上どうしようもありませんしね。
従って、全ての多言語との境界となるAPI関数は、以下のような形式になります。
もちろん、具体的な例外を補足して、挙動を変えるということもありでしょう。
C++的にはnoexceptを付けてもいいと思います。(この場合例外が境界を超えるとterminate()されます。)
int ApiFunction(...) noexcept {
try{
//各種処理
}
catch(...){
return 0;
}
return 1;
}上記は STDCALL や CDECL 等の呼び出し規約を省略していますが、X64ではどれを書いても同じだからです。
このライブラリは X64のみのサポート とするので、これでOKです。
さてさて、ゲームで使う場合、このAPI呼び出し回数はかなり多くなるはずで、try catch の速度的なコストが気になるところですよね。
技術者的には気になるはず…
Windows上において、例外は 構造化例外(SEH) というOSの仕組みを使って実装されます。
結論から言うと、少し状況依存な部分もあるのですが、例外が発生しない正常フローにおいてはほぼコストは発生しません。
(もちろん、例外が発生するとそれなりのコストが必要になるため、正常フローに例外を利用すべきでない理由の一つになっています。)
でSEHの仕組みが 32bit と 64bit で変わっていて、X64の場合は、実行時に何かすることは基本的になく、予めモジュール内部に例外に関する情報を事前登録する仕組みになっているため、速度的にはノーコストです。
※ただ、それによってアセンブリが全く同一になるかというとそうではなく、catchのコード等が存在するため、アセンブラレベルでの最適化の違いは発生します。
Win32の場合は、SEHがランタイムでの交通整理みたいなものが必要なため、メモリ上のデータ操作がコストとして必要になります。まぁ誤差ですがね。
ただしです。上記の各種処理の部分に依存していて、ここでC++的なオブジェクト等を定義すると、どこまで処理が進んだから、どれを削除しないといけないか。みたいなC++的な管理が必要になります。
従って、ここは関数呼び出しのみ。とすると、その管理コストは必要ありません。
3 初期化API
とりあえず最初はエンジンを初期化するAPIを定義します。
ただ、今のところ何もすることはないので、実装はこれから必要に応じて足していきます。
EngineをSingletonで作成して、そちらを呼び出すだけのコードです。
ovlit_bool OVLIT_Initialize() OVLIT_NOEXCEPT;
void OVLIT_Finalize() OVLIT_NOEXCEPT;ovlit_bool OVLIT_Initialize() noexcept {
try {
return Engine::GetInstance().Initialize() ? ovlit_true : ovlit_false;
}
catch (...) {
return ovlit_false;
}
}
void OVLIT_Finalize() noexcept {
try {
Engine::GetInstance().Finalyze();
}
catch (...) {
}
}