Control de cadenas en C++/WinRT

Con C++/WinRT, puede llamar a las API de Windows Runtime mediante tipos de cadenas anchas de la biblioteca estándar de C++ como std::wstring (nota: no con tipos de cadena estrechos como std::string). C++/WinRT tiene un tipo de cadena personalizado denominado winrt::hstring (definido en la biblioteca base de C++/WinRT, que es %WindowsSdkDir%Include\<WindowsTargetPlatformVersion>\cppwinrt\winrt\base.h). Y ese es el tipo de cadena que los constructores, las funciones y las propiedades de Windows Runtime realmente aceptan y devuelven. Pero en muchos casos —gracias a los constructores y operadores de conversión de hstring— puedes elegir si quieres tener presente o no hstring en el código cliente. Si está desarrollando APIs, es más probable que necesite saber qué es hstring.

Hay muchos tipos de cadena en C++. Existen variantes en muchas bibliotecas además de std::basic_string de la biblioteca estándar de C++. C++17 tiene utilidades de conversión de cadenas y std::basic_string_view, para salvar las brechas entre todos los tipos de cadena. winrt::hstring proporciona convertibilidad con std::wstring_view para proporcionar la interoperabilidad para la que se diseñó std::basic_string_view .

Uso de std::wstring (y, opcionalmente, winrt::hstring) con Uri

Windows::Foundation::Uri se construye a partir de winrt::hstring.

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

Pero hstring tiene constructores de conversión que le permiten trabajar con él sin necesidad de tenerlo en cuenta. Este es un ejemplo de código que muestra cómo crear un URI a partir de un literal de cadena ancho, desde una vista de cadena ancha y desde un 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 };
}

El descriptor de acceso a la propiedad Uri::Domain es de tipo hstring.

public:
    winrt::hstring Domain();

Pero, de nuevo, conocer ese detalle es opcional gracias al operador de conversión de hstringa 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"

Del mismo modo, IStringable::ToString devuelve hstring.

public:
    hstring ToString() const;

Uri implementa la interfaz 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/"

Puede usar la función hstring::c_str para obtener una cadena ancha estándar de un hstring (igual que puede desde std::wstring).

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

Si tiene un hstring , puede hacer un URI a partir de él.

Uri awUriFromHstring{ tostringHstring };

Considere un método que acepta un hstring.

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

Todas las opciones que acaba de ver también se aplican en estos casos.

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

hstring tiene un operador de conversión std::wstring_view miembro y la conversión se logra sin costo alguno.

void legacy_print(std::wstring_view view);

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

Funciones y operadores winrt::hstring

Se implementa un host de constructores, operadores, funciones e iteradores para winrt::hstring.

Una hstring es un rango, por lo que puede usarla con forbasado en rangos o con std::for_each. También proporciona operadores de comparación para comparar de forma natural y eficaz sus homólogos en la biblioteca estándar de C++. Además, incluye todo lo que necesita para usar hstring como clave para contenedores asociativos.

Reconocemos que muchas bibliotecas de C++ usan std::string y funcionan exclusivamente con texto UTF-8. Como comodidad, proporcionamos asistentes, como winrt::to_string y winrt::to_hstring, para convertir hacia atrás y hacia adelante.

WINRT_ASSERT es una definición de macro y se expande a _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!");

Para obtener más ejemplos e información sobre las funciones y operadores de hstring , consulta el tema de referencia de la API winrt::hstring .

La justificación de winrt::hstring y winrt::param::hstring

El Windows Runtime se implementa en términos de caracteres de wchar_t, pero la interfaz binaria de aplicaciones (ABI) del Windows Runtime no es un subconjunto de lo que proporcionan std::wstring o std::wstring_view. El uso de estos provocaría una ineficacia significativa. En su lugar, C++/WinRT proporciona winrt::hstring, que representa una cadena inmutable coherente con el HSTRING subyacente e implementada detrás de una interfaz similar a la de std::wstring.

Es posible que observe que los parámetros de entrada de C++/WinRT que lógicamente deberían aceptar winrt::hstring en realidad esperan winrt::param::hstring. El espacio de nombres param contiene un conjunto de tipos usados exclusivamente para optimizar los parámetros de entrada para enlazar naturalmente a tipos de biblioteca estándar de C++ y evitar copias y otras ineficiencias. No se recomienda usar estos tipos directamente. Si desea usar una optimización para sus propias funciones, use std::wstring_view. Consulte también Pasar parámetros al límite de ABI.

La conclusión es que puedes ignorar en gran medida los detalles de la administración de cadenas de Windows Runtime y simplemente trabajar eficazmente con lo que ya conoces. Y eso es importante, dada la gran cantidad de cadenas que se usan en el Windows Runtime.

Formato de cadenas

Una opción para el formato de cadena es std::wostringstream. Este es un ejemplo que da formato y muestra un mensaje de seguimiento de depuración simple.

#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());
}

La manera correcta de establecer una propiedad

Puede establecer una propiedad pasando un valor a una función de asignación. Este es un ejemplo.

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

El código siguiente es incorrecto. Compila, pero no hace más que modificar el winrt::hstring temporal devuelto por la función de acceso Text() y luego descartar el resultado.

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

API importantes