我的做法很簡單,就是定義一個標頭檔nPlatform.h,讓其他的程式碼一定要include進來,這樣就可以在編譯(compile)知道是哪一個平台了。
#ifndef nPlatform_h #define nPlatform_h #if (defined(NS_WIN32) || defined(WIN32)) #define NS_PLATFORM_WIN32 1 #define NS_PLATFORM_WINDOWS 1 #elif (defined(_WIN32_WCE) || defined(NS_WINCE)) #define NS_PLATFORM_WINCE 1 #define NS_PLATFORM_WINDOWS 1 #elif (defined(__ANDROID__) || defined(NS_ANDROID)) #define NS_PLATFORM_ANDROID 1 #define NS_PLATFORM_LINUX 1 #elif (defined(__APPLE__)) #define NS_APPLE 1 #include "TargetConditionals.h" #if TARGET_IPHONE_SIMULATOR // iOS Simulator #define NS_PLATFORM_IOS 1 #elif TARGET_OS_IPHONE // iOS device #define NS_PLATFORM_IOS 1 #elif TARGET_OS_MAC // Other kinds of Mac OS #define NS_PLATFORM_OSX 1 #else // Unsupported platform DASSERT(0); #endif #else DASSERT(0); #endif #endif // nPlatform_h
從上面的程式碼可以知道,在Mac裡頭是使用#elif (defined(__APPLE__))來決定編譯環境的,一旦程式檔include這個標頭檔,就可以知道目前的平台了。
其實為了避免,不同平台的函式呼叫散步在這個項目中,通常會在底層的部分將這些不同函式呼叫集中起來,上層一律使用包裝過的類別或者Api,就可以將跨平台的移植負擔最小化。
舉個例子,我會將檔案存取(file access)的函式特別包裝一個類別,實作的部分會根據不同平台使用不同的函式,上層程式碼就一律呼叫這個特定的類別,這樣發展就可以簡單了一些。
nsFile類別
/* * class for file */ // define SEEK method. #ifndef SEEK_SET #define SEEK_SET 0 #endif #ifndef SEEK_CUR #define SEEK_CUR 1 #endif #ifndef SEEK_END #define SEEK_END 2 #endif class nsFile { public: enum StateType_E { IFEXIST = 0x01, IFDIR = 0x02, IFREG = 0x04 }; static const char DELM; static const char* DELM_STR; static const wchar_t DELM_W; static const wchar_t* DELM_STR_W; static int stat(const nsUChar_T* filename); static bool isAbsPath(const nsUChar_T* full_path); static bool isExecutable(const nsUChar_T* filename); static long getSize(const nsUChar_T* filename); static bool copy(const nsUChar_T* src, const nsUChar_T* dest); static bool remove(const nsUChar_T* filename); static bool move(const nsUChar_T* src, const nsUChar_T* dest); static bool isDir(const nsUChar_T* filename) { return (stat(filename)&IFDIR)?true:false; } static bool isRegular(const nsUChar_T* filename) { return (stat(filename)&IFREG)?true:false; } static bool isExist(const nsUChar_T* filename) { return (stat(filename)&IFEXIST)?true:false; } enum Mode_E { eREAD = 0x01, eWRITE = 0x02, //eAPPEND = 0x04 }; nsFile(); ~nsFile(); bool open(const nsUChar_T* path, int mode); void close(); void flush(); U64 size(); U64 seek(U64 offset, int origin); U64 tell(); U32 read(void*, U32, U32); U32 write(void*, U32, U32); private: int _mode; #ifdef NS_PLATFORM_WINDOWS FILE* _fd; #elif NS_APPLE FILE* _fd; #else int _fd; //FILE* _fd; #endif };
nsFile類別實作範例
bool nsFile::open(const nsUChar_T* path, int mode) { _mode = mode; #ifdef NS_PLATFORM_WIN32 if (mode&eREAD) { _fd = _wfopen((wchar_t*)path, L"rb+"); } else if (mode&eWRITE) { _fd = _wfopen((wchar_t*)path, L"wb+"); } #elif NS_PLATFORM_OSX static char cpath[1024]; memset(cpath, 0, 1024); nsUStr::toMultiBytes(path, cpath, 1024); if (mode&eREAD) { _fd = fopen(cpath, "rb+"); } else if (mode&eWRITE) { _fd = fopen(cpath, "wb+"); } #else static char cpath[1024]; memset(cpath, 0, 1024); nsUStr::toMultiBytes(path, cpath, 1024); if (mode&eREAD) { _fd = ::open(cpath, O_RDONLY); } else if (mode&eWRITE) { _fd = ::open(cpath, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU|S_IRWXG|S_IRWXO); } endif return ((_fd)?true:false); } void nsFile::close() { if (_fd) { #ifdef NS_PLATFORM_WINDOWS fclose(_fd); #elif NS_APPLE fclose(_fd); #else ::close(_fd); #endif } }
其實,上面的範例其實可以在Windows,OSX,Linux(Android)三個平台正常使用的,是我已經驗證過的。