本篇的内容,会介绍几个内容:单例,dll动态加载以及一些跨平台的处理。
1、单例:单例模式是一种使用广泛而又比较简单的设计模式,他的定义我就不多介绍了,大家上网一查就知道了,基本都能理解。在游戏开发中,会有很多单件,所以封装一个单例类供后面的开发使用。
本单例使用模板实现,代码如下:
- //singleton.h
- #ifndef_SINGLETON_H
- #define_SINGLETON_H
- namespaceBlaze
- {
- template<classT>
- classSingleton
- {
- public:
- staticT*instance()
- {
- if(!_instance)
- {
- _instance=newT;
- }
- return_instance;
- }
- protected:
- ///使用保护构造是为了用户不能在栈上声明一个实例
- Singleton(){}
- private:
- staticT*_instance;///实例静态指针
- };
- ///静态实例指针初始化
- template<classT>T*Singleton<T>::_instance=NULL;
- }//namespace
- #endif//_SINGLETON_H
//singleton.h #ifndef _SINGLETON_H #define _SINGLETON_H namespace Blaze { template<class T> class Singleton { public: static T* instance() { if (!_instance) { _instance = new T; } return _instance; } protected: /// 使用保护构造是为了用户不能在栈上声明一个实例 Singleton() {} private: static T* _instance; /// 实例静态指针 }; /// 静态实例指针初始化 template <class T> T* Singleton<T>::_instance = NULL; } // namespace #endif //_SINGLETON_H
- //使用的时候只需要单件类继承此模板即可。
- classTest:publicSingleton<Test>{...};
//使用的时候只需要单件类继承此模板即可。 class Test : public Singleton<Test >{...};
2、dll(so)动态加载
在开发网络游戏的过程中,现在已经不是能够单打独斗的年代了,一款游戏基本上不可能有一个人完成,因此分模块开发成为了必然,各自开发相关的模块,然后组合到一起。dll就是分模块开发的产物之一,它的加载有动态和静态之分,各有优势,但是由于服务器程序是需要运行在多个平台,而他们又各自有各自的加载方法,为了方便使用,因此我们队加载dll进行了封装。
实现使用模板,非常简单,代码如下
- //lib_def.h
- #ifndef__LIB_DEF_H_
- #define__LIB_DEF_H_
- namespaceBlaze
- {
- //DLL对象创建辅助类
- template<constTCHAR*&szFileName>
- classDllApi
- {
- public:
- staticBOOLLoad()
- {
- if(!m_h)m_h=::LoadLibrary(szFileName);
- returnm_h!=NULL;
- }
- staticvoidUnload()
- {
- if(m_h)::FreeLibrary(m_h);
- m_h=NULL;
- }
- protected:
- staticHMODULEm_h;
- };
- template<constTCHAR*&szFileName>HMODULEDllApi<szFileName>::m_h;
- //库文件前后缀
- #ifdefWIN32
- #define__DLL_PREFIX_T("")
- #define__DLL_SUFFIX_T(".dll")
- #else
- #define__DLL_PREFIX_T("lib")
- #define__DLL_SUFFIX_T(".so")
- #endif
- //声明DLL文件名常量
- #defineDECLARE_DLL_FILE(module)extern"C"constTCHAR*module;
- //定义DLL文件名常量
- #if!defined(_LIB)&&!defined(_USE_STATIC_LIB)
- #defineDEFINE_DLL_FILE(module)extern"C"constTCHAR*module=_T("./")""__DLL_PREFIX""_T(#module)""__DLL_SUFFIX;
- #else
- #defineDEFINE_DLL_FILE(module)
- #endif
- }
- #endif
本例中使用了LoadLibrary,是windows的实现方法,在后面平台相关处理中,我会将linux的函数封装一下,和windows同名。此模板使用方法很简单:
- #ifdefined(_LIB)||defined(_USE_STATIC_LIB)//静态库版本
- #ifndef_LUA_ENGINE_API
- #define_LUA_ENGINE_APIIMPORT_API
- #pragmacomment(lib,MAKE_LIB_NAME(LuaEngine))
- #endif
- _LUA_ENGINE_APIILuaEngine*GlobalLuaEngine();
- #else
- DECLARE_DLL_FILE(LuaEngine);
- classGlobalLuaEngine:publicDllApi<LuaEngine>
- {
- typedefILuaEngine*(*CREATE_PROC)();
- ILuaEngine*m_p;
- public:
- GlobalLuaEngine():m_p(NULL)
- {
- Load();
- staticCREATE_PROCfunc;
- if(func==NULL)func=(CREATE_PROC)::GetProcAddress(m_h,"GlobalLuaEngine");
- if(func!=NULL)m_p=func();
- }
- operatorILuaEngine*(){returnm_p;}
- ILuaEngine*operator->(){returnm_p;}
- };
- #endif
#if defined(_LIB) || defined(_USE_STATIC_LIB) // 静态库版本 #ifndef _LUA_ENGINE_API #define _LUA_ENGINE_API IMPORT_API #pragma comment(lib, MAKE_LIB_NAME(LuaEngine)) #endif _LUA_ENGINE_API ILuaEngine* GlobalLuaEngine(); #else DECLARE_DLL_FILE(LuaEngine); class GlobalLuaEngine : public DllApi<LuaEngine> { typedef ILuaEngine* (*CREATE_PROC)(); ILuaEngine* m_p; public: GlobalLuaEngine() : m_p(NULL) { Load(); static CREATE_PROC func; if(func == NULL) func = (CREATE_PROC)::GetProcAddress(m_h, "GlobalLuaEngine"); if(func != NULL) m_p = func(); } operator ILuaEngine* (){ return m_p; } ILuaEngine* operator ->(){ return m_p; } }; #endif
如上面代码所示,LuaEngine是一个dll,我们在加载它的时候,使用了一个额外的类,在他的构造函数里面加载了共享库。而且在应用级上也与平台无关。
3、跨平台的若干处理
windows的处理相当简单,只是定义一些简单的宏。
- //gwindef.h:windows开发定义文件
- #ifndef__G_WIN_DEF_H_
- #define__G_WIN_DEF_H_
- #include<windows.h>
- #include<process.h>
- #include<tchar.h>
- #include<unknwn.h>
- #include<stdio.h>
- #include<stdlib.h>
- #defineSYS_APIWINAPI
- #defineSTD_CALL__stdcall
- #if!defined(_LIB)
- #defineEXPORT_APIextern"C"_declspec(dllexport)
- #else
- #defineEXPORT_APIextern"C"
- #endif
- #if!defined(_LIB)&&!defined(_USE_STATIC_LIB)
- #defineIMPORT_APIextern"C"_declspec(dllimport)
- #else
- #defineIMPORT_APIextern"C"
- #endif
- #endif//ndef__G_WIN_DEF_H_
而为了开发的时候去除平台无关性,在linux的开发中,我们需要做一些包装,使其在开发过程中和window代码一致。
- //glindef.h:linux开发定义文件
- #ifndef__G_LIN_DEF_H_
- #define__G_LIN_DEF_H_
- //
- #include<stdlib.h>
- #include<stdio.h>
- #include<string.h>
- #include<wchar.h>
- #include<unistd.h>
- #include<pthread.h>
- #include<semaphore.h>
- #include<errno.h>
- #include<sys/times.h>
- #include<time.h>
- #include<dlfcn.h>
- #include<sys/types.h>
- #include<linux/unistd.h>
- inline_syscall0(pid_t,gettid)/*Usingsyscall(2)maybepreferable;seeintro(2)*/
- #ifdefUNICODE
- #define_T(str)L##str
- #else
- #define_T(str)str
- #endif
- #defineTRUE1
- #defineFALSE0
- #defineMAX_PATH256
- #defineSYS_API
- #defineSTD_CALL
- #defineEXPORT_APIextern"C"
- #defineIMPORT_APIextern"C"
- ///HRESULT常量定义
- typedeflongHRESULT;
- enumHResult
- {
- S_OK=((HRESULT)0x00000000),/**<成功,值为0*/
- S_FALSE=((HRESULT)0x00000001),/**<成功,但值为1*/
- E_FAIL=_HRESULT_TYPEDEF_(0x80004005),/**<未定义错误*/
- E_NOTIMPL=_HRESULT_TYPEDEF_(0x80004001),/**<接口未实现*/
- E_OUTOFMEMORY=_HRESULT_TYPEDEF_(0x8007000E),/**<内存不足*/
- E_INVALIDARG=_HRESULT_TYPEDEF_(0x80070057),/**<无效参数*/
- E_NOINTERFACE=_HRESULT_TYPEDEF_(0x80004002),/**<接口不存在*/
- E_POINTER=_HRESULT_TYPEDEF_(0x80004003),/**<无效指针*/
- E_HANDLE=_HRESULT_TYPEDEF_(0x80070006),/**<无效句柄*/
- E_ABORT=_HRESULT_TYPEDEF_(0x80004004),/**<操作被取消*/
- E_ACCESSDENIED=_HRESULT_TYPEDEF_(0x80070005),/**<访问拒绝*/
- E_PENDING=_HRESULT_TYPEDEF_(0x8000000A),/**<操作被挂起*/
- E_UNEXPECTED=_HRESULT_TYPEDEF_(0x8000FFFF)/**<未预料的错误*/
- };
- ///判定HRESULT值是否为成功值
- #defineSUCCEEDED(Status)((HRESULT)(Status)>=0)
- ///判定HRESULT值是否为失败值
- #defineFAILED(Status)((HRESULT)(Status)<0)
- ///GUID类型定义
- /**
- 要定义GUID常量请使用GUID专门的生成工具(比如VS携带的guidgen.exe程序)来生成,
- 以确保其唯一性。
- 接口ID(IID),类ID(CLSID)均为GUID的别名*/
- structGUID
- {
- unsignedlongData1;
- unsignedshortData2;
- unsignedshortData3;
- unsignedcharData4[8];
- };
- typedefGUIDIID;
- typedefGUIDCLSID;
- #defineREFGUIDconstGUID&
- #defineREFIIDconstIID&
- #defineREFCLSIDconstCLSID&
- ///判断两个GUID是否相等(内联版)
- inlineBOOLInlineIsEqualGUID(REFGUIDrguid1,REFGUIDrguid2)
- {
- return((long*)&rguid1)[0]==((long*)&rguid2)[0]&&
- ((long*)&rguid1)[1]==((long*)&rguid2)[1]&&
- ((long*)&rguid1)[2]==((long*)&rguid2)[2]&&
- ((long*)&rguid1)[3]==((long*)&rguid2)[3];
- }
- ///判断两个GUID是否相等
- inlineBOOLIsEqualGUID(REFGUIDrguid1,REFGUIDrguid2)
- {
- return!memcmp(&rguid1,&rguid2,sizeof(GUID));
- }
- #defineCopyMemory(dest,src,len)memcpy((dest),(src),(len))
- #defineZeroMemory(dest,len)memset((dest),0,(len))
- #defineFillMemory(dest,len,value)memset((dest),value,(len))
- #defineGetCurrentThreadIdgettid
- #defineOutputDebugString(str)tprintf(_T("%s"),str)
- #defineLoadLibrary(file)dlopen(file,RTLD_NOW)
- #defineFreeLibrarydlclose
- #defineGetProcAddressdlsym
- inlineintGetLastError()
- {
- returnerrno;
- }
- inlineDWORDGetTickCount()
- {
- staticintclkTck=0;
- if(clkTck==0)clkTck=1000/::sysconf(_SC_CLK_TCK);
- return(DWORD)::times(NULL)*clkTck;//不能溢出
- }
- inlinevoidSleep(DWORDms)
- {
- structtimespecreq,rem;
- req.tv_sec=ms/1000;req.tv_nsec=(ms%1000)*1000000;
- while(::nanosleep(&req,&rem)&&::GetLastError()==EINTR)req=rem;
- }
- inlinelongInterlockedIncrement(longvolatile*v)
- {
- longsrc=1;
- /*Modern486+processor*/
- __asm____volatile__(
- "lockxaddl%0,%1;"
- :"=r"(src),"=m"(*v)
- :"0"(src));
- returnsrc+1;
- }
- inlinelongInterlockedDecrement(longvolatile*v)
- {
- longsrc=-1;
- /*Modern486+processor*/
- __asm____volatile__(
- "lockxaddl%0,%1;"
- :"=r"(src),"=m"(*v)
- :"0"(src));
- returnsrc-1;
- }
- #definestricmpstrcasecmp
- #include<ctype.h>
- inlinevoidstrupr(char*s)
- {
- while(*s){
- *s=toupper((unsignedchar)*s);
- s++;
- }
- }
- inlinevoidstrlwr(char*s)
- {
- while(*s){
- *s=tolower((unsignedchar)*s);
- s++;
- }
- }
- #endif//ndef__G_LIN_DEF_H_
// glindef.h : linux开发定义文件 #ifndef __G_LIN_DEF_H_ #define __G_LIN_DEF_H_ // #include <stdlib.h> #include <stdio.h> #include <string.h> #include <wchar.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h> #include <errno.h> #include <sys/times.h> #include <time.h> #include <dlfcn.h> #include <sys/types.h> #include <linux/unistd.h> inline _syscall0(pid_t, gettid) /* Using syscall(2) may be preferable; see intro(2) */ #ifdef UNICODE #define _T(str) L##str #else #define _T(str) str #endif #define TRUE 1 #define FALSE 0 #define MAX_PATH 256 #define SYS_API #define STD_CALL #define EXPORT_API extern "C" #define IMPORT_API extern "C" /// HRESULT 常量定义 typedef long HRESULT; enum HResult { S_OK = ((HRESULT)0x00000000), /**< 成功,值为0 */ S_FALSE = ((HRESULT)0x00000001), /**< 成功,但值为1 */ E_FAIL = _HRESULT_TYPEDEF_(0x80004005), /**< 未定义错误 */ E_NOTIMPL = _HRESULT_TYPEDEF_(0x80004001), /**< 接口未实现 */ E_OUTOFMEMORY = _HRESULT_TYPEDEF_(0x8007000E), /**< 内存不足 */ E_INVALIDARG = _HRESULT_TYPEDEF_(0x80070057), /**< 无效参数 */ E_NOINTERFACE = _HRESULT_TYPEDEF_(0x80004002), /**< 接口不存在 */ E_POINTER = _HRESULT_TYPEDEF_(0x80004003), /**< 无效指针 */ E_HANDLE = _HRESULT_TYPEDEF_(0x80070006), /**< 无效句柄 */ E_ABORT = _HRESULT_TYPEDEF_(0x80004004), /**< 操作被取消 */ E_ACCESSDENIED = _HRESULT_TYPEDEF_(0x80070005), /**< 访问拒绝 */ E_PENDING = _HRESULT_TYPEDEF_(0x8000000A), /**< 操作被挂起 */ E_UNEXPECTED = _HRESULT_TYPEDEF_(0x8000FFFF) /**< 未预料的错误 */ }; /// 判定 HRESULT 值是否为成功值 #define SUCCEEDED(Status) ((HRESULT)(Status) >= 0) /// 判定 HRESULT 值是否为失败值 #define FAILED(Status) ((HRESULT)(Status) < 0) /// GUID 类型定义 /** 要定义 GUID 常量请使用 GUID 专门的生成工具(比如 VS 携带的 guidgen.exe 程序)来生成, 以确保其唯一性。 接口 ID(IID), 类 ID(CLSID)均为 GUID 的别名*/ struct GUID { unsigned long Data1; unsigned short Data2; unsigned short Data3; unsigned char Data4[8]; }; typedef GUID IID; typedef GUID CLSID; #define REFGUID const GUID& #define REFIID const IID& #define REFCLSID const CLSID& /// 判断两个 GUID 是否相等(内联版) inline BOOL InlineIsEqualGUID(REFGUID rguid1, REFGUID rguid2) { return ((long*)&rguid1)[0] == ((long*)&rguid2)[0] && ((long*)&rguid1)[1] == ((long*)&rguid2)[1] && ((long*)&rguid1)[2] == ((long*)&rguid2)[2] && ((long*)&rguid1)[3] == ((long*)&rguid2)[3]; } /// 判断两个 GUID 是否相等 inline BOOL IsEqualGUID(REFGUID rguid1, REFGUID rguid2) { return !memcmp(&rguid1, &rguid2, sizeof(GUID)); } #define CopyMemory(dest, src, len) memcpy((dest), (src),(len)) #define ZeroMemory(dest, len) memset((dest), 0, (len)) #define FillMemory(dest, len, value) memset((dest), value, (len)) #define GetCurrentThreadId gettid #define OutputDebugString(str) tprintf(_T("%s"), str) #define LoadLibrary(file) dlopen(file, RTLD_NOW) #define FreeLibrary dlclose #define GetProcAddress dlsym inline int GetLastError() { return errno; } inline DWORD GetTickCount() { static int clkTck = 0; if(clkTck == 0) clkTck = 1000 / ::sysconf(_SC_CLK_TCK); return (DWORD)::times(NULL) * clkTck; // 不能溢出 } inline void Sleep(DWORD ms) { struct timespec req, rem; req.tv_sec = ms / 1000; req.tv_nsec = (ms % 1000) * 1000000; while(::nanosleep(&req, &rem) && ::GetLastError() == EINTR) req = rem; } inline long InterlockedIncrement(long volatile* v) { long src = 1; /* Modern 486+ processor */ __asm__ __volatile__( "lock xaddl %0, %1;" :"=r"(src), "=m"(*v) :"0"(src)); return src + 1; } inline long InterlockedDecrement(long volatile* v) { long src = -1; /* Modern 486+ processor */ __asm__ __volatile__( "lock xaddl %0, %1;" :"=r"(src), "=m"(*v) :"0"(src)); return src - 1; } #define stricmp strcasecmp #include <ctype.h> inline void strupr(char *s) { while (*s) { *s = toupper((unsigned char) *s); s++; } } inline void strlwr(char *s) { while (*s) { *s = tolower((unsigned char) *s); s++; } } #endif // ndef __G_LIN_DEF_H_
代码都比较简单,我也不对这些做详细的解析,功能就是对一些常用函数改装成windows相关函数的名字。如果在开发中遇到了其他的情况,也可以加到此文件中,以方便应用开发。
大家可能会觉得在这里看代码比较别扭,我把代码上传到了空间,大家可以去下载。