您好,登錄后才能下訂單哦!
這篇文章主要講解了“C++跨平臺開發遇到的問題有哪些”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“C++跨平臺開發遇到的問題有哪些”吧!
在我的開發環境,clang 編碼默認是 utf8, VS 是 GB2312(代碼頁是 936),它們都兼容 ASCII。
假如代碼文件中只出現英文,兩端都可編譯。假如代碼中出現中文,文件編碼為 utf8, iOS 編譯沒有問題,VS 會出現編譯錯誤 error C2001。假如設置編碼為 utf16, VS 編譯沒有問題,而 iOS 會出現編譯錯誤 encoding is not supported。因此假如代碼有中文,需要將源文件編碼修改為 Unicode(UTF8 帶簽名)- 代碼頁 65001。
參見 vs編譯 error C2001: 常量中有換行符 中文無法通過編譯
另外假如包含中文字符串,直接讀取使用,程序運行起來很容易出現亂碼。類似這樣的代碼:
const char* str = "你好啊,世界";
想將 str 的文字在不同平臺都顯示正確,是不可控的。跨平臺代碼不應該使用中文,絕對不能用中文定義字符串再讀取,更嚴格的甚至不能用中文寫注釋。假如要顯示中文字符串,應該將其從程序中分離出來,寫在一個 utf8 編碼的配置文件中,再動態讀取。
我們就碰坑了,我們本意是在導出一個 lua Api 的時候,自動生成對應的文檔。有類似這樣的代碼:
ADD_METHOD_WITH_DOC(Context,nv12ToRGBPass, "獲取顏色空間 nv12 到 rgb 的著色器程序","3.2","[Program](#program)", "program",0)
后來發覺在 Windows 上編譯通過,iOS 編譯不過。修改編碼后, iOS 編譯過了,Windows 上又編譯不過。當兩端都編譯過了,但又亂碼了。最后只好都寫成英文,自動生成英文文檔。
在 VS 上,int8_t
實際上是 char 的 typedef,也就是說 int8_t
和 char 是同類型的。但是在 iOS 上,int8_t 和 char 是不同類型的。下列代碼
printf("%d\n", (int)std::is_same<int8_t, char>::value);
在 VS 上輸出 1,在 iOS 上輸出 0。這刷新我認知,我一直以為 char 和 int8_t 是相同的。因為這區別,又踩坑了。
為了方便寫 lua 導出,我們用了 LuaCpp 的庫,里面有這代碼
typedef LOKI_TYPELIST_15(bool, char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, float, double,std::string, luaObject, luatable, int64_t) SupportType;
這里定義了一個 Loki 的 typelist, 包含支持自動轉換的類型。typelist 參見書籍C++設計新思維。
LuaCpp 基本都是模板代碼,假如類型 T 屬于 SupportType,就可執行自動轉換的代碼,不然就需要手寫轉換,假如沒有手寫轉換,對于此類型 T, 就會直接崩掉。
這里的代碼很老了,一直都運行正常。直到某個接口出現了 int8_t,于是 VS 上運行正常,iOS 上崩潰了。
同理,int8_t, uint8_t, int16_t, uint16_t 也需要注意。
類似的模板代碼,最好還是乖乖地使用標準庫中的 std::is_integral 吧。不要那么聰明自己手寫 typelist 了。
__VA_ARGS__ 可用于不定參數的宏。但是它的行為在 VS 和 clang 上是有區別的。如下面代碼
#include <iostream>#define MY_PRINT(format, ...) printf(format, __VA_ARGS__)int main(int argc, const char* argv[]){MY_PRINT("Hello, World");return 0;}
在 VS 上可以編譯通過。但在 clang 上確實編譯失敗,clang 編譯器的 __VA_ARGS__ 不能展開 0 個變長參數的。寫成
MY_PRINT("Hello, World, %d", 1);
才可以正確展開。為了展開 0 個參數,需要寫成 ##__VA_ARGS__, 定義為
#define MY_PRINT(format, ...) printf(format, ##__VA_ARGS__)
參考 Variadic macros with zero arguments
一個工程經常有多個動態模塊。在 VS 上,動態模塊為 dll 文件; iOS 上為 framework 或者 dylib。VS 在跨模塊時,默認符號是不導出的。clang 默認符號都是導出的。
在 VS 上,當想在 A 模塊中定義某個類或者某個函數,讓 B 模塊使用,就需要使用 __declspec(dllexport)
、__declspec(dllimport)
標明。通常會定義一些宏,比如。
#if defined(OF_WIN32) || defined(_WIN32) || defined(WIN32)# ifdef MODULE_A_API_LIB# define MODULE_A_API __declspec(dllexport)# else# define MODULE_A_API __declspec(dllimport)# endif#else# define MODULE_A_API#endif
之后需要跨模塊使用的函數或者類寫成
class MODULE_A_API TestClass {};MODULE_A_API void myfunction(int a, int b);
通常都沒有問題,假如忘記寫導出,就會鏈接錯誤。但一旦涉及到模板和靜態變量,這種平臺的差別,就會是個坑。
模板代碼通常會直接寫在頭文件中,比如下代碼。
// myheader.htemplate <typename T>class TemplateClass {public:static std::string str;};template <typename T>std::string TemplateClass<T>::str;
在模塊中,包含了頭文件 myheader.h,就可以使用 TemplateClass 了。假如這時模塊 A 使用語句設置 str 的值
TemplateClass<int>::str = "Hello, World";
之后模塊 B 讀取 str 的值。
std::string str = TemplateClass<int>::str;
在 VS 中,模塊 A 和模塊 B 雖然都使用 TemplateClass<int>,但因為沒有導出,實際是分離的兩個類,他們的靜態變量并不會共享。于是就是模塊 A 設置了 TemplateClass<int>::str,模塊 B 讀取的還是默認的空值。
而在 clang 編譯器中,默認是導出的。于是模塊 A 和模塊 B 看到的是相同的 TemplateClass<int>,靜態變量是共享的。于是模塊 A 設置了 TemplateClass<int>::str,模塊 B 讀取的是設置后的 "Hello, World"
這種 Bug 比較隱蔽,可以正常編譯,也可以運行,但實際結果就是不對。我們就踩過類似的坑。
前文說過,我們使用了 LuaCpp 這個庫來導出 lua。這個庫是個模板庫,它包含一些靜態變量,用來實現自動注冊。我們在模塊 A 中注冊了一批 lua 類。之后在模塊 B 中往 lua 虛擬機壓注冊過的類對象,在 iOS 上運行正常,但在 Windows 上就異常。因為在模塊 B 中看來,LuaCpp 的記錄中,這些類根本就沒有被注冊過。
感謝各位的閱讀,以上就是“C++跨平臺開發遇到的問題有哪些”的內容了,經過本文的學習后,相信大家對C++跨平臺開發遇到的問題有哪些這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。