C++/WinRT を使用したコレクション

内部的には、Windows ランタイム コレクションには多くの複雑な可動部分があります。 ただし、コレクション オブジェクトをWindows ランタイム関数に渡す場合や、独自のコレクション プロパティとコレクション型を実装する場合は、C++/WinRT にサポートする関数と基底クラスがあります。 これらの機能は複雑さを手から取り出し、時間と労力のオーバーヘッドを大幅に削減します。

IVector は、要素のランダム アクセス コレクションによって実装されるWindows ランタイム インターフェイスです。 IVector を自分で実装する場合は、IIterableIVectorViewおよび IIterator も実装する必要があります。 カスタム コレクション型が 必要 な場合でも、これは多くの作業です。 しかし、std::vector (または std::map、または std::unordered_map) にデータがあり、それをWindows ランタイム API に渡す必要がある場合は、可能であれば、そのレベルの作業は避けたいと思います。 また、C++/WinRT 使用すると、コレクションを効率的かつ少ない労力で作成できるため、これを回避できます。

XAML 項目コントロール、C++/WinRT コレクションへのバインドも参照してください。

コレクションのヘルパー関数

汎用コレクション(空)

このセクションでは、最初は空のコレクションを作成し、そのに内容を追加するシナリオについて説明します。

汎用コレクションを実装する型の新しいオブジェクトを取得するには、 winrt::single_threaded_vector 関数テンプレートを呼び出します。 オブジェクトは IVector として返され、返されたオブジェクトの関数とプロパティを呼び出すインターフェイスです。

次のコード例をコピーして、Windows コンソール アプリケーション (C++/WinRT) プロジェクトのメイン ソース コード ファイルに直接貼り付ける場合は、最初にプロジェクトのプロパティでプリコンパイル済みヘッダーを使用しないを設定します。

// main.cpp
#include <winrt/Windows.Foundation.Collections.h>
#include <iostream>
using namespace winrt;

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

    Windows::Foundation::Collections::IVector<int> coll{ winrt::single_threaded_vector<int>() };
    coll.Append(1);
    coll.Append(2);
    coll.Append(3);

    for (auto const& el : coll)
    {
        std::cout << el << std::endl;
    }

    Windows::Foundation::Collections::IVectorView<int> view{ coll.GetView() };
}

上記のコード例でわかるように、コレクションを作成した後は、要素を追加し、反復処理を行い、一般に、API から受け取る Windows ランタイム のコレクション オブジェクトと同じようにこのオブジェクトを扱うことができます。 コレクションに対して変更できないビューが必要な場合は、次のように IVector::GetView を呼び出すことができます。 上記のパターン (コレクションの作成と使用) は、API にデータを渡したり、API からデータを取得したりする単純なシナリオに適しています。 IVector または IVectorView は、IIterable が必要とされる場所であればどこでも渡すことができます。

上記のコード例では、winrt::init_apartment の呼び出しによって、マルチスレッド アパートメント内のWindows ランタイムのスレッドが既定で初期化されます。 呼び出しによって COM も初期化されます。

データで事前学習された汎用コレクション

このセクションでは、コレクションを作成すると同時に項目を追加するシナリオについて説明します。

前のコード例の Append の呼び出しのオーバーヘッドを回避できます。 既にソース データがある場合や、Windows ランタイム コレクション オブジェクトを作成する前にソース データを設定することもできます。 その方法は次のとおりです。

auto coll1{ winrt::single_threaded_vector<int>({ 1,2,3 }) };

std::vector<int> values{ 1,2,3 };
auto coll2{ winrt::single_threaded_vector<int>(std::move(values)) };

for (auto const& el : coll2)
{
    std::cout << el << std::endl;
}

上記のと同様に、データを含む一時オブジェクトを coll1 に渡すことができます。 または、 std::vector (再びアクセスしない場合) を関数に移動することもできます。 どちらの場合も、関数に右辺値を渡していることになります。 これにより、コンパイラは効率的になり、データのコピーを回避できます。 右辺値の詳細については、「値のカテゴリとそれらの参照」を参照してください。

