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::Uriwinrt::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::Domain 的类型为 hstring

public:
    winrt::hstring Domain();

但是,再说一遍,多亏了 hstringstd::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 是一种范围类型,因此你可以将它用于基于范围的 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());
}

设置属性的正确方法

通过将值传递给 setter 函数来设置属性。 下面是一个示例。

// 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