Tipos de dados C++ e C++/WinRT padrão

Com C++/WinRT, é possível chamar APIs do Windows Runtime usando tipos de dados C++ padrão, incluindo alguns tipos de dados da Biblioteca Padrão do C++. É possível passar cadeias de caracteres padrão para as APIs (confira Processamento da cadeia de caracteres em C++/WinRT) e passar as listas de inicializadores e contêineres padrão para APIs que esperam uma coleção semanticamente equivalente.

Confira também Passagem de parâmetros para o limite do ABI.

Listas de inicializadores padrão

Uma lista de inicializadores (std::initializer_list) é um constructo da Biblioteca Padrão do C++. Use listas de inicializadores para chamar determinados construtores e métodos do Windows Runtime. Por exemplo, chame com DataWriter::WriteBytes com uma lista dessas.

#include <winrt/Windows.Storage.Streams.h>

using namespace winrt::Windows::Storage::Streams;

int main()
{
    winrt::init_apartment();

    InMemoryRandomAccessStream stream;
    DataWriter dataWriter{stream};
    dataWriter.WriteBytes({ 99, 98, 97 }); // the initializer list is converted to a winrt::array_view before being passed to WriteBytes.
}

Há duas partes envolvidas ao executar essa tarefa. Primeiro, o método DataWriter::WriteBytes usa um parâmetro do tipo winrt::array_view.

void WriteBytes(winrt::array_view<uint8_t const> value) const

winrt::array_view é um tipo C++/WinRT personalizado que representa com segurança uma série contígua de valores (ele é definido na biblioteca base do C++/WinRT, que é %WindowsSdkDir%Include\<WindowsTargetPlatformVersion>\cppwinrt\winrt\base.h).

Segundo, winrt::array_view tem um construtor de lista de inicializadores.

template <typename T> winrt::array_view(std::initializer_list<T> value) noexcept

Em muitos casos, é possível escolher se deseja ou não ficar ciente de winrt::array_view durante a programação. Se optar por não ficar ciente, não haverá nenhum código para alterar se e quando um tipo equivalente aparecer na Biblioteca Padrão do C++.

É possível passar uma lista de inicializadores para uma API do Windows Runtime que espera um parâmetro de coleção. Pegue StorageItemContentProperties::RetrievePropertiesAsync, por exemplo.

IAsyncOperation<IMap<winrt::hstring, IInspectable>> StorageItemContentProperties::RetrievePropertiesAsync(IIterable<winrt::hstring> propertiesToRetrieve) const;

Chame essa API com uma lista de inicializadores como esta.

IAsyncAction retrieve_properties_async(StorageFile const storageFile)
{
    auto properties{ co_await storageFile.Properties().RetrievePropertiesAsync({ L"System.ItemUrl" }) };
}

Existem dois fatores em jogo aqui. Primeiro, o computador chamado constrói um std::vector a partir da lista de inicializadores (esse computador chamado é assíncrono, portanto, pode ser proprietário desse objeto, e deve ser). Segundo, o C++/WinRT associa std:: Vector de forma transparente (e sem introduzir cópias) como um parâmetro de coleção do Windows Runtime.

Vetores e matrizes padrão

winrt::array_view também tem os construtores de conversão de std::vector e std::array.

template <typename C, size_type N> winrt::array_view(std::array<C, N>& value) noexcept
template <typename C> winrt::array_view(std::vector<C>& vectorValue) noexcept

Então, é possível chamar DataWriter::WriteBytes com um std::vector.

std::vector<byte> theVector{ 99, 98, 97 };
dataWriter.WriteBytes(theVector); // theVector is converted to a winrt::array_view before being passed to WriteBytes.

Ou com um std::array.

std::array<byte, 3> theArray{ 99, 98, 97 };
dataWriter.WriteBytes(theArray); // theArray is converted to a winrt::array_view before being passed to WriteBytes.