XAML 項目コントロールをコレクションにバインドする場合は、次のことができます。 ただし、ItemsControl.ItemsSource プロパティを正しく設定するには、IInspectable (または IBindableObservableVector などの相互運用性型) の IVector 型の値に設定する必要があることに注意してください。

バインドに適した型のコレクションを生成し、それに要素を追加するコード例を次に示します。 このコード例のコンテキストは、XAML 項目コントロールで見つけることができます。 C++/WinRT コレクションにバインドします。

auto bookSkus{ winrt::single_threaded_vector<Windows::Foundation::IInspectable>() };
bookSkus.Append(winrt::make<Bookstore::implementation::BookSku>(L"Moby Dick"));

データからWindows ランタイム コレクションを作成し、そのビューを API に渡す準備を整えることができます。すべてをコピーしなくても実行できます。

std::vector<float> values{ 0.1f, 0.2f, 0.3f };
Windows::Foundation::Collections::IVectorView<float> view{ winrt::single_threaded_vector(std::move(values)).GetView() };

上記の例では、作成したコレクションを XAML 項目コントロールにバインド できます 。ただし、コレクションは監視できません。

監視可能なコレクション

監視可能なコレクションを実装する型の新しいオブジェクトを取得するには、任意の要素型で winrt::single_threaded_observable_vector 関数テンプレートを呼び出します。 ただし、XAML 項目コントロールへのバインドに適した監視可能なコレクションを作成するには、要素型として IInspectable を使用します。

オブジェクトは IObservableVector として返され、返されたオブジェクトの関数とプロパティを呼び出すインターフェイス (またはバインド先のコントロール) です。

auto bookSkus{ winrt::single_threaded_observable_vector<Windows::Foundation::IInspectable>() };

ユーザー インターフェイス (UI) コントロールを監視可能なコレクションにバインドする方法の詳細とコード例については、 XAML 項目コントロール、C++/WinRT コレクションへのバインドに関するページを参照してください。

連想コレクション (マップ)

見てきた 2 つの関数の連想コレクション バージョンがあります。

必要に応じて、型 std::map または std::unordered_map右辺値 を関数に渡すことで、これらのコレクションにあらかじめデータを設定できます。

auto coll1{
    winrt::single_threaded_map<winrt::hstring, int>(std::map<winrt::hstring, int>{
        { L"AliceBlue", 0xfff0f8ff }, { L"AntiqueWhite", 0xfffaebd7 }
    })
};

std::map<winrt::hstring, int> values{
    { L"AliceBlue", 0xfff0f8ff }, { L"AntiqueWhite", 0xfffaebd7 }
};
auto coll2{ winrt::single_threaded_map<winrt::hstring, int>(std::move(values)) };

単一スレッド

これらの関数の名前の "シングル スレッド" は、コンカレンシーを提供しないことを示します。つまり、スレッド セーフではありません。 これらの関数から返されるオブジェクトはすべてアジャイルであるため、スレッドの言及はアパートメントとは無関係です ( C++/WinRT のアジャイル オブジェクトを参照)。 オブジェクトがシングル スレッドになっているだけです。 また、アプリケーション バイナリ インターフェイス (ABI) 全体でデータを一方向または他方に渡すだけの場合は、これが完全に適しています。

コレクションの基底クラス

完全な柔軟性を実現するために、独自のカスタム コレクションを実装する場合は、難しい方法を避ける必要があります。 たとえば、 C++/WinRT の基底クラスを使用しなくても、カスタム ベクター ビューは次のようになります。

...
using namespace winrt;
using namespace Windows::Foundation::Collections;
...
struct MyVectorView :
    implements<MyVectorView, IVectorView<float>, IIterable<float>>
{
    // IVectorView
    float GetAt(uint32_t const) { ... };
    uint32_t GetMany(uint32_t, winrt::array_view<float>) const { ... };
    bool IndexOf(float, uint32_t&) { ... };
    uint32_t Size() { ... };

    // IIterable
    IIterator<float> First() const { ... };
};
...
IVectorView<float> view{ winrt::make<MyVectorView>() };

代わりに、 winrt::vector_view_base 構造体テンプレートからカスタム ベクター ビューを派生させ、 get_container 関数を実装してデータを保持するコンテナーを公開する方がはるかに簡単です。

struct MyVectorView2 :
    implements<MyVectorView2, IVectorView<float>, IIterable<float>>,
    winrt::vector_view_base<MyVectorView2, float>
{
    auto& get_container() const noexcept
    {
        return m_values;
    }

private:
    std::vector<float> m_values{ 0.1f, 0.2f, 0.3f };
};

get_containerによって返されるコンテナーは、winrt::vector_view_base が期待する開始インターフェイスと終了インターフェイスを提供する必要があります。 上記の例に示すように、 std::vector によってそれが提供されます。 ただし、独自のカスタム コンテナーを含め、同じコントラクトを満たす任意のコンテナーを返すことができます。

struct MyVectorView3 :
    implements<MyVectorView3, IVectorView<float>, IIterable<float>>,
    winrt::vector_view_base<MyVectorView3, float>
{
    auto get_container() const noexcept
    {
        struct container
        {
            float const* const first;
            float const* const last;

            auto begin() const noexcept
            {
                return first;
            }

            auto end() const noexcept
            {
                return last;
            }
        };

        return container{ m_values.data(), m_values.data() + m_values.size() };
    }

private:
    std::array<float, 3> m_values{ 0.2f, 0.3f, 0.4f };
};

これらは、カスタム コレクションの実装に役立つ C++/WinRT が提供する基本クラスです。

winrt::vector_view_base

上記のコード例を参照してください。

winrt::vector_base

struct MyVector :
    implements<MyVector, IVector<float>, IVectorView<float>, IIterable<float>>,
    winrt::vector_base<MyVector, float>
{
    auto& get_container() const noexcept
    {
        return m_values;
    }

    auto& get_container() noexcept
    {
        return m_values;
    }

private:
    std::vector<float> m_values{ 0.1f, 0.2f, 0.3f };
};

winrt::observable_vector_base

struct MyObservableVector :
    implements<MyObservableVector, IObservableVector<float>, IVector<float>, IVectorView<float>, IIterable<float>>,
    winrt::observable_vector_base<MyObservableVector, float>
{
    auto& get_container() const noexcept
    {
        return m_values;
    }

    auto& get_container() noexcept
    {
        return m_values;
    }

private:
    std::vector<float> m_values{ 0.1f, 0.2f, 0.3f };
};

winrt::map_view_base

struct MyMapView :
    implements<MyMapView, IMapView<winrt::hstring, int>, IIterable<IKeyValuePair<winrt::hstring, int>>>,
    winrt::map_view_base<MyMapView, winrt::hstring, int>
{
    auto& get_container() const noexcept
    {
        return m_values;
    }

private:
    std::map<winrt::hstring, int> m_values{
        { L"AliceBlue", 0xfff0f8ff }, { L"AntiqueWhite", 0xfffaebd7 }
    };
};

winrt::map_base

struct MyMap :
    implements<MyMap, IMap<winrt::hstring, int>, IMapView<winrt::hstring, int>, IIterable<IKeyValuePair<winrt::hstring, int>>>,
    winrt::map_base<MyMap, winrt::hstring, int>
{
    auto& get_container() const noexcept
    {
        return m_values;
    }

    auto& get_container() noexcept
    {
        return m_values;
    }

private:
    std::map<winrt::hstring, int> m_values{
        { L"AliceBlue", 0xfff0f8ff }, { L"AntiqueWhite", 0xfffaebd7 }
    };
};

winrt::observable_map_base

struct MyObservableMap :
    implements<MyObservableMap, IObservableMap<winrt::hstring, int>, IMap<winrt::hstring, int>, IMapView<winrt::hstring, int>, IIterable<IKeyValuePair<winrt::hstring, int>>>,
    winrt::observable_map_base<MyObservableMap, winrt::hstring, int>
{
    auto& get_container() const noexcept
    {
        return m_values;
    }

    auto& get_container() noexcept
    {
        return m_values;
    }

private:
    std::map<winrt::hstring, int> m_values{
        { L"AliceBlue", 0xfff0f8ff }, { L"AntiqueWhite", 0xfffaebd7 }
    };
};

重要な API