WinRTはコレクションに対するイテレータが用意されているので、その点ではC++との親和性も良いです。

この例は、Twitterのトレンド(地域:東京)を取得してきて、ListBoxに格納するコードです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <collection.h>
#include <boost/range.hpp>
#include <boost/range/algorithm.hpp>
 
void Application1::MainPage::Button_Click(Platform::Object^ sender, RoutedEventArgs^ e)
{
  using namespace Windows::Foundation;
  using namespace Windows::Foundation::Collections;
  using namespace Windows::Data::Xml::Dom;
 
  auto loadOp = XmlDocument::LoadFromUriAsync(ref new Uri("https://api.twitter.com/1/trends/1118370.xml"));
  loadOp->Completed = ref new AsyncOperationCompletedHandler<XmlDocument^>([this](IAsyncOperation<XmlDocument^>^ op)
  {
    auto doc = op->GetResults();
    IIterable<IXmlNode^>^ nodes = doc->SelectNodes("/matching_trends/trends/trend/text()");
    boost::transform(nodes, back_inserter(this->TrendList->Items), [](IXmlNode^ trend)
    {
      return safe_cast<XmlText^>(trend)->Data;
    });
  });
  loadOp->Start();
}

ラムダ式の中、IIterable<>からはInput Iteratorが取り出せます。これがtransformの入力側です。

一方、出力側のback_inserterはIVector<>に対するOutput Iteratorを返す関数で、もちろん末尾に要素を追加していきます。このアプリでは、XAML側で<ListBox Name=”TrendList” … />と宣言しており、this->TrendList->Items (ItemsControl.Itemsプロパティ)が返すItemCollectionクラスはIVector<Object^>などから派生しています。

……とまあ、こう書くためには、Boost.Rangeに対するゴニョゴニョとした準備が必要なんですけどね。Boost.Rangeの“Method 2: provide free-standing functions and specialize metafunctions”の方法を選んでいます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
namespace Windows
{
  namespace Foundation
  {
    namespace Collections
    {
      template<typename T>
      auto range_begin(T^ r) -> decltype(begin(r)) {return begin(r);}
      template<typename T>
      auto range_end(T^ r) -> decltype(end(r)) {return end(r);}
    }
  }
}
 
namespace boost
{
  template<typename T>
  struct range_mutable_iterator<::Windows::Foundation::Collections::IIterable<T>^>
  {
    typedef ::Platform::InputIterator<T> type;
  };
  template<typename T>
  struct range_const_iterator<::Windows::Foundation::Collections::IIterable<T>^>
  {
    typedef ::Platform::InputIterator<T> type;
  };
}

このように、IIterable<>からイテレータを取り出す関数はbegin/endという名前(しかもIIterable<>と同じ名前空間で定義)なので、Range-based forと相性ばつぐん、のはずですが、このVisual C++にはまだRange-based forが実装されていないというオチが待っています(VC++独自のfor eachはだめでした。Range-based forのルールに対応していないようです)。

なお、今回使ったWinRTのイテレータ周りの対応は、Metro style apps > Learn > Metro style app reference > Language reference for Metro style apps > Visual C++ reference for Windows Runtime > collection.hWindows::Foundation::Collections Namespace (C++/CX)にリファレンスがあります。今回登場しませんでしたが、IVector<>に対するbegin/end(ランダムアクセスイテレータを返す)などがあります。

2015年5月5日追記:MSDNライブラリのcollection.hがリンク切れとなったので、同等と思われるWindows::Foundation::Collections Namespaceへのリンクを追加しました


スポンサード リンク

この記事のカテゴリ

  • ⇒ WinRTとC++イテレータ
  • ⇒ WinRTとC++イテレータ
  • ⇒ WinRTとC++イテレータ
  • ⇒ WinRTとC++イテレータ