最近2周比较忙,没有抽出时间来写Blog,不过在这段时间里面把整个思路理了一遍,梳理了一下大纲,以后会多抽时间来写Blog。
好了,言归正传,做任何事情都需要一定的基础,没有坚实的地基,是不可能建立雄伟的大厦的。所以我在整个系列博文的最前面,把一些最基础的东西先整理出来,为后面的系统做铺垫。
本篇的内容,会介绍几个内容:单例,dll动态加载以及一些跨平台的处理。
1、单例:单例模式是一种使用广泛而又比较简单的设计模式,他的定义我就不多介绍了,大家上网一查就知道了,基本都能理解。在游戏开发中,会有很多单件,所以封装一个单例类供后面的开发使用。
本单例使用模板实现,代码如下:
- //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
//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
- //使用的时候只需要单件类继承此模板即可。
- class Test : public Singleton<Test >{...};
//使用的时候只需要单件类继承此模板即可。 class Test : public Singleton<Test >{...};
2、dll(so)动态加载
在开发网络游戏的过程中,现在已经不是能够单打独斗的年代了,一款游戏基本上不可能有一个人完成,因此分模块开发成为了必然,各自开发相关的模块,然后组合到一起。dll就是分模块开发的产物之一,它的加载有动态和静态之分,各有优势,但是由于服务器程序是需要运行在多个平台,而他们又各自有各自的加载方法,为了方便使用,因此我们队加载dll进行了封装。
实现使用模板,非常简单,代码如下
- // lib_def.h
- #ifndef __LIB_DEF_H_
- #define __LIB_DEF_H_
- namespace Blaze
- {
- // DLL对象创建辅助类
- template<const TCHAR*& szFileName>
- class DllApi
- {
- public:
- static BOOL Load()
- {
- if(!m_h) m_h = ::LoadLibrary(szFileName);
- return m_h != NULL;
- }
- static void Unload()
- {
- if(m_h) ::FreeLibrary(m_h);
- m_h = NULL;
- }
- protected:
- static HMODULE m_h;
- };
- template<const TCHAR*& szFileName> HMODULE DllApi<szFileName>::m_h;
- //库文件前后缀
- #ifdef WIN32
- #define __DLL_PREFIX _T("")
- #define __DLL_SUFFIX _T(".dll")
- #else
- #define __DLL_PREFIX _T("lib")
- #define __DLL_SUFFIX _T(".so")
- #endif
- // 声明DLL文件名常量
- #define DECLARE_DLL_FILE(module) extern "C" const TCHAR* module;
- // 定义DLL文件名常量
- #if !defined(_LIB) && !defined(_USE_STATIC_LIB)
- #define DEFINE_DLL_FILE(module) extern "C" const TCHAR* module = _T("./")""__DLL_PREFIX""_T(#module)""__DLL_SUFFIX;
- #else
- #define DEFINE_DLL_FILE(module)
- #endif
- }
- #endif
本例中使用了LoadLibrary,是windows的实现方法,在后面平台相关处理中,我会将linux的函数封装一下,和windows同名。此模板使用方法很简单:
- #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
#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>
- #define SYS_API WINAPI
- #define STD_CALL __stdcall
- #if !defined(_LIB)
- #define EXPORT_API extern "C" _declspec(dllexport)
- #else
- #define EXPORT_API extern "C"
- #endif
- #if !defined(_LIB) && !defined(_USE_STATIC_LIB)
- #define IMPORT_API extern "C" _declspec(dllimport)
- #else
- #define IMPORT_API extern "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) /* 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_
// 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相关函数的名字。如果在开发中遇到了其他的情况,也可以加到此文件中,以方便应用开发。
大家可能会觉得在这里看代码比较别扭,我把代码上传到了空间,大家可以去下载。