10 月 2022

[翻譯] Eric 的 BSTR 完全攻略

(原文出處:https://ericlippert.com/2003/09/12/erics-complete-guide-to-bstr-semantics/


如果你曾經用 C 或 C++ 寫過任何使用到 COM 物件的程式,你一定看過類似的程式碼:

STDMETHODIMP CFoo:Bar(BSTR bstrABC)
{ ... }

這個 BSTR 到底是三小?它和 WCHAR* 又有什麼區別?

像 C 或 C++ 這種低階語言,你有絕對的自由可以決定:究竟要用什麼方式實作某種概念。Unicode 字串就是個絕佳範例。用 C++ 來表示長度為 n 個字元的 Unicode 字串的標準方法是一個指向 2 * (n + 1) bytes 記憶體空間的指標。這塊空間中的前 2 * n bytes 是用來表示 UTF-16 編碼字元的無號短整數 (unsigned short integers),最後 2 個 bytes 的內容則是 0,用來表示字串的結尾。

將只有 ASCII 範圍的 wstring 轉換成 string

Windows 的程式,若是 Unicode enabled 的話,預設使用的編碼是 UTF-16,每個字元的型別為 wchar_t。這樣的字串,無法使用 std::string 處理,必須改用 std::wstring。但有時候,現有的函式如果只吃 std::string,而且我們的字串又只包含有 ASCII 定義的字元(也就是只有英文及半型符號)的話,我們就必須把 std::wstring 轉成 std::string

有一種比較偷懶的做法:

std::wstring ws(L"Hello");
std::string s(ws.begin(), ws.end());

但是在比較嚴謹的 C++ 編譯器(例如 VS 2022),就會發出警告(因為隱式把 wchar_t 轉換成 char,可能造成資料流失)。想要避開警告,就必須明確使用 static_cast<> 進行資料轉換。此時就可以利用 std::transform() 來做:

std::wstring ws(L"Hello");
std::string s(ws.size(), 0);
std::transform(ws.begin(), ws.end(), s.begin(), [](wchar_t c){
    return static_cast<char>c;
});