C++/WinRT 中的字串處理

使用 C++/WinRT 時,你可以使用 C++ 標準函式庫的寬字串類型(例如 std::wstring)呼叫 Windows 執行階段 API(注意:不適用於像 std::string 這類窄字串型別)。 C++/WinRT 有一個自訂字串類型稱為 winrt::hstring(定義於 C++/WinRT 基礎函式庫中)。%WindowsSdkDir%Include\<WindowsTargetPlatformVersion>\cppwinrt\winrt\base.h 而這正是 Windows 執行階段 建構子、函式和屬性實際接收並回傳的字串類型。 但在許多情況下,多虧了 hstring 的轉換建構函式和轉換運算子,你可以選擇是否在用戶端程式碼中留意 hstring 的存在。 如果你在 撰寫 API,那麼你更可能需要了解 hstring

C++ 中有許多字串類型。 除了 C++ 標準函式庫的 std::basic_string 外,還有許多函式庫中的變體。 C++17 有字串轉換工具和 std::basic_string_view,用來彌補所有字串類型之間的空隙。 WinRT::hstring 提供 STD::wstring_view 的可轉換性,以實現 STD::basic_string_view 設計時的互通性。

使用 std::wstring(以及選擇性地使用 winrt::hstring)搭配 Uri

Windows::Foundation::Uri 是由 winrt::hstring 構成的。

public:
    Uri(winrt::hstring uri) const;

hstring轉換建構器 ,讓你不用特別注意就能操作它。 這裡有一個程式碼範例,示範如何從寬字串字面值、寬字串視圖和 std::wstring 製作 Uri

#include <winrt/Windows.Foundation.h>
#include <string_view>

using namespace winrt;
using namespace Windows::Foundation;

int main()
{
    using namespace std::literals;

    winrt::init_apartment();

    // You can make a Uri from a wide string literal.
    Uri contosoUri{ L"http://www.contoso.com" };

    // Or from a wide string view.
    Uri contosoSVUri{ L"http://www.contoso.com"sv };

    // Or from a std::wstring.
    std::wstring wideString{ L"http://www.adventure-works.com" };
    Uri awUri{ wideString };
}

屬性存取器 Uri::D omain 屬於 hstring 類型。

public:
    winrt::hstring Domain();

不過,再說一次,多虧 hstring轉換為 std::wstring_view 的轉換運算子,你不一定需要了解那個細節。

// Access a property of type hstring, via a conversion operator to a standard type.
std::wstring domainWstring{ contosoUri.Domain() }; // L"contoso.com"
domainWstring = awUri.Domain(); // L"adventure-works.com"

// Or, you can choose to keep the hstring unconverted.
hstring domainHstring{ contosoUri.Domain() }; // L"contoso.com"
domainHstring = awUri.Domain(); // L"adventure-works.com"

同樣地, IStringable::ToString 回傳 hstring。

public:
    hstring ToString() const;

Uri 實作 IStringable 介面。

// Access hstring's IStringable::ToString, via a conversion operator to a standard type.
std::wstring tostringWstring{ contosoUri.ToString() }; // L"http://www.contoso.com/"
tostringWstring = awUri.ToString(); // L"http://www.adventure-works.com/"

// Or you can choose to keep the hstring unconverted.
hstring tostringHstring{ contosoUri.ToString() }; // L"http://www.contoso.com/"
tostringHstring = awUri.ToString(); // L"http://www.adventure-works.com/"

你可以用 hstring::c_str 函式hstring 取得標準寬字串(就像從 std::wstring 取得一樣)。

#include <iostream>
std::wcout << tostringHstring.c_str() << std::endl;

如果你有 hstring ,那你就可以用它來做 Uri

Uri awUriFromHstring{ tostringHstring };

考慮一個接受 hstring 的方法。

public:
    Uri CombineUri(winrt::hstring relativeUri) const;

你剛剛看到的所有選項同樣適用於這種情況。

std::wstring contact{ L"contact" };
contosoUri = contosoUri.CombineUri(contact);
    
std::wcout << contosoUri.ToString().c_str() << std::endl;

hstring 有一個成員 std::wstring_view 轉換運算子,且轉換過程不需額外成本。

void legacy_print(std::wstring_view view);

void Print(winrt::hstring const& hstring)
{
    legacy_print(hstring);
}

winrt::hstring 函式與運算元

winrt::hstring 實作了許多建構子、運算子、函式和迭代器。

hstring 是一種範圍型別,因此你可以將它用於以 range 為基礎的 for,或搭配 std::for_each 使用。 它也提供比較運算子,讓使用者能自然且有效地與 C++ 標準函式庫中的對應運算子進行比較。 而且它包含了使用 hstring 作為關聯容器鍵所需的一切。

我們知道許多 C++ 函式庫使用 std::string,且專門處理 UTF-8 文字。 為了方便,我們提供協助工具,例如 winrt::to_stringwinrt::to_hstring,方便來回轉換。

WINRT_ASSERT 是一個巨集定義,並擴展為 _ASSERTE

winrt::hstring w{ L"Hello, World!" };

std::string c = winrt::to_string(w);
WINRT_ASSERT(c == "Hello, World!");

w = winrt::to_hstring(c);
WINRT_ASSERT(w == L"Hello, World!");

想了解更多關於 hstring 函式和運算子的範例和資訊,請參考 winrt::hstring API 參考主題。

使用 winrt::hstringwinrt::param::hstring 的理由

Windows 執行階段以wchar_t字元實作,但Windows 執行階段的應用程式二進位介面(ABI)並非 std::wstringstd::wstring_view 所提供的子集。 使用這些會造成顯著的效率低下。 取而代之的是,C++/WinRT 提供 winrt::hstring,代表一個與底層 HSTRING 一致的不可變字串,並在類似 std::wstring 的介面下實作。

你可能會注意到,C++/WinRT 輸入參數在邏輯上應接受 winrt::hstring,但實際上卻預期接受 winrt::param::hstring參數命名空間包含一組專門用於優化輸入參數,使其自然綁定 C++ 標準函式庫型別,避免複製及其他低效率問題。 你不應該直接使用這些類型。 如果你想用優化方法來優化自己的函式,那就用 std::wstring_view。 另見 「將參數傳入 ABI 邊界」。

也就是說,你大致上可以不必理會 Windows 執行階段 字串管理的細節,直接運用你熟悉的方式有效率地工作。 這點很重要,因為 Windows 執行階段 中字串的使用量非常高。

字串格式化

字串格式化的一個選項是 std::wostringstream。 這裡有一個範例,可以格式化並顯示一個簡單的除錯追蹤訊息。

#include <sstream>
#include <winrt/Microsoft.UI.Input.h>
#include <winrt/Microsoft.UI.Xaml.Input.h>
...
void MainPage::OnPointerPressed(winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& e)
{
    winrt::Windows::Foundation::Point const point{ e.GetCurrentPoint(nullptr).Position() };
    std::wostringstream wostringstream;
    wostringstream << L"Pointer pressed at (" << point.X << L"," << point.Y << L")" << std::endl;
    ::OutputDebugString(wostringstream.str().c_str());
}

設定屬性的正確方法

你透過將值傳給設定函式來設定屬性。 以下是範例。

// The right way to set the Text property.
myTextBlock.Text(L"Hello!");

以下程式碼不正確。 它會編譯,但所做的只是修改 Text() 存取器函式回傳的暫時 winrt::hstring,然後丟棄結果。

// *Not* the right way to set the Text property.
myTextBlock.Text() = L"Hello!";

重要 API