Обработка строк в C++/WinRT
С помощью C++/WinRT можно вызывать интерфейсы API среды выполнения Windows, используя типы широких строк стандартной библиотеки C++, такие как std::wstring (но не типы узких строк, такие как std::string). В C++/WinRT имеется настраиваемый строковый тип winrt::hstring (определенный в базовой библиотеке C++/WinRT — %WindowsSdkDir%Include\<WindowsTargetPlatformVersion>\cppwinrt\winrt\base.h
). Этот строковый тип фактически принимают и возвращают конструкторы, функции и свойства среды выполнения Windows. Но, во многих случаях благодаря конструкторам преобразования hstring и операторам преобразования можно выбирать, следует ли учитывать hstring в коде клиента. Если вы разрабатываете интерфейсы API, вероятность того, что вам нужно знать о hstring, возрастает.
В 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 имеет конструкторы преобразования, позволяющие работать с ним, не учитывая этот факт. Ниже приведен пример кода, показывающий, как создать Uri из литерала широкой строки, представления широкой строки и std::wstring.
#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();
Но опять же, знать об этом не обязательно благодаря оператору преобразования 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 является диапазоном, поэтому вы можете использовать его с основанным на диапазоне for
или с std::for_each
. Он также предоставляет операторы сравнения для естественного и эффективного сравнения с его аналогами в стандартной библиотеке C++. Кроме того, он включает в себя все, что необходимо для использования hstring в качестве основы для ассоциативных контейнеров.
Мы понимаем, что многие библиотеки C++ используют std::string и работают только с текстом UTF-8. Для удобства мы предоставляем вспомогательные методы, такие как winrt::to_string и winrt::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 приведены в справочных материалах по API winrt::hstring.
Обоснование для winrt::hstring и winrt::param::hstring
Среда выполнения Windows реализуется в знаках wchar_t, но двоичный интерфейс приложения (ABI) среды выполнения Windows не является подмножеством того, что предоставляют std::wstring или std::wstring_view. Их использование приведет к значительной неэффективности. Вместо этого C++/WinRT предоставляет winrt::hstring, представляющий собой неизменяемую строку, согласованную с базовым HSTRING и реализованную за интерфейсом, аналогичным std::wstring.
Можно заметить, что входные параметры C++/ WinRT, которые логически должны принимать winrt::hstring, фактически ожидают winrt::param::hstring. Пространство имен param содержит набор типов, используемых исключительно для оптимизации входных параметров для естественной привязки к типам стандартной библиотеки C++, а также позволяющих избежать копий и других аспектов, снижающих эффективность. Эти типы не следует использовать напрямую. Если вы хотите использовать оптимизацию для собственных функций, применяйте std::wstring_view. Также см. статью о передаче параметров в интерфейс ABI.
Идея состоит в том, что вы можете по большей части игнорировать особенности управления строками среды выполнения Windows и просто эффективно работать с тем, что вам известно. Это важно, учитывая, насколько активно строки используются в среде выполнения Windows.
Форматирование строк
Одно из средств форматирования строк — std::wstringstream. Вот пример, который форматирует и отображает простое сообщение отладочной трассировки.
#include <sstream>
#include <winrt/Windows.UI.Input.h>
#include <winrt/Windows.UI.Xaml.Input.h>
...
void MainPage::OnPointerPressed(winrt::Windows::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!");
Ниже приведен неправильный код. Компиляция происходит, но все, что выполняется — это изменение значения winrt::hstring, возвращаемое функцией получения доступаText(), а затем результаты отклоняются.
// *Not* the right way to set the Text property.
myTextBlock.Text() = L"Hello!";