まず、画像などを一元管理するために、次のような形式のテキストファイルを考えます。(内容に意味は特にありません。)
【image_list.txt】
background001:bg001.png
character001:char001.png,12,3,4,32,32
「画像ID:パラメータ」という並びになっています。
まずは、ファイルから文字列を読み込んで、文字列を分割するために、以下のような関数を作っておきます。
【utility.hpp】
#pragma once
#include
namespace a5ua
{
//// sourceをdelimiterごとに区切り、destを通して出力する
template
void tokenize(const StrT &source, const StrT &delimiter, OutIt dest)
{
// delimiterが空ならsourceそのものを返して終了
if (delimiter.empty()) {
*dest = source;
return;
}
StrT::size_type offset= 0; // 探索位置
StrT::size_type pos; // delimiterの位置
while ((pos = source.find(delimiter, offset)) != StrT::npos) {
// delimiterが見つかったら、文字を区切って出力
*dest = source.substr(offset, pos - offset);
// 探索位置を更新
offset = pos + delimiter.length();
}
// delimiterが見つからなかったら、残り全部を出力
*dest = source.substr(offset);
}
//// delimiterが文字列リテラルの場合
template
void tokenize(const std::basic_string &source, const CharT *delimiter, OutIt dest)
{
tokenize(source, std::basic_string(delimiter), dest);
}
}
なぜ、このようなクラスを作るかというと、どんなオブジェクトも、IDを表す文字列から、生成できるようになるからです。
【Factory.hpp】
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include "utility.hpp"
namespace a5ua
{
//// ファクトリの基底クラス
template
class BasicFactory
{
protected:
typedef std::basic_string tstring;
typedef std::map database_type;
public:
// 使用するデータベースファイル名を指定
// データベースの形式は、「id:value」を想定
BasicFactory(const TCHAR *database_name)
{
std::basic_ifstream ifs(database_name);
if (!ifs.is_open()) {
assert(false);
}
tstring line;
while (std::getline(ifs, line)) {
std::vector v;
// 「:」で区切る
a5ua::tokenize(line, _T(":"), std::back_inserter(v));
assert(v.size() == 2);
// ID二重登録のチェックをすること!
m_database.insert(std::make_pair(v[0], v[1]));
}
}
virtual ~BasicFactory()
{
}
// idを指定して新しいオブジェクトを作る
virtual T *create(const TCHAR *name) const = 0;
protected:
database_type m_database;
};
}
【Store.hpp】
#pragma once
#include
#include
#include
#include
#include "Factory.hpp"
namespace a5ua
{
// 実体のポインタをdeleteできないように
// Handleを介したポインタへのアクセス機能だけを提供する
template
class Handle
{
public:
Handle(T *body = 0) : m_body(body)
{
}
T *operator->()
{
return m_body;
}
const T *operator->() const
{
return m_body;
}
private:
T *m_body;
};
// オブジェクトの倉庫
template
class Store
{
private:
typedef std::map map_type;
public:
// Tをcreateするファクトリを指定
Store(std::auto_ptr> factory) : m_factory(factory)
{
}
// 保管オブジェクト全削除
~Store()
{
for (map_type::const_iterator itr = m_map.begin(); itr != m_map.end(); ++itr) {
delete itr->second;
}
}
// 名前を指定して削除
void release(const TCHAR *name)
{
map_type::const_iterator pos = m_map.find(name);
if (pos != m_map.end()) {
delete pos->second;
m_map.erase(pos);
}
}
// 指定したIDのハンドルを取得
// もし対応するオブジェクトがない場合は、新たにオブジェクトを生成して、そのハンドルを返す
Handle get(const TCHAR *name)
{
map_type::const_iterator pos = m_map.find(name);
if (pos != m_map.end()) {
return pos->second;
}
T *new_object = m_factory->create(name);
m_map[name] = new_object;
return Handle(new_object);
}
private:
Store(const Store &);
Store &operator=(const Store &);
map_type m_map;
std::auto_ptr> m_factory;
};
}
今回は、具体的なサンプルソースはなしです。後編へ続く。