最近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相關函數的名字。如果在開發中遇到了其他的情況,也可以加到此文件中,以方便應用開發。
大家可能會覺得在這裏看代碼比較彆扭,我把代碼上傳到了空間,大家可以去下載。