c++游戏服务器配置加载管理实现

北城以北 提交于 2020-01-14 07:36:15

做过游戏的都知道在游戏服务器中配置文件很多,基本上会达到两三百张配置表,而加载配置的方式有很多,下面就为大家介绍几种配置加载的方式。

首先简介一种常用的也是最简单的方式,那就是包含所有的配置文件一个个的调用,代码大致如下:

//a.h 配置文件a

bool loada();//加载配置文件a

//b.h 配置文件b

bool loadb();//加载配置文件b

……

//cfgmgr.h 统一调用配置文件加载函数

include“a.h”

include“b.h”

……

void loadall(){

loada();

loadb ()

}

这种方式简单易学,一般做过项目的人都能够想到,但是在每次增加新的配置的时候都需要在cfgmgr文件中包含头文件并且调用加载函数。

下面我们对上面的代码进行一下改进,我们希望增加配置的时候不需要在cfgmgr中包含头文件和调用对应的函数,这样可以减少头文件包含加快编译速度,避免修改任何配置头文件都会重新编译cfgmgr。代码大致如下:

//cfgmgr.h

class CfgBase;

class CfgMgr:public CSingleton<CfgMgr>{//CSingleton 为单例

void addcfg(CfgBase* cfg){m_allCfg.emplace_back(cfg)}

void load(){

for(auto& iter : m_allCfg)

iter->load();}

private:

std::vector<CfgBase* >m_allCfg;

};

class CfgBase{

CfgBase(){CfgMgr::instance().addcfg(this);}//在构造的时候把自己注册到CfgMgr中

virtual ~CfgBase();

virtual  void load()=0;

};

//a.h//配置a

class A:public CfgBase,public CSingleton<A>{

void load();//加载a

};

//a.cpp//配置a

static bool __TYPE__ =&A::instance() != NULL;//注意这里是在cpp文件中定义一个静态变量,全局静态变量会在调用main函数之前初始化,在初始化的时候就会调用单例生成对象,生成对象的时候在调用构造函数这样就可以实现自动注册到配置管理器中了。我们不用在管理器中一个个头文件包含和调用。

那可能的人不想使用继承,就希望自己定义的任意类型都能够加到配置管理器中,或者是包括配置或者数据加载都放到一个管理器中,下面介绍一种更为特殊的加载方式,代码如下:

//cfgmgr.h

#ifndef _CONFIG_MANAGER_H_
#define _CONFIG_MANAGER_H_
#include <map>
#include "singleton.h"
//服务器配置数据加载管理器
class CBaseHolder
{
public:
    CBaseHolder() {}
    virtual ~CBaseHolder() {}
    virtual bool LoadCfg() = 0;
    virtual bool ReloadCfg() = 0;
    virtual bool LoadData() = 0;
    virtual bool Init() = 0;
    virtual bool Reinit() = 0;
    virtual const char* GetInfo() = 0;
};
template<class T>
class CCommonHolder :public CBaseHolder
{
    //通过模板自动设别加载配置加载数据和初始化 通过LoadCfgOrder的第一位作为是否需要重新加载和重新初始化 用变参宏传入是否需要重新加载和重新初始化
private:
    template <typename U>
    class IsCfgLoader
    {
        template <class C> static uint8_t check(typename C::ConfigValueType*) { return 0; }
        template <class C> static uint16_t check(...) { return 0; }
    public:
        enum { value = (sizeof (uint8_t) == sizeof( check<U>(nullptr))) };
    };
    template<typename U>
    std::enable_if_t<std::is_same_v<decltype(&U::LoadCfg), bool (U::*)()> && IsCfgLoader<U>::value, bool> LoadCfg(U*ptr) { return U::Instance().LoadCfg(); }
    template<typename U>
    bool LoadCfg(...) { return true; }
    template<typename U>
    std::enable_if_t<std::is_same_v<decltype(&U::ReloadCfg), bool (U::*)()> && IsCfgLoader<U>::value, bool> ReloadCfg(U* ptr) { return U::Instance().ReloadCfg(); }
    template<typename U>
    bool ReloadCfg(...) { return true; }
    template<typename U>
    std::enable_if_t<std::is_same_v<decltype(&U::LoadData), bool (U::*)()>, bool> LoadData(U* ptr) { return U::Instance().LoadData(); }
    template<typename U>
    bool LoadData(...) { return true; }
    template<typename U>
    std::enable_if_t<std::is_same_v<decltype(&U::Init), bool (U::*)()>, bool> Init(U* ptr) { return U::Instance().Init(); }
    template<typename U>
    bool Init(...) { return true; }
    template<typename U>
    std::enable_if_t<std::is_same_v<decltype(&U::Reinit), bool (U::*)()>, bool> Reinit(U* ptr) { return U::Instance().Reinit(); }
    template<typename U>
    bool Reinit(...) { return true; }
public:
    CCommonHolder() {}
    virtual ~CCommonHolder() {}
    virtual bool LoadCfg()override { return LoadCfg<T>(nullptr); }
    virtual bool ReloadCfg()override { return ReloadCfg<T>(nullptr); }
    virtual bool LoadData()override { return LoadData<T>(nullptr); }
    virtual bool Init()override { return Init<T>(nullptr); }
    virtual bool Reinit()override { return Reinit<T>(nullptr); }
    virtual const char* GetInfo() override{ return typeid(T).name(); }
};
enum LoadCfgOrder :unsigned int
{
    LoadCfgOrder_Reload = 1,//重新加载和重新初始化标记 
    LoadCfgOrder_Pre = 0xFF,//优先加载的数据库数据的枚举放在这个的前面
    LoadCfgOrder_Data = 0x8FFF,//配置加载顺序枚举放这个前面 数据加载顺序枚举放在这个后面
    LoadCfgOrder_Defualt = 0xFFFF,//默认的顺序 有加载顺序需求的配置和数据库枚举放在这个前面
    LoadCfgOrder_PreData = (LoadCfgOrder_Pre << 1) | LoadCfgOrder_Reload,
};
//可以同时支持加载数据 加载配置 初始化配置
//一个类对象同时加载配置和数据如果一个需要支持重新加载一个不需要支持重新加载只能自己在Init和LoadCfg中控制
#ifdef WIN32
#define REGHOLDER(TYPE,...) static bool __##TYPE__ = CCfgManager::GetInstance().AddLoader(new CCommonHolder<TYPE>,##__VA_ARGS__);
#else
#define REGHOLDER(TYPE,...) static bool __##TYPE__  __attribute__((unused))= CCfgManager::GetInstance().AddLoader(new CCommonHolder<TYPE>,##__VA_ARGS__);
#endif // WIN32
class CCfgManager:public CSingleton<CCfgManager>
{
public:
    CCfgManager(){}
    ~CCfgManager();
    bool AddLoader(CBaseHolder* pLoader, LoadCfgOrder id = LoadCfgOrder_Defualt,bool reload=false);
    bool Load();
    bool ReLoad();
private:
    std::multimap<unsigned int, CBaseHolder*> m_holder;//有加载顺序需求
};
#endif

//cfgmgr.cpp

#include "pch.h"
#include "configmanager.h"
#include "log.h"
 CCfgManager::~CCfgManager()
 {
     for (auto& iter : m_holder)
         delete iter.second;
 }
 bool CCfgManager::AddLoader(CBaseHolder* pLoader, LoadCfgOrder id , bool reload)
 { 
     unsigned int tmp = id;
     return m_holder.emplace((tmp << 1) | (reload ? 1 : 0), pLoader),true;
 }
bool CCfgManager::Load()
{
    for (auto& iter : m_holder)//在配置加载之前加载的数据
        if (iter.first < LoadCfgOrder_PreData && !iter.second->LoadData())
            return WRITE_ERROR_LOG("pre load data has error info=%s", iter.second->GetInfo()), false;
    for (auto& iter : m_holder)//加载配置
        if (!iter.second->LoadCfg())
            return WRITE_ERROR_LOG("load cfg has error info=%s", iter.second->GetInfo()), false;
    for (auto& iter : m_holder)//加载数据
        if (iter.first > LoadCfgOrder_PreData && !iter.second->LoadData())
            return WRITE_ERROR_LOG("load data has error info=%s", iter.second->GetInfo()), false;
    for (auto& iter : m_holder)//初始化数据
        if (!iter.second->Init())
            return WRITE_ERROR_LOG("init data has error info=%s", iter.second->GetInfo()), false;
    return true;
}
bool CCfgManager::ReLoad()
{
    for (auto& iter : m_holder)//重新加载配置
        if ((iter.first & LoadCfgOrder_Reload) > 0 && !iter.second->ReloadCfg())
            return WRITE_ERROR_LOG("reload cfg has error info=%s", iter.second->GetInfo()), false;
    for (auto& iter : m_holder)//重新初始化数据
        if ((iter.first & LoadCfgOrder_Reload) > 0 && !iter.second->Reinit())
            return WRITE_ERROR_LOG("reinit data has error info=%s", iter.second->GetInfo()), false;
    return true;
}

//a.h//配置a

class A:public CSingleton<A>{

bool  LoadCfg();//加载a

};

//a.cpp//配置a

REGHOLDER(A);//在cpp调用宏这样就会使配置在main函数调用之前直接注册的配置管理器中,而且可以同时支持配置加载,数据加载等,也实现了加载顺序,简单方便快捷,作为程序员永远要想着如何能够少写代码,即使是一两行代码。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!