我到公司后接到的是之前的一个客户端的项目,在老版本基础上修改了个简单的界面引擎,最近把旧的业务也迁移了过来。旧的项目配置模块使用的是SQLite,经理发现那个升级不方便,让我把配置修改成普通配置文件的方式。
把程序里面用到配置文件的地方整理了下,分为两类:
1 参数类,简单来说类似(Key, Value)
2 列表类,同数据库一样的多个域的一条条记录
最先想到的是使用XML来做配置文件。第一类类似
<Config> <Param key1=value1 key2=value2......./> </Config>
第二类的类似
<Config> <Item field1=value1 field2=value2....../> ..... </Config>
项目已经使用的库有rapidxml,貌似仅能解析,且对UNICODE支持不好,我之前还对他作了少量修改才可以。几年前的项目还用到了Tinyxml,也仅支持Ansi字符串,也懒得去动它。
反正用了各种途径没有发现好用的C++的XML模块,于是打算自己写个简单的读写配置文件的模块。最基本的想法是简单,可用,就想到了Lua, Javascript的表示方法
第一类的类似
{
key1:value1,
key2:value2,
......
}
第二类的类似
[
{field1:value1, field2:value2,...},
...
]
下面是代码关键片段,不包含Parser.
namespace Config {
enum Type{TYPE_NULL, TYPE_TABLE, TYPE_ARRAY, TYPE_TEXT, TYPE_NUMBER};
class Object
{
public:
Object(Type type);
~Object();
Object(const Object& obj);
Object& operator=(const Object& obj);
Type GetType();
//表和数组的操作
Object& operator[](const std::wstring&);
Object& operator[](int index);
int GetCount();
int Contains(const std::wstring&);
//数组的更多操作
//数字和文本的操作
int GetNumber();
void SetNumber(int number);
std::wstring GetText();
void SetText(const std::wstring&);
protected:
struct InnerObject()
{
InnerObject(Type type);
~InnerObject();
void AddRef();
void DecRef();
Type m_Type;
int m_iReferenceCount;
union
{
std::vector<Object>* m_arrObjects;
std::map<std::wstring, Object>* m_tblObjects;
std::wstring* m_sText;
int m_iNumber;
}
}*m_pInnerObject;
};
}
为甚么要把几种数据操作糅合在一起看上去很奇特,这是经过仔细思考了的。如果不放在一起必然使用指针啊,类型强制转换之类的操作起来更不方便。
现在只需要用GetType(),再调用匹配的函数操作即可,并且对象内部使用计数管理,可以像使用值对象一样来使用,不用自己来进行内存管理。
代码段的接口都有了,实现起来就很简单了,代码也不太长。
目前也只写了个大概,可能还有点问题。
使用举例:
int main() {
Config::Object obj = Parse(L"{a:5,b:[{x:3,y:4,name:\"a\"},{x:3,y:4,name:\"a\"}],c:\"aaa\"}");
//读取
std::wstring name = obj[L"b"][1][L"name"].GetText();
//修改
obj[L"b"][0][L"y"].SetNumber(8);
return 0;
}
大概意思就这样,Object可以多重载几个构造函数,初始化的时候直接赋值。