O C++/WinRT associa std::vector como um parâmetro de coleção do Windows Runtime. Assim, é possível passar um std::vector<winrt::hstring>, e ele é convertido na coleção apropriada do Windows Runtime de winrt::hstring. Há um detalhe adicional a considerar se o computador chamado for assíncrono. Devido aos detalhes de implementação nesse caso, é preciso fornecer um rvalue, portanto é necessário fornecer uma cópia ou um movimento do vetor. No exemplo de código abaixo, movemos a propriedade do vetor para o objeto do tipo de parâmetro aceito pelo computador chamado assíncrono (e tomamos cuidado para não acessar novamente vecH após movê-lo). Se quiser saber mais sobre rvalues, confira Categorias de valor e referências a elas.

IAsyncAction retrieve_properties_async(StorageFile const storageFile, std::vector<winrt::hstring> vecH)
{
	auto properties{ co_await storageFile.Properties().RetrievePropertiesAsync(std::move(vecH)) };
}

Entretanto, não é possível passar um std::vector<std::wstring> quando se espera uma coleção do Windows Runtime. Isso acontece porque, ao converter para a coleção apropriada do Windows Runtime de std::wstring, a linguagem C++ não forçará os parâmetros de tipo dessa coleção. Consequentemente, o exemplo de código a seguir não será compilado (e, em vez disso, a solução é passar um std::vector<winrt::hstring>, como mostrado acima).

IAsyncAction retrieve_properties_async(StorageFile const storageFile, std::vector<std::wstring> vecW)
{
    auto properties{ co_await storageFile.Properties().RetrievePropertiesAsync(std::move(vecW)) }; // error! Can't convert from vector of wstring to async_iterable of hstring.
}

Matrizes brutas e intervalos de ponteiro

Lembrando que um tipo equivalente pode existir no futuro na Biblioteca Padrão do C++, também é possível trabalhar diretamente com winrt::array_view se quiser ou precisar.

O winrt::array_view tem os construtores de conversão de uma matriz bruta e de um intervalo de T* (ponteiros para o tipo de elemento).

using namespace winrt;
...
byte theRawArray[]{ 99, 98, 97 };
array_view<byte const> fromRawArray{ theRawArray };
dataWriter.WriteBytes(fromRawArray); // the winrt::array_view is passed to WriteBytes.

array_view<byte const> fromRange{ theArray.data(), theArray.data() + 2 }; // just the first two elements.
dataWriter.WriteBytes(fromRange); // the winrt::array_view is passed to WriteBytes.

Operadores e funções de winrt::array_view

Uma série de construtores, operadores, funções e iteradores foram implementados para winrt::hstring. Um winrt::array_view é um intervalo, então é possível usá-lo com for baseado em intervalo ou com std::for_each.

Para saber mais e obter exemplos, confira o tópico de referência de API winrt::array_view.

O IVector<T> e os constructos de iteração padrão

O SyndicationFeed.Items é um exemplo de uma API do Windows Runtime que retorna uma coleção do tipo IVector<T> (projetado no C++/WinRT como winrt::Windows::Foundation::Collections::IVector<T>). Use esse tipo com constructos de iteração padrão, como for baseado em intervalo.

// main.cpp
#include "pch.h"
#include <winrt/Windows.Web.Syndication.h>
#include <iostream>

using namespace winrt;
using namespace Windows::Web::Syndication;

void PrintFeed(SyndicationFeed const& syndicationFeed)
{
    for (SyndicationItem const& syndicationItem : syndicationFeed.Items())
    {
        std::wcout << syndicationItem.Title().Text().c_str() << std::endl;
    }
}

Corrotinas C++ com APIs assíncronas do Windows Runtime

Você pode continuar a usar a PPL (biblioteca de padrões paralelos) ao chamar as APIs assíncronas do Windows Runtime. No entanto, em muitos casos, as corrotinas do C++ fornecem uma linguagem eficiente e mais fácil de codificar para interagir com objetos assíncronos. Para saber mais e obter exemplos de código, confira Simultaneidade e operações assíncronas com C++/WinRT.

APIs importantes