Gestione delle stringhe in C++/WinRT

Con C++/WinRT puoi chiamare API di Windows Runtime utilizzando tipi di stringa estesi della libreria standard C++ come std::wstring (non con tipi di stringa stretti come std::string). C++/WinRT ha un tipo di stringa personalizzata chiamato winrt::hstring (definita nella libreria di base C++/WinRT, ovvero %WindowsSdkDir%Include\<WindowsTargetPlatformVersion>\cppwinrt\winrt\base.h). Questo è il tipo di stringa che le proprietà, le funzioni e i costruttori di Windows Runtime effettivamente accettano e restituiscono. In molti casi, grazie agli operatori e ai costruttori di conversione di hstring, puoi scegliere di essere a conoscenza o meno di hstring nel codice client. Se crei API, è probabile che tu abbia bisogno di conoscere hstring.

Esistono molti tipi di stringa in C++. Esistono varianti in molte librerie oltre a std::basic_string della libreria standard C++. C++17 include utilità di conversione di stringa e std::basic_string_view per colmare il divario tra tutti i tipi di stringa. winrt::hstring fornisce convertibilità con std::wstring_view per fornire l'interoperabilità per cui std::basic_string_view è stato progettato.

Uso di std::wstring (e facoltativamente di winrt::hstring) con Uri

Windows::Foundation::Uri è costruito a partire da un winrt::hstring.

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

Ma hstring ha costruttori di conversione che ti consentono di utilizzarlo senza la necessità di tenerne conto. Ecco un esempio di codice che illustra come creare un Uri da un valore letterale di stringa estesa, a partire da una visualizzazione di stringa estesa e da 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 };
}

La funzione di accesso della proprietà Uri::Domain è di tipo hstring.

public:
    winrt::hstring Domain();

Ma, ancora una volta, devi sapere che questo dettaglio è facoltativo grazie all'operatore di conversione in std::wstring_view di hstring.

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

Analogamente, IStringable::ToString restituisce hstring.

public:
    hstring ToString() const;

Uri implementa l'interfaccia 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/"

Puoi utilizzare la funzione hstring::c_str function per ottenere una stringa estesa standard da un hstring (come da un std::wstring).

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

Se hai un hstring puoi creare un Uri da quest'ultimo.

Uri awUriFromHstring{ tostringHstring };

Prendi in considerazione un metodo che accetta un hstring.

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

Tutte le opzioni che abbiamo appena visto si applicano anche in questi casi.

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

hstring dispone di un operatore di conversione std::wstring_view membro e la conversione viene ottenuta gratuitamente.

void legacy_print(std::wstring_view view);

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

Operatori e funzioni winrt::hstring

Una serie di costruttori, operatori, funzioni e iteratori è implementata per winrt::hstring.

Un hstring è un intervallo, pertanto puoi utilizzarlo con for basato sull'intervallo o con std::for_each. Fornisce inoltre operatori di confronto per un confronto naturale ed efficace con le controparti della libreria standard C++. Include tutto ciò di cui hai bisogno per utilizzare hstring come chiave per i contenitori associativi.

Riconosciamo che molte librerie C++ utilizzano std::string e funzionano esclusivamente con testo UTF-8. Per comodità, forniamo gli helper per la conversione in entrambi i sensi, ad esempio winrt::to_string e winrt::to_hstring.

WINRT_ASSERT è una definizione di macro e si espande in 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!");

Per ulteriori esempi e info su funzioni e operatori hstring, vedi l'argomento di riferimento sull'API per winrt::hstring.

La logica per winrt::hstring e winrt::param::hstring

Windows Runtime viene implementato in termini di caratteri wchar_t, ma l'ABI (Application Binary Interface) di Windows Runtime non è un sottoinsieme di ciò che fornisce std::wstring o std::wstring_view. Il loro uso provocherebbe una significativa inefficienza. Invece, C++/WinRT fornisce winrt::hstring, che rappresenta una stringa non modificabile coerente con l'HSTRING sottostante e implementata dietro un'interfaccia simile a quella di std::wstring.

Potresti notare che i parametri di input C++/WinRT che devono accettare dal punto di vista logico winrt::hstring in realtà si aspettano winrt::param::hstring. Lo spazio dei nomi param contiene un set di tipi utilizzati esclusivamente per ottimizzare i parametri di input per eseguire il binding ai tipi della libreria standard C++ in modo naturale ed evitare copie o altre inefficienze. Ti consigliamo di non utilizzare questi tipi direttamente. Se desideri utilizzare un'ottimizzazione per le tue funzioni, utilizza std::wstring_view. Vedi anche Passaggio di parametri nel limite ABI.

Il risultato è che puoi ignorare in gran parte le specifiche di gestione delle stringhe Windows Runtime e utilizzare con efficienza le tue conoscenze. Questo è importante, considerato l'utilizzo intenso di stringhe in Windows Runtime.

Formattazione di stringhe

Un'opzione per la formattazione di stringhe è std::wostringstream. Ecco un esempio che formatta e visualizza un messaggio di traccia di debug semplice.

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

Il modo corretto per impostare una proprietà

Per impostare una proprietà è necessario passare un valore a una funzione setter. Ecco un esempio.

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

Il codice seguente non è corretto. Esegue la compilazione, ma si limita a modificare il valore di winrt::hstring temporaneo restituito dalla funzione di accesso Text() e a cancellare il risultato.

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

API importanti