iteratorをIEnumerable化するには...
BCLとSTL/CLRでは要素の列挙のやり方が異なります。BCLでは一つのIEnumerableをfor eachで列挙できるのに対し、STL/CLRでは要素列の先頭と末尾(の次)を指すiteratorの組が用いられます。これについてもSTL/CLRヘッダ<cliext/adapter>で定義されたrange_agdapterで二つのiteratorをIEnumerableに変換することができます。
#include <cliext/adapter> #include <cliext/vector> using namespace System; using namespace System::Collections::Generic; int main() { cliext::vector<int> iv = gcnew array<int> { 0, 1, 2, 3, 4 }; cliext::vector<int>::iterator from = iv.begin(); ++from; // from は先頭の次 cliext::vector<int>::iterator to = iv.end(); --to; // to は末尾の手前 // range_adapterで fromとtoの組をIEnumerable化する cliext::range_adapter<cliext::vector<int>::iterator> range(from,to); for each ( int item in range ) { Console::WriteLine(item); } }
1 2 3
二つのiteratorからrange_adapterを生成する関数make_collectionが用意されているので、通常はこれを利用する方がお手軽でしょう。
#include <cliext/adapter> #include <cliext/vector> using namespace System; using namespace System::Collections::Generic; int main() { cliext::vector<int> iv = gcnew array<int> { 0, 1, 2, 3, 4 }; cliext::vector<int>::iterator from = iv.begin(); ++from; // from は先頭の次 cliext::vector<int>::iterator to = iv.end(); --to; // to は末尾の手前 // make_collectionで fromとtoの組をIEnumerable化する for each ( int item in cliext::make_collection(from,to) ) { Console::WriteLine(item); } }
range_adapterでiteratorをIEnumerable化することにより、アセンブリの外に対して"要素の列挙範囲"を返すことができます。
簡単な例を紹介しましょう。名前と得点をメンバに持つExamとその集合ExamUtilを定義します。ExamUtilにはExamの追加メソッドadd、および得点の範囲を与えてその条件を満たすExamの集合を返すrangeを定義します。
#ifndef STLCLR_lib_H_ #define STLCLR_lib_H_ #include <cliext/vector> using namespace System; using namespace System::Collections::Generic; namespace STLCLR_lib { public ref class Exam { public: String^ name; // 名前 int score; // 得点 // コンストラクタ Exam(System::String^ n, int s) : name(n), score(s) {} // 比較 static int Compare(Exam^ x, Exam^ y) { return x->score < y->score ? -1 : (y->score < x->score ? 1 : 0); } static bool operator<(Exam^ x, Exam^ y) { return Compare(x,y) < 0; } }; public ref class ExamUtil { private: cliext::vector<Exam^> data_; public: // Examを追加する void add(Exam^ e); // lo以上/hi未満の得点を持つExamの列挙を返す IEnumerable<Exam^>^ range(int lo, int hi); }; } #endif
ExamUtil::rangeではdata_が内包する要素を得点の昇順にソートし、与えられた範囲をlower_boundで検索しIEnumerableを生成します。
#include <cliext/algorithm> #include <cliext/adapter> #include "STLCLR_lib.h" namespace STLCLR_lib { void ExamUtil::add(Exam^ e) { data_.push_back(e); } IEnumerable<Exam^>^ ExamUtil::range(int lo, int hi) { // 得点の昇順にソートし、 cliext::sort(data_.begin(), data_.end()); // 与えられた範囲を示すIEnumerableを返す return cliext::make_collection( cliext::lower_bound(data_.begin(), data_.end(), gcnew Exam(L"",lo)), cliext::lower_bound(data_.begin(), data_.end(), gcnew Exam(L"",hi)) ); } }
このコード(STLCLR_lib.h/STLCLR_lib.cpp)からアセンブリを作り、C#から利用します。
using System; using STLCLR_lib; namespace STLCLR_use { class Program { static void Main() { ExamUtil exam = new ExamUtil(); exam.add(new Exam("相川", 35)); exam.add(new Exam("井上", 20)); exam.add(new Exam("上村", 80)); exam.add(new Exam("江口", 50)); exam.add(new Exam("小田", 75)); // 30点以上/80点未満を列挙する foreach (Exam item in exam.range(30, 80)) { Console.WriteLine("{0}/{1}", item.name, item.score); } } } }
相川/35 江口/50 小田/75
まとめ
...いかがでしょうか、STL/CLRは単にSTLのmanaged版であるのに加え、.NET Frameworkとの親和性を考慮したいくつかの"からくり"が提供されています。.NET環境下で主力となるのはC#やVBですが、レガシーコードの利用など、managedとnativeの混在/協調にC++/CLIが利用されます。STL/CLRはC++プログラマの手に馴染んだやりかたでmanagedをサポートする強力な手駒と言えるでしょう。
