最近2周比较忙,没有抽出时间来写Blog,不过在这段时间里面把整个思路理了一遍,梳理了一下大纲,以后会多抽时间来写Blog。
好了,言归正传,做任何事情都需要一定的基础,没有坚实的地基,是不可能建立雄伟的大厦的。所以我在整个系列博文的最前面,把一些最基础的东西先整理出来,为后面的系统做铺垫。
本篇的内容,会介绍几个内容:单例,dll动态加载以及一些跨平台的处理。
1、单例:单例模式是一种使用广泛而又比较简单的设计模式,他的定义我就不多介绍了,大家上网一查就知道了,基本都能理解。在游戏开发中,会有很多单件,所以封装一个单例类供后面的开发使用。
本单例使用模板实现,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
//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 |
1 2 |
//使用的时候只需要单件类继承此模板即可。 class Test : public Singleton<Test >{...}; |
2、dll(so)动态加载
在开发网络游戏的过程中,现在已经不是能够单打独斗的年代了,一款游戏基本上不可能有一个人完成,因此分模块开发成为了必然,各自开发相关的模块,然后组合到一起。dll就是分模块开发的产物之一,它的加载有动态和静态之分,各有优势,但是由于服务器程序是需要运行在多个平台,而他们又各自有各自的加载方法,为了方便使用,因此我们队加载dll进行了封装。
实现使用模板,非常简单,代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
// 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同名。此模板使用方法很简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#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的处理相当简单,只是定义一些简单的宏。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
// 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代码一致。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
// 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相关函数的名字。如果在开发中遇到了其他的情况,也可以加到此文件中,以方便应用开发。
发表评论
要发表评论,您必须先登录。