vector オブジェクトを IEnumerable 型として返す C++/CLI ライブラリ
vector 型はテンプレートが用いられているクラスなので、コンパイル時に型パラメータに指定された型に展開されます。C# や Visual Basic から、展開された型を認識することは可能ですが、vector 型そのものは非公開のアセンブリなので直接利用することはできません。そこで、インターフェイスを通して STL/CLR で生成した列挙子にアクセスします。まずは、vector オブジェクトを IEnumerable 型として返す C++/CLI のライブラリを作ってみましょう。
#include <cliext/vector> #using <mscorlib.dll> using namespace System; using namespace System::Collections::Generic; using namespace cliext; public ref class Donor { public: static IEnumerable<int>^ GetVector(int min, int max); }; IEnumerable<int>^ Donor::GetVector(int min, int max) { vector<int>^ result = gcnew vector<int>(); for (int i = min ; i < max ; i++) result->push_back(i); return result; }
Sample01 は、C++/CLI で書かれたプログラムです。このコードはエントリーポイントを持たないため、DLL ファイルとしてコンパイルしてください。STL/CLR のコンテナである vector<int> 型オブジェクトを IEnumerable<int> 型として返す Donor クラスの静的な GetVector() メソッドを用意しています。このメソッドは、単純に min パラメータで指定された値から max パラメータに指定された値までの要素を vector<int> 型のコンテナに追加し、結果を返します。メソッドの戻り値は IEnumerable<int> 型なので、このメソッドは、他のアセンブリからも参照することができます。
IEnumerable<int> を取得して列挙する C# プログラム
次に、Sample01 で作成した Donor クラスの GetVector() メソッドから列挙子を取得してコンソールに各要素を出力するプログラムを C# 言語で書いてみましょう。
using System.Collections.Generic; class Test { public static void Main(string[] args) { IEnumerable<int> e = Donor.GetVector(0, 10); System.Console.WriteLine(e.GetType()); foreach (int item in e) System.Console.WriteLine(item); } }
cliext.vector<int> 0 1 2 3 4 5 6 7 8 9
Sample02 は C# 言語で書かれたプログラムです。このコードを見れば、Donor.GetVector() メソッドから取得した値を IEnumerable<int> として扱っているため、一般的な .NET Framework のコレクションと同じ方法でデータを処理できることが分かります。コンテナの生成や、要素を管理する処理は C++/CLI と STL/CLR を用い、結果だけを C# 言語で書かれた .NET Framework アプリケーション側で取得して利用することができます。このような方法であれば、高い技術力と経験のある C++ プログラマが、これまで十分テストされた STL を使った再利用可能なコードを応用してフレームワークを開発し、外部に公開する型は .NET Framework のコレクションとすることができます。他の一般的な .NET アプリケーション開発者は、実体が STL/CLR オブジェクトであることを意識する必要はありません。
Microsoft.VisualC.StlClr.IVector インターフェイス
しかし、IEnumerable 型にキャストされてしまったコンテナは、当然 STL コンテナとしての機能は失われてしまいます。そうではなく、C# 言語で作られた .NET Framework アプリケーションから C++/CLI のコンテナを操作したい場合は、vector 型が実装している Microsoft.VisualC.StlClr.IVector インターフェイスを使います。
public interface IVector<TValue> : IRandomAccessContainer<TValue>, ICloneable, ICollection, IEnumerable
このインターフェイスは、vector 型が提供するメソッドを宣言しています。vector<T> は、他の言語から IVector<T> 型のインターフェイスとして操作できるため、STL/CLR の vector 型の機能を失うことなく、外部からオブジェクトにアクセスできます。同様の方法で、他の STL/CLR コンテナも Microsoft.VisualC.StlClr 名前空間内に用意されているインターフェイスを実装しています。C++/CLI 以外の言語から STL/CLR オブジェクトにアクセスするには、これらのインターフェイスを使います。また、IVector インターフェイスは IEnumerable を継承しているため、IVector 型のオブジェクトをコレクションとして扱うことにも問題ありません。
IVector<int> を返すように変更した C++/CLI ライブラリ
では、Sample01 をベースに、生成した vector<int> オブジェクトを IVector<int> 型で返すライブラリを作ります。
#include <cliext/vector> #using <mscorlib.dll> using namespace System; using namespace cliext; using namespace Microsoft::VisualC::StlClr; public ref class Donor { public: static IVector<int>^ GetVector(int min, int max); }; IVector<int>^ Donor::GetVector(int min, int max) { vector<int>^ result = gcnew vector<int>(); for (int i = min ; i < max ; i++) result->push_back(i); return result; }
Sample03 は、Sample01 を書き換えて IEnumerable<int> の代わりに IVector<int> を返すようにしたプログラムです。IVector インターフェイスは、STL の vector 型が提供するメソッドを宣言しているため push_back() メソッドや size() メソッドに外部からアクセスできます。C# 言語で書かれたコードから、C++/CLI で作成されたコンテナに要素を追加したい場合などに利用できます。
IVector<int> を取得して列挙する C# プログラム
次に、上で作成した Sample03 から IVector<int> 型のオブジェクトを取得し、コンテナを操作するコードを C# 言語で作ります。
using Microsoft.VisualC.StlClr; class Test { public static void Main(string[] args) { IVector<int> vector = Donor.GetVector(0, 10); System.Console.WriteLine(vector.GetType()); for (int i = 0; i < vector.size(); i++) System.Console.WriteLine("vector[{0}]={1}", i, vector[i]); } }
cliext.vector<int> vector[0]=0 vector[1]=1 vector[2]=2 vector[3]=3 vector[4]=4 vector[5]=5 vector[6]=6 vector[7]=7 vector[8]=8 vector[9]=9
Sample04 は、Sample03 で作成した Donor クラスの GetVector() メソッドから IVector<int> 型のオブジェクトを取得し、内容を標準出力に出力するというものです。vector 型の size() メソッドやインデクサなどを使い、STL の vector 型と同じように操作できることが確認できます。ただし、IVector インターフェイスでは at() メソッドとインデクサの戻り値が異なっています。at() メソッドでは追跡参照を返しますが、インデクサはコンテナの型パラメータの指定された型をそのまま返します。
TValue% at(int _Pos)
property TValue Item[int _Pos] {
TValue get (int _Pos);
void set (int _Pos, TValue value);
}
この違いは、おそらく C++/CLI 以外の言語からの利用を想定したものだと考えられます。本来 STL では、これらのメソッドとインデクサの振る舞いは同じものでしたが、STL に合わせて C++/CLI に依存する追跡参照を返すようにしてしまうと、他の言語から要素にアクセスできなくなってしまいます。
