接上篇
在本系列第一篇文章中,我介绍了这个项目的Json、Lua相关设计,这篇文章中将会对基础库RJCBLib进行详细介绍。我将相关的头文件给出以供参考。
RJCBLib库包含功能(2016-3-6):
GenericValue : 通用类型。
Singleton<> : 提供单例支持。
ObjectManager<> : 提供对象管理功能,继承自Sigleton<>。
JsonBuffer : 提供对Json数据的管理,结合了cocos2d中提供的RapidJson,继承自ObjectManager<>。
LuaObject : 实现了对Lua的支持,继承自此类可以使用Lua功能。
KVObject : 以Key/Value的方式管理对象属性,可以直接解析RapidJson对象。
Matrix<> : 通过矩阵的方式管理对象。
基本定义
typedef const char* const FUNC_RETURN_TYPE;
typedef const char* FUNC_RETURN_TYPE_W;
FUNC_RETURN_TYPE FUNC_RETURN_VALUE_SUCCESS = "SUCCESS";
FUNC_RETURN_TYPE FUNC_RETURN_VALUE_FAILURE = "FAILURE";
FUNC_RETURN_TYPE FUNC_RETURN_VALUE_FOUND = "FOUND";
FUNC_RETURN_TYPE FUNC_RETURN_VALUE_NOT_FOUND = "NOT FOUND";
FUNC_RETURN_TYPE FUNC_RETURN_VALUE_EXISTED = "EXISTED";
//将某个类型转换为字符串,存在效率问题,不建议在帧中使用
template<typename T>
inline std::string to_string(const T &arg) { std::stringstream ss;ss << arg; std::string t;ss >> t;return t; }
//对常用类型进行了封装,你需要提前知道这个类的实例究竟该解释为什么类型
struct GenericValue
{
GenericValue()
{
}
GenericValue(std::string String)
{
this->String = String;
}
GenericValue(int Int)
{
this->Int = Int;
}
GenericValue(bool Bool)
{
this->Bool = Bool;
}
GenericValue(double Double)
{
this->Double = Double;
}
std::string String;
int Int;
bool Bool;
double Double;
};
定义了函数返回值、GenericValue
是对基本类型的封装。
单例支持
通过模板实现了单例:
template<typename ClassSelfType>
class Singleton
{
public:
static ClassSelfType* getInstance()
{
if (RJCBLib::Singleton<ClassSelfType>::instance == nullptr)
{
RJCBLib::Singleton<ClassSelfType>::instance = new ClassSelfType();
dynamic_cast<RJCBLib::Singleton<ClassSelfType>*>(RJCBLib::Singleton<ClassSelfType>::instance)->init();
}
return RJCBLib::Singleton<ClassSelfType>::instance;
}
~Singleton()
{}
protected:
Singleton()
{}
virtual void init()//如果被单例的类override了这个函数,那么它将会在第一次创建单例的时候被调用
{
}
private:
static ClassSelfType* instance;//单例的对象指针
template<typename classSelfType>
class Automatic//自动释放类,待测试
{
private:
Automatic() {
}
~Automatic() {
delete RJCBLib::Singleton<classSelfType>::instance;
}
};
static Automatic<ClassSelfType> automatic;
static int a;
};
template<typename ClassSelfType> ClassSelfType *RJCBLib::Singleton<ClassSelfType>::instance = nullptr;
如何使用:
class A : public RJCBLib::Sigleton<A>
{
};
--------------
A* instance = A::getInstance();
在继承这个模板的时候将继承类A
自身传入这个模板,会为A
生成一个基类Sigleton<A>
。A
的单例被创建时会调用虚函数Sigleton<A>::init
,可以在A
中重写这个虚函数以实现初始化的相关工作。Automatic
内部类实现了单例的自动释放。
对象管理
继承自Sigleton<>
与cocos2d::Map<>
。
template<typename ClassSelfType, typename KeyType, typename ValueType_MustBePointerToRef>
class ObjectManager : private cocos2d::Map<KeyType, ValueType_MustBePointerToRef>, public RJCBLib::Singleton<ClassSelfType>
{
public:
//获取被管理KV的数量
SSIZE_T size()
{
return Map<KeyType, ValueType_MustBePointerToRef>::size();
}
//清空所有KV
RJCBLib::FUNC_RETURN_TYPE clear()
{
Map<KeyType, ValueType_MustBePointerToRef>::clear();
return RJCBLib::FUNC_RETURN_VALUE_SUCCESS;
}
//插入一个KV,K应未被使用过
RJCBLib::FUNC_RETURN_TYPE insert(KeyType key, ValueType_MustBePointerToRef value)
{
if (this->has(key) == RJCBLib::FUNC_RETURN_VALUE_FOUND)
{
return RJCBLib::FUNC_RETURN_VALUE_EXISTED;
}
Map<KeyType, ValueType_MustBePointerToRef>::insert(key, value);
return RJCBLib::FUNC_RETURN_VALUE_SUCCESS;
}
//获取指定K的V
RJCBLib::FUNC_RETURN_TYPE get(KeyType key, ValueType_MustBePointerToRef &outValue)
{
auto ite = Map<KeyType, ValueType_MustBePointerToRef>::find(key);
if (ite == Map<KeyType, ValueType_MustBePointerToRef>::end())
{
outValue = nullptr;
return RJCBLib::FUNC_RETURN_VALUE_NOT_FOUND;
}
outValue = (*ite).second;
return RJCBLib::FUNC_RETURN_VALUE_SUCCESS;
}
//移除指定K所对应的KV
RJCBLib::FUNC_RETURN_TYPE remove(KeyType key)
{
if (Map<KeyType, ValueType_MustBePointerToRef>::find(key) == Map<KeyType, ValueType_MustBePointerToRef>::end())
{
return RJCBLib::FUNC_RETURN_VALUE_NOT_FOUND;
}
Map<KeyType, ValueType_MustBePointerToRef>::erase(key);
return RJCBLib::FUNC_RETURN_VALUE_SUCCESS;
}
//是否包含某个K
RJCBLib::FUNC_RETURN_TYPE has(KeyType key)
{
if (Map<KeyType, ValueType_MustBePointerToRef>::find(key) == Map<KeyType, ValueType_MustBePointerToRef>::end())
{
return RJCBLib::FUNC_RETURN_VALUE_NOT_FOUND;
}
return RJCBLib::FUNC_RETURN_VALUE_FOUND;
}
//返回cocos2d::Map的begin迭代器
auto begin()
{
return Map<KeyType, ValueType_MustBePointerToRef>::begin();
}
//返回cocos2d::Map的end迭代器
auto end()
{
return Map<KeyType, ValueType_MustBePointerToRef>::end();
}
virtual ~ObjectManager() {}
protected:
ObjectManager() {}
};
如何使用:
class A : public RJCBLib::ObjectManager<A,std::string,std::string>
{
};
------------
把它当做单例的Map用吧。
A继承自ObjectManager,传入三个模板类型:A自身(因为继承自Sigleton<>)、Key类型、被管理者类型。实际上A就是一个单例的Map。
Json数据管理
继承自ObjectManager<>。
class JsonBufferDocumentBox : public cocos2d::Ref
{
public:
static JsonBufferDocumentBox *create();
rapidjson::Document& operator*();
rapidjson::Document* operator->();
private:
JsonBufferDocumentBox();
~JsonBufferDocumentBox();
rapidjson::Document* document;
};
class JsonBuffer : public RJCBLib::ObjectManager<JsonBuffer,std::string, JsonBufferDocumentBox*>
{
public:
JsonBuffer();
FUNC_RETURN_TYPE parseFile(std::string key, std::string filePath);
FUNC_RETURN_TYPE parseString(std::string key, std::string data);
};
辅助类JsonBufferDocumentBox
封装了rapidjson::Document
,因为它继承自cocos2d::ref
,所以可以对它进行内存管理,这也是cocos2d::Map<>
要求的。
JsonBuffer
继承自ObjectManager<>
,提供了两个函数分别解析字符串和文件,解析后的内容和可以通过基类ObjectManager<JsonBuffer,KeyType,ValueType>
的方法获取。
Lua支持
我没有使用cocos2d自带的Lua功能而是根据需要自己实现了Lua接口相关的工作。尚有很多不足(如线程安全)有待修改,也请读者多提宝贵意见。
#define MAKE_LUA_FUNCTION_NAME(_LUA_ID) (std::string("UpdateFunction")+_LUA_ID)
class LuaObject
{
private:
//唯一标识的id,在lua虚拟机中不同的对象会通过这个id区别
static unsigned int _lua_id;
//是否已被初始化
bool isInited;
//
std::string realFunctionName;
//生成新的LuaID
inline unsigned int generateLuaID()
{
++_lua_id;
return _lua_id;
}
//当前活动的LuaObject,因为会有许多LuaObject,这个变量用于和Lua虚拟机交互
static LuaObject * currentLuaObject;
protected:
LuaObject();
virtual ~LuaObject();
//override这个函数,返回脚本文件路径
virtual std::string getLuaScriptFilePath() = 0;
//文件中Update函数的名字
virtual std::string getUpdateFunctionName() = 0;
//override这个函数,lua虚拟机查询String时会被调用
virtual std::string LuaObjectGetString(std::string key) = 0;
//override这个函数,lua虚拟机查询Int时会被调用
virtual int LuaObjectGetInt(std::string key) = 0;
//override这个函数,lua虚拟机查询Double时会被调用
virtual double LuaObjectGetDouble(std::string key) = 0;
//override这个函数,lua虚拟机查询Bool时会被调用
virtual bool LuaObjectGetBool(std::string key) = 0;
//override这个函数,lua虚拟机设置String时会被调用
virtual void LuaObjectSetString(std::string key, std::string value) = 0;
//override这个函数,lua虚拟机设置Int时会被调用
virtual void LuaObjectSetInt(std::string key, int value) = 0;
//override这个函数,lua虚拟机设置Bool时会被调用
virtual void LuaObjectSetBool(std::string key, bool value) = 0;
//override这个函数,lua虚拟机设置Double时会被调用
virtual void LuaObjectSetDouble(std::string key, double value) = 0;
//虚拟机回调的日志函数
static int Log(lua_State *L);
//虚拟机回调的获取String函数
static int StringGet(lua_State *L);
//虚拟机回调的获取Int函数
static int IntGet(lua_State *L);
//虚拟机回调的获取Bool函数
static int BoolGet(lua_State *L);
//虚拟机回调的获取Double函数
static int DoubleGet(lua_State *L);
//虚拟机回调的设置String函数
static int StringSet(lua_State *L);
//虚拟机回调的设置Bool函数
static int IntSet(lua_State *L);
//虚拟机回调的设置Bool函数
static int BoolSet(lua_State *L);
//虚拟机回调的设置Double函数
static int DoubleSet(lua_State *L);
/*
初始化这个LuaObject
:
注册虚拟机回调函数(static *Set, static *Get,static Log)
执行脚本文件
重命名update函数(使其在虚拟机中全局唯一)
*/
void initLuaObject();
public:
static cocos2d::LuaEngine *luaEngine;
//外部发起lua事件调用时应调用这个函数,state会被传入虚拟机(通过 IN_STATE__ 获取),可以override这个函数来扩展功能
virtual void Do(std::string state);
//设置虚拟机的全局String
void setGlobalString(std::string key, std::string value);
//设置虚拟机的全局Int
void setGlobalInt(std::string key, int value);
//设置虚拟机的全局Bool
void setGlobalBool(std::string key, bool value);
//设置虚拟机的全局Double
void setGlobalDouble(std::string key, double value);
};
如何使用:
class A : public RJCBLib::LuaObject
{
private:
virtual std::string getLuaScriptFilePath() override;
virtual std::string getUpdateFunctionName() override;
virtual std::string LuaObjectGetString(std::string key) override;
virtual int LuaObjectGetInt(std::string key) override;
virtual bool LuaObjectGetBool(std::string key) override;
virtual double LuaObjectGetDouble(std::string key) override;
virtual void LuaObjectSetString(std::string key, std::string value) override;
virtual void LuaObjectSetInt(std::string key, int value) override;
virtual void LuaObjectSetBool(std::string key, bool value) override;
virtual void LuaObjectSetDouble(std::string key, double value) override;
};
LuaObject
作为基类使用,也是和Lua脚本交互的接口。每个对象对应一个回调函数(如Update),在不同事件触发时对其传入不同字符串,这样脚本就可以进行判断。脚本通过一些注册的全局函数用来和LuaObject
交互,在执行脚本时LuaObject
也会维护静态的当前LuaObject
实例,这样就可以知道脚本获取的属性是哪个是哪个实例的。
A
继承自LuaObject
,override
了一些方法:
获取Lua脚本路径
std::string getLuaScriptFilePath()
获取回调函数名
std::string getUpdateFunctionName()
返回属性给Lua脚本
std::string LuaObjectGetString(std::string key)
int LuaObjectGetInt(std::string key)
bool LuaObjectGetBool(std::string key)
double LuaObjectGetDouble(std::string key)
Lua脚本设置属性到这个类
void LuaObjectSetString(std::string key, std::string value)
void LuaObjectSetInt(std::string key, int value)
void LuaObjectSetBool(std::string key, bool value)
void LuaObjectSetDouble(std::string key, double value)
在Lua中使用这些全局函数:
获取当前对象属性
rjcblib_getstring
rjcblib_getint
rjcblib_getbool
rjcblib_getdouble
设置当前对象属性
rjcblib_setstring
rjcblib_setint
rjcblib_setbool
rjcblib_setdouble
通过cocos2d打印日志
rjcblib_log
KVObject
这个类通过std::map<>
管理属性,提供灵活的属性变更能力,能够直接将Json数据解析。
class KVObject
{
public:
std::map<std::string, int> *intKV;
std::map<std::string, std::string> *stringKV;
std::map<std::string, bool> *boolKV;
std::map<std::string, double> *doubleKV;
KVObject();
~KVObject();
//查询是否包含KEY,T - 支持类型
template<typename T>
bool hasValue(std::string key)
{
do
{
if (typeid(T) == typeid(std::string))
{
if (stringKV->find(key) == stringKV->end())
break;
}
if (typeid(T) == typeid(int))
{
if (intKV->find(key) == intKV->end())
break;
}
if (typeid(T) == typeid(bool))
{
if (boolKV->find(key) == boolKV->end())
break;
}
if (typeid(T) == typeid(double))
{
if (doubleKV->find(key) == doubleKV->end())
break;
}
return true;
} while (false);
return false;
}
//直接获取值,KEY应是存在的,T - 支持类型
template<typename T>
void getValueDirect(std::string key, GenericValue& out)
{
if (typeid(T) == typeid(std::string))
{
out.String = stringKV->at(key);
}
if (typeid(T) == typeid(int))
{
out.Int = intKV->at(key);
}
if (typeid(T) == typeid(bool))
{
out.Bool = boolKV->at(key);
}
if (typeid(T) == typeid(double))
{
out.Double = doubleKV->at(key);
}
}
//获取值,检测KEY是否存在,T - 支持类型
template<typename T>
FUNC_RETURN_TYPE getValue(std::string key,GenericValue & out)
{
auto hasvalue = hasValue<T>(key);
if (!hasvalue)
return FUNC_RETURN_VALUE_NOT_FOUND;
getValueDirect<T>(key, out);
return FUNC_RETURN_VALUE_SUCCESS;
}
//直接设置值,KEY应是存在的, T - 支持类型
template<typename T>
void setValueDirect(std::string key, const GenericValue& in)
{
if (typeid(T) == typeid(std::string))
{
stringKV->at(key) = in.String;
}
if (typeid(T) == typeid(int))
{
intKV->at(key) = in.Int;
}
if (typeid(T) == typeid(bool))
{
boolKV->at(key) = in.Bool;
}
if (typeid(T) == typeid(double))
{
doubleKV->at(key) = in.Double;
}
}
//直接插入值,KEY应是不存在的, T - 支持类型
template<typename T>
void insertValueDirect(std::string key, const GenericValue& in)
{
if (typeid(T) == typeid(std::string))
{
stringKV->insert(std::pair<std::string, std::string>(key, in.String));
}
if (typeid(T) == typeid(int))
{
intKV->insert(std::pair<std::string,int>(key,in.Int));
}
if (typeid(T) == typeid(bool))
{
boolKV->insert(std::pair<std::string,bool>(key,in.Bool));
}
if (typeid(T) == typeid(double))
{
doubleKV->insert(std::pair<std::string,double>(key,in.Double));
}
}
//设置值,检测KEY是否存在, T - 支持类型
template<typename T>
FUNC_RETURN_TYPE setValue(std::string key, const GenericValue& in)
{
auto ret = hasValue<T>(key);
if (!ret)
insertValueDirect<T>(key, in);
else
setValueDirect<T>(key, in);
return FUNC_RETURN_VALUE_SUCCESS;
}
/*
解析RapidjsonGenericValue
RapidjsonGenericValueSelfType - RapidjsonGenericValue的类型
示例:
obj.parseRapidjsonGenericValue<decltype(arg)>(arg)
*/
template<typename RapidjsonGenericValueSelfType>
void parseRapidjsonGenericValue(RapidjsonGenericValueSelfType &gv)
{
if (!gv.IsObject())
{
return;
}
for (auto ite = gv.MemberBegin();ite != gv.MemberEnd();ite++)
{
if (ite->value.IsInt())
{
intKV->insert(std::pair<std::string, int>(ite->name.GetString(), ite->value.GetInt()));
}
if (ite->value.IsString())
{
stringKV->insert(std::pair<std::string,std::string>(ite->name.GetString(), ite->value.GetString()));
}
if (ite->value.IsBool())
{
boolKV->insert(std::pair<std::string,bool>(ite->name.GetString(), ite->value.GetBool()));
}
if (ite->value.IsDouble())
{
doubleKV->insert(std::pair<std::string, double>(ite->name.GetString(), ite->value.GetDouble()));
}
}
}
};
如何使用:
这个类目前(2016-3-6)支持`int`、`bool`、`double`、`std::string`类型的属性管理,存取方法需要传入对应的类型作为模板参数如`instance.getValue<int>(key,out)`,相关方法在代码中均有说明。
矩阵对象管理
通过矩阵的方式管理二维对象:
template<typename DataType_MustBeTypeOnRef>
class Matrix
{
private:
Matrix() :data(nullptr), height(0), width(0)
{
}
DataType_MustBeTypeOnRef*** data;
public:
int height;
int width;
static Matrix<DataType_MustBeTypeOnRef>* create()
{
auto t = new Matrix<DataType_MustBeTypeOnRef>();
return t;
}
bool init(int height, int width)
{
this->height = height;
this->width = width;
data = new DataType_MustBeTypeOnRef**[height];
for (int y = 0;y < height;y++)
{
data[y] = new DataType_MustBeTypeOnRef*[width];
for (int x = 0;x < width;x++)
{
data[y][x] = nullptr;
}
}
return true;
}
inline void set(int y, int x, DataType_MustBeTypeOnRef* data)
{
this->data[y][x] = data;
data->retain();
}
inline DataType_MustBeTypeOnRef* get(int y, int x)
{
return this->data[y][x];
}
inline void remove(int y, int x)
{
if (this->data[y][x] != nullptr)
{
this->data[y][x]->release();
this->data[y][x] = nullptr;
}
}
virtual ~Matrix()
{
if (data != nullptr)
{
for (int y = 0;y < height;y++)
{
for (int x = 0;x < width;x++)
{
remove(y, x);
}
delete[] data[y];
data[y] = nullptr;
}
delete[] data;
data = nullptr;
}
}
};
如何使用:
Matrix<Object>* matrix = Matrix<Object>::create();
matrix.init(10,10);
set..
get..
remove..
delete matrix;
注意Object
类型须继承自cocos2d::ref
,Matrix<>
会进行内存管理。