オブジェクトを生成したり、オブジェクトに必要なメモリを割り当てたり、オブジェクト内部に適切な値をセットしたり、あるいはこれらの処理の後始末を実行する関数をセットアップ API と呼ぶことにします。
Pia のセットアップ API には、 Initialize() / Finalize(), BeginSetup() / EndSetup(), CreateInstance() / DestroyInstance(), Startup() / Cleanup() といった名前が付けられており、これらには統一された呼び出し順の規則を設けています。
ある Pia モジュールのオブジェクト群を初期化するには、最初にモジュールの namespace 直下にある Initialize() 関数を実行し、続いて BeginSetup() 関数、シングルトン作成関数/クラスの Initialize() 関数、 EndSetup() 関数を呼び出していきます。
シングルトン作成関数やクラスの Initialize() 関数呼び出しは、 Pia 内部のメモリ確保処理を伴うことがあります。そのため、 Pia のメモリ管理システムの都合上、これらの関数呼び出しは BeginSetup() と EndSetup() で挟むことが必要です。
Pia のクラスには、 Startup() メンバ関数が実装されているものがあります。Startup() は、オブジェクト内部の値を適切に設定するなどして、オブジェクトを利用可能な状態にする関数です。
なお、 Startup() の実行に際しては、 Pia 内部でメモリ確保が発生することはありません。Startup() は、対応するオブジェクトやモジュールの初期化処理が済んでいる状態で呼び出されることを想定しています。
Pia のクラスには、 Cleanup() メンバ関数が実装されているものがあります。Cleanup() は Startup() と対をなす関数であり、オブジェクト内部の値を無効な値に設定するなどして、オブジェクトを利用できない状態にします。
なお、 Cleanup() の実行に際しては、 Pia 内部でメモリ解放が発生することはありません。
Pia の終了処理は、 Finalize() 関数や DestroyInstance() 関数などによって行われます。これらはそれぞれ Initialize() 関数、 CreateInstance() 関数と対をなす関数です。
これらの終了処理関数呼び出しは、初期化処理関数の呼び出し順とは逆順に実行する必要があります。
前述した初期化処理、スタートアップ、クリーンアップ、終了処理の一連のサンプルコードを以下に示します。
// module_x モジュール初期化処理
{ nn::pia::module_x::Initialize(); nn::pia::module_x::BeginSetup(); // 一般に、 CreateInstance() によるシングルトン作成は、内部でメモリ確保が発生します。 // そのため、 CreateInstance() は BeginSetup() ~ EndSetup() 間で呼び出す必要があります。 nn::pia::module_x::ClassA::CreateInstance(); nn::pia::module_x::ClassB::CreateInstance(); nn::pia::module_x::EndSetup(); } // module_y モジュール初期化処理 { nn::pia::module_y::Initialize(); nn::pia::module_y::BeginSetup(); nn::pia::module_y::ClassC::CreateInstance(); nn::pia::module_y::ClassD::CreateInstance(); // Pia には Initialize() メンバ関数呼び出しが必要なクラスもあります。 // Initialize() は、 Pia 内部でメモリ確保が発生しうる関数なので、 // BeginSetup() ~ EndSetup() 間で実行する必要があります。 nn::pia::module_y::ClassD::GetInstance()->Initialize(); nn::pia::module_y::EndSetup(); } // Startup() 呼び出しで、 ClassC のオブジェクトを利用可能な状態にします。 nn::pia::module_y::ClassC::GetInstance()->Startup(); // ゲームのメインループ。 while(1) { ... } // Cleanup() 呼び出しで、 ClassC のオブジェクトを利用不可能な状態にします。 nn::pia::module_y::ClassC::GetInstance()->Cleanup(); // module_y モジュール終了処理。初期化時とは逆順に実行します。 { nn::pia::module_y::ClassD::GetInstance()->Finalize(); nn::pia::module_y::ClassD::DestroyInstance(); nn::pia::module_y::ClassC::DestroyInstance(); nn::pia::module_y::Finalize(); } // module_x モジュール終了処理。初期化時とは逆順に実行します。 { nn::pia::module_x::ClassB::DestroyInstance(); nn::pia::module_x::ClassA::DestroyInstance(); nn::pia::module_x::Finalize(); } |
Pia モジュールの初期化処理は、
1. common
2. inet または local または lan
3. transport
4. sync、chat、clone、reckoning
5. session
の順序で実行する必要があります。
Pia モジュールの終了処理は、この逆順に実行する必要があります。
common 、 module_x 、 module_y の順にモジュールの初期化/セットアップを実行した後で、 module_x モジュールのみ終了処理を実行し、新たに module_z モジュールを初期化/セットアップするといった使用方法は禁止されています。
各モジュールの BeginSetup()/EndSetup() は、必ずセットで両方とも呼ぶ必要があります。 EndSetup() を呼び忘れた場合の動作はサポートしていません。
また、 Pia モジュールの終了処理の後で、他の Pia モジュールの(終了処理以外の) API を呼び出してはいけません。終了処理によって解放されたオブジェクトを Pia API が参照してしまい、プログラムがクラッシュする危険があるためです。
全 Pia モジュールの終了処理は、最後にまとめて実行する必要があります(参考: 1.10. 用語説明)。
// ここでは悪い例を提示しています。
// common モジュールに属する Dispatch() を session モジュールの終了処理である Session::DestroyInstance() の // 後で呼び出してはいけません。 // このように順序を誤ってしまうと、プログラムはアクセス違反などで // クラッシュする危険があります。 nn::pia::session::Session::DestroyInstance(); . . . nn::pia::common::Scheduler::GetInstance()->Dispatch(); |
前述したとおり、 Pia のモジュールやクラスには、オブジェクトの後始末や終了処理を実行する API として、 Cleanup() / Finalize() 関数が用意されています。
これらの関数は、それぞれ対応する Startup() / Initialize() 関数が実行されていなかったり、あるいは実行が失敗していた状態で呼び出されたとしても、常に成功するように設計されています。
これにより、セットアップ処理の途中で失敗した場合の後始末処理をアプリケーションプログラマが記述する手間を軽減しています。