📗
Janitor(清掃員)とRAIIの実装例
概要
ゲーム開発では、動的なメモリ管理やリソース管理の煩雑さがバグの原因になることが多くあります。こうした問題を軽減する手法の一つが、リソースの確保を初期化時に行うRAII(Resource Acquisition Is Initialization) です。
このRAIIのアイデアを体現した具体的なC++クラスとして、「Janitor(清掃員)」があります。リソースの確保と管理は、それぞれクラスのコンストラクタとデストラクタで行われます。プログラマがさまざまな処理を行ったあと、このクラスが「片付けて」くれることから、Janitor(清掃員)と呼ばれています。
本題
RAIIの原則とは、「リソースの確保をオブジェクトの初期化時に、解放を破棄時に行う」ことです。
JanitorクラスはこのRAIIを応用し、アロケータコンテキストのPush/Popを自動化する例です。
ファイル構成
SimpleRAIIProject/
├── include/
│ └── mem/
│ └── Allocator.hpp // コンテキストのPush/Pop
├── src/
│ ├── AllocJanitor.hpp // Janitorクラス
│ └── main.cpp // 使用例
Allocator.hpp — グローバルなコンテキスト管理
#pragma once
#include <stack>
#include <string>
#include <iostream>
namespace mem
{
using Context = std::string;
inline std::stack<Context>& allocatorStack() {
static std::stack<Context> stack;
return stack;
}
inline void PushAllocator(const Context& ctx) {
allocatorStack().push(ctx);
std::cout << "Push: " << ctx << "\n";
}
inline void PopAllocator() {
if (!allocatorStack().empty()) {
std::cout << "Pop: " << allocatorStack().top() << "\n";
allocatorStack().pop();
} else {
std::cerr << "Warning: PopAllocator called on empty stack!\n";
}
}
inline const Context& GetCurrentAllocator() {
return allocatorStack().top();
}
}
AllocJanitor.hpp — RAIIを実現するJanitorクラス
#pragma once
#include "mem/Allocator.hpp"
class AllocJanitor {
public:
explicit AllocJanitor(mem::Context context) {
mem::PushAllocator(context);
}
~AllocJanitor() {
mem::PopAllocator();
}
AllocJanitor(const AllocJanitor&) = delete;
AllocJanitor& operator=(const AllocJanitor&) = delete;
};
main.cpp — 使用例
#include "AllocJanitor.hpp"
#include <iostream>
void DoSomething() {
AllocJanitor janitor("TemporaryContext");
std::cout << "Doing something in " << mem::GetCurrentAllocator() << "...\n";
} // janitor がスコープを抜けると自動で Pop される
int main() {
mem::PushAllocator("MainContext");
{
DoSomething(); // スコープ内で TemporaryContext を一時的に使用
}
mem::PopAllocator(); // MainContext を手動でポップ(RAIIで管理してもよい)
return 0;
}
実行例
$ cd /path/to/SimpleRAIIProject
$ g++ -std=c++20 -Iinclude src/main.cpp -o janitor_example
$ ./janitor_example
実行結果
Push: MainContext
Push: TemporaryContext
Doing something in TemporaryContext...
Pop: TemporaryContext
Pop: MainContext
参考文献
Jason Gregory. ゲームエンジン・アーキテクチャ 第3版. ボーンデジタル. 2020
Discussion