#ifndef oo_ObjSelector_H__
#define oo_ObjSelector_H__
#include "ObjBase.h"
#include <utility>
#include <vector>
#include <string>
#include <map>
#include <cstdlib>
#include <cstdio>
namespace meow{
/*!
* @brief 利用register的概念, 達到runtime用string選擇要new的class
*
* @author cathook
*/
template<size_t id> //!< 讓程式可以有不只一個 \c ObjSelector
class ObjSelector {
private:
struct Info {
ObjSelector* parent_;
ObjBase const* pointer_;
bool autoDelete_;
//
Info(ObjSelector* parent,
ObjBase const* ptr,
bool autoDelete) {
parent_ = parent;
pointer_ = ptr;
autoDelete_ = autoDelete;
}
~Info() {
if (autoDelete_) {
delete pointer_;
}
if (parent_ != NULL) {
parent_->me_.second = NULL;
}
}
};
friend struct Info;
typedef typename std::map<std::string, Info*> Funcs;
typedef typename std::map<std::string, Info*>::iterator FuncsIterator;
static Funcs& funcs() {
static Funcs f;
return f;
}
static Info* add(std::string name,
ObjSelector* parent,
ObjBase* ptr,
bool autoDelete) {
Info* info = new Info(parent, ptr, autoDelete);
del(name);
funcs()[name] = info;
return info;
}
std::pair<std::string, Info*> me_;
public:
/*!
* @brief 新增(註冊) 一個Class (必須要繼承自 \c ObjBase) 並且給定其Name
*/
static void add(std::string name, ObjBase* obj, bool autoDelete) {
add(name, NULL, obj, autoDelete);
}
/*!
* @brief 新增(註冊) 一個Class (必須要繼承自 \c ObjBase) 並且默認type為name
*/
static void add(ObjBase* obj, bool autoDelete) {
add(obj->type(), NULL, obj, autoDelete);
}
/*!
* @brief 依照name刪除之前註冊過得Class
*/
static void del(std::string name) {
if (funcs().find(name) != funcs().end()) {
delete funcs()[name];
funcs().erase(name);
}
}
/*!
* @brief 取得之前註冊過得Class
*/
static ObjBase const* get(std::string name) {
if (funcs().find(name) == funcs().end()) return NULL;
return funcs()[name]->pointer_;
}
/*!
* @brief 回傳一個之前註冊過得Class new出來的實體
*/
static ObjBase* create(std::string name) {
ObjBase const* ptr = get(name);
if(ptr == NULL) return NULL;
return ptr->create();
}
/*!
* @brief 利用type檢查是否有註冊過同種類的Class
*/
static bool exist(ObjBase* obj) {
for (FuncsIterator it = funcs().begin(); it != funcs().end(); it++) {
if (it->second->pointer_ == obj ||
(it->second->pointer_ != NULL &&
it->second->pointer_->type() == obj->type())) {
return true;
}
}
return false;
}
/*!
* @brief 利用type尋找name
*/
static std::string name(ObjBase* obj) {
for (FuncsIterator it = funcs().begin(); it != funcs().end(); it++) {
if (it->second->pointer_ == obj ||
(it->second->pointer_ != NULL &&
it->second->pointer_->type() == obj->type())) {
return it->first;
}
}
return std::string();
}
/*!
* @brief 回傳所有註冊過的name
*/
static std::vector<std::string> names() {
std::vector<std::string> ret;
for (FuncsIterator it = funcs().begin(); it != funcs().end(); it++)
ret.push_back(it->first);
return ret;
}
/*!
* @brief 宣告一個ObjSelector實體, 並且註冊一個 ObjBase
*/
ObjSelector(std::string name, ObjBase* obj, bool autoDelete) {
me_.first = name;
me_.second = add(me_.first, this, obj, autoDelete);
}
/*!
* @brief 宣告一個ObjSelector實體, 並且註冊一個 ObjBase
*/
ObjSelector(ObjBase* obj, bool autoDelete) {
me_.first = obj->type();
me_.second = add(me_.first, this, obj, autoDelete);
}
//! 解構子
~ObjSelector() {
if (me_.second != NULL) {
del(me_.first);
}
}
/*!
* @brief 將一個物件寫到檔案裡(該物件必須要有註冊過)
*/
static bool write(FILE* f, bool binary, ObjBase* obj, unsigned int fg) {
if (!exist(obj)) return false;
char const* nme = name(obj).c_str();
size_t len = strlen(nme);
if (binary) {
if (fwrite(&len, sizeof(size_t ), 1, f) < 1) return false;
if (fwrite(nme , sizeof(char ), len, f) < len) return false;
if (fwrite(&fg , sizeof(unsigned int), 1, f) < 1) return false;
} else {
if (fprintf(f, "%s %u\n", nme, fg) < 2) return false;
}
return obj->write(f, binary, fg);
}
/*!
* @brief 從檔案中讀取一個物件(該物件必須要有註冊過)
*/
static ObjBase* read(FILE* f, bool binary) {
char name[2048];
size_t len;
unsigned int fg;
if (binary) {
if (fread(&len, sizeof(size_t ), 1, f) < 1) return NULL;
if (fread(name, sizeof(char ), len, f) < len) return NULL;
if (fread(&fg , sizeof(unsigned int), 1, f) < 1) return NULL;
name[len] = '\0';
} else {
if (fscanf(f, "%s %u", name, &fg) < 2) return NULL;
}
ObjBase* ret = create(std::string(name));
if (ret != NULL && ret->read(f, binary, fg) == false) {
delete ret;
ret = NULL;
}
return ret;
}
};
static const size_t kGlobalSeletorID = 0;
}
#endif // oo_ObjSelector_H__