Manipulação de cadeia de caracteres em C++/WinRT

Com o C++/WinRT, você pode chamar APIs Windows Runtime usando tipos de cadeia de caracteres largos da Biblioteca Padrão C++, como std::wstring (observação: não com tipos de cadeia de caracteres estreitos, como std::string). O C++/WinRT tem um tipo de cadeia de caracteres personalizado chamado winrt::hstring (definido na biblioteca base C++/WinRT, que é %WindowsSdkDir%Include\<WindowsTargetPlatformVersion>\cppwinrt\winrt\base.h). E esse é o tipo de cadeia de caracteres que os construtores, as funções e as propriedades do Windows Runtime de fato aceitam e retornam. Mas, em muitos casos, graças aos construtores de conversão e operadores de conversão da hstring, você pode escolher se deseja ou não estar ciente do hstring no código do cliente. Se você estiver criando APIs, é mais provável que precise saber mais sobre hstring.

Há muitos tipos de cadeia de caracteres no C++. As variantes existem em muitas bibliotecas, além de std::basic_string da Biblioteca Padrão do C++. O C++17 tem utilitários de conversão de cadeia de caracteres e std::basic_string_view, para preencher as lacunas entre todos os tipos de cadeia de caracteres. winrt::hstring fornece conversibilidade com std::wstring_view para fornecer a interoperabilidade para a qual std::basic_string_view foi projetado.

Usando std::wstring (e opcionalmente winrt::hstring) com Uri

Windows::Foundation::Uri é construído a partir de um winrt::hstring.

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

Mas o hstring tem construtores de conversão que permitem que você trabalhe com ele sem precisar estar ciente dele. Aqui está um exemplo de código mostrando como fazer um Uri de um literal de cadeia de caracteres largo, de uma exibição de cadeia de caracteres larga e de um 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 };
}

O acessador de propriedade Uri::Domain é do tipo hstring.

public:
    winrt::hstring Domain();

Mas, novamente, estar ciente desse detalhe é opcional graças ao operador de conversão do hstringpara 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"

Da mesma forma, IStringable::ToString retorna hstring.

public:
    hstring ToString() const;

O Uri implementa a interface 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/"

Você pode usar a função hstring::c_str para obter uma cadeia de caracteres larga padrão de um hstring (assim como você pode de um std::wstring).

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

Se você tem um hstring , então você pode fazer um Uri a partir dele.

Uri awUriFromHstring{ tostringHstring };

Considere um método que usa um hstring.

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

Todas as opções que você acabou de ver também se aplicam nesses casos.

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

o hstring tem um operador de conversão std::wstring_view membro e a conversão é obtida sem custo.

void legacy_print(std::wstring_view view);

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

operadores e funções winrt::hstring

Um host de construtores, operadores, funções e iteradores são implementados para winrt::hstring.

Um hstring é um intervalo, portanto, você pode usá-lo com base em forintervalo ou com std::for_each. Ele também fornece operadores de comparação para comparação natural e eficiente com seus equivalentes na Biblioteca Padrão do C++. E inclui tudo o que você precisa para usar o hstring como uma chave para contêineres associativos.

Reconhecemos que muitas bibliotecas C++ usam std::string e funcionam exclusivamente com texto UTF-8. Como conveniência, fornecemos auxiliares, como winrt::to_string e winrt::to_hstring, para converter para frente e para trás.

WINRT_ASSERT é uma definição de macro e se expande para _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 obter mais exemplos e informações sobre operadores e funções de hstring , consulte o tópico de referência da API winrt::hstring .

A justificativa para winrt::hstring e winrt::param::hstring

O Windows Runtime é implementado em termos de caracteres wchar_t, mas a ABI (Interface Binária de Aplicativo) do Windows Runtime não é um subconjunto do que std::wstring ou std::wstring_view fornecem. Usá-los levaria a uma ineficiência significativa. Em vez disso, C++/WinRT fornece winrt::hstring, que representa uma cadeia de caracteres imutável consistente com o HSTRING subjacente e implementada por trás de uma interface semelhante à de std::wstring.

Você pode notar que os parâmetros de entrada do C++/WinRT que, logicamente, deveriam aceitar winrt::hstring na verdade esperam winrt::param::hstring. O namespace param contém um conjunto de tipos usados exclusivamente para otimizar parâmetros de entrada, para que se vinculem naturalmente aos tipos da Biblioteca Padrão do C++ e evitem cópias e outras ineficiências. Você não deve usar esses tipos diretamente. Se você quiser usar uma otimização para suas próprias funções, use std::wstring_view. Veja também Passando parâmetros pela fronteira da ABI.

O resultado é que você pode ignorar em grande parte as especificidades do gerenciamento de cadeias de caracteres Windows Runtime e apenas trabalhar com eficiência com o que você sabe. E isso é importante, dado o quanto as cadeias de caracteres são usadas no Windows Runtime.

Formatação de cadeias de caracteres

Uma opção para formatação de cadeia de caracteres é std::wostringstream. Aqui está um exemplo que formata e exibe uma mensagem de rastreamento de depuração simples.

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

A maneira correta de definir uma propriedade

Você define uma propriedade passando um valor para uma função setter. Veja um exemplo.

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

O código abaixo está incorreto. Ele compila, mas tudo o que faz é modificar o winrt::hstring temporário retornado pela função de acesso Text() e, em seguida, descartar o resultado.

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

APIs importantes