方法: アセンブリから STL/CLR コンテナーを公開する
list
や map
などの STL/CLR コンテナーは template ref クラスとして実装されています。 C++ テンプレートはコンパイル時にインスタンスが作成されるため、署名がまったく同じでも、アセンブリが異なる 2 つのテンプレート クラスは、実際には異なる型です。 つまり、アセンブリの境界を越えてテンプレート クラスを使うことはできません。
アセンブリを越えた共有を可能にするために、STL/CLR コンテナーにはジェネリック インターフェイス ICollection<T> が実装されています。 このジェネリック インターフェイスを使うことで、C++、C#、Visual Basic などのジェネリックをサポートするすべての言語が STL/CLR コンテナーにアクセスできます。
このトピックでは、StlClrClassLibrary
という C++ アセンブリで記述された複数の STL/CLR コンテナー要素を表示する方法を示します。 StlClrClassLibrary
にアクセスする 2 つのアセンブリについて説明します。 1 つ目のアセンブリは C++ で、2 つ目のアセンブリは C# で記述されています。
両方のアセンブリが C++ で書かれている場合、その generic_container
typedef を使ってコンテナーのジェネリック インターフェイスにアクセスすることができます。 たとえば、cliext::vector<int>
型のコンテナーがある場合、そのジェネリック インターフェイスは cliext::vector<int>::generic_container
です。 同様に、cliext::vector<int>::generic_iterator
のように、generic_iterator
typedef を使って、ジェネリック インターフェイスを介して反復子を取得することができます。
これらの typedef は C++ ヘッダー ファイルで宣言されているので、他の言語で記述されたアセンブリから使うことはできません。 そのため、C# や他の .NET 言語で cliext::vector<int>
のジェネリック インターフェイスにアクセスするには、System.Collections.Generic.ICollection<int>
を使います。 このコレクションを反復処理するには、foreach
ループを使います。
各 STL/CLR コンテナーが実装しているジェネリック インターフェイスを次の表に示します。
STL/CLR コンテナー | ジェネリック インターフェイス |
---|---|
deque<T> |
ICollection<T> |
hash_map<K, V> |
IDictionary<K, V> |
hash_multimap<K, V> |
IDictionary<K, V> |
hash_multiset<T> |
ICollection<T> |
hash_set<T> |
ICollection<T> |
list<T> |
ICollection<T> |
map<K, V> |
IDictionary<K, V> |
multimap<K, V> |
IDictionary<K, V> |
multiset<T> |
ICollection<T> |
set<T> |
ICollection<T> |
vector<T> |
ICollection<T> |
Note
queue
、priority_queue
、stack
コンテナーは反復子をサポートしていないため、ジェネリック インターフェイスを実装しておらず、アセンブリを越えてアクセスすることはできません。
例 1
説明
この例では、STL/CLR のプライベート メンバー データを含む C++ クラスを宣言しています。 次に、クラスのプライベート コレクションへのアクセスを許可するパブリック メソッドを宣言します。 これは、2 つの異なる方法で行います。1 つは C++ クライアントに対するもの、もう 1 つは他の .NET クライアントに対するものです。
コード
// StlClrClassLibrary.h
#pragma once
#include <cliext/deque>
#include <cliext/list>
#include <cliext/map>
#include <cliext/set>
#include <cliext/stack>
#include <cliext/vector>
using namespace System;
using namespace System::Collections::Generic;
using namespace cliext;
namespace StlClrClassLibrary {
public ref class StlClrClass
{
public:
StlClrClass();
// These methods can be called by a C++ class
// in another assembly to get access to the
// private STL/CLR types defined below.
deque<wchar_t>::generic_container ^GetDequeCpp();
list<float>::generic_container ^GetListCpp();
map<int, String ^>::generic_container ^GetMapCpp();
set<double>::generic_container ^GetSetCpp();
vector<int>::generic_container ^GetVectorCpp();
// These methods can be called by a non-C++ class
// in another assembly to get access to the
// private STL/CLR types defined below.
ICollection<wchar_t> ^GetDequeCs();
ICollection<float> ^GetListCs();
IDictionary<int, String ^> ^GetMapCs();
ICollection<double> ^GetSetCs();
ICollection<int> ^GetVectorCs();
private:
deque<wchar_t> ^aDeque;
list<float> ^aList;
map<int, String ^> ^aMap;
set<double> ^aSet;
vector<int> ^aVector;
};
}
例 2
説明
この例では、例 1 で宣言したクラスを実装します。 クライアントからこのクラス ライブラリを使うために、マニフェスト ツール mt.exe を使ってマニフェスト ファイルを DLL に埋め込みます。 詳細については、コードのコメントを参照してください。
マニフェスト ツールとサイドバイサイド アセンブリの詳細については、「C/C++ 分離アプリケーションおよび side-by-side アセンブリのビルド」を参照してください。
コード
// StlClrClassLibrary.cpp
// compile with: /clr /LD /link /manifest
// post-build command: (attrib -r StlClrClassLibrary.dll & mt /manifest StlClrClassLibrary.dll.manifest /outputresource:StlClrClassLibrary.dll;#2 & attrib +r StlClrClassLibrary.dll)
#include "StlClrClassLibrary.h"
namespace StlClrClassLibrary
{
StlClrClass::StlClrClass()
{
aDeque = gcnew deque<wchar_t>();
aDeque->push_back(L'a');
aDeque->push_back(L'b');
aList = gcnew list<float>();
aList->push_back(3.14159f);
aList->push_back(2.71828f);
aMap = gcnew map<int, String ^>();
aMap[0] = "Hello";
aMap[1] = "World";
aSet = gcnew set<double>();
aSet->insert(3.14159);
aSet->insert(2.71828);
aVector = gcnew vector<int>();
aVector->push_back(10);
aVector->push_back(20);
}
deque<wchar_t>::generic_container ^StlClrClass::GetDequeCpp()
{
return aDeque;
}
list<float>::generic_container ^StlClrClass::GetListCpp()
{
return aList;
}
map<int, String ^>::generic_container ^StlClrClass::GetMapCpp()
{
return aMap;
}
set<double>::generic_container ^StlClrClass::GetSetCpp()
{
return aSet;
}
vector<int>::generic_container ^StlClrClass::GetVectorCpp()
{
return aVector;
}
ICollection<wchar_t> ^StlClrClass::GetDequeCs()
{
return aDeque;
}
ICollection<float> ^StlClrClass::GetListCs()
{
return aList;
}
IDictionary<int, String ^> ^StlClrClass::GetMapCs()
{
return aMap;
}
ICollection<double> ^StlClrClass::GetSetCs()
{
return aSet;
}
ICollection<int> ^StlClrClass::GetVectorCs()
{
return aVector;
}
}
例 3
説明
この例では、例 1 と例 2 で作成したクラス ライブラリを使う C++ クライアントを作成します。 このクライアントでは、STL/CLR コンテナーの generic_container
typedef を使ってコンテナーを反復処理し、その内容を表示します。
コード
// CppConsoleApp.cpp
// compile with: /clr /FUStlClrClassLibrary.dll
#include <cliext/deque>
#include <cliext/list>
#include <cliext/map>
#include <cliext/set>
#include <cliext/vector>
using namespace System;
using namespace StlClrClassLibrary;
using namespace cliext;
int main(array<System::String ^> ^args)
{
StlClrClass theClass;
Console::WriteLine("cliext::deque contents:");
deque<wchar_t>::generic_container ^aDeque = theClass.GetDequeCpp();
for each (wchar_t wc in aDeque)
{
Console::WriteLine(wc);
}
Console::WriteLine();
Console::WriteLine("cliext::list contents:");
list<float>::generic_container ^aList = theClass.GetListCpp();
for each (float f in aList)
{
Console::WriteLine(f);
}
Console::WriteLine();
Console::WriteLine("cliext::map contents:");
map<int, String ^>::generic_container ^aMap = theClass.GetMapCpp();
for each (map<int, String ^>::value_type rp in aMap)
{
Console::WriteLine("{0} {1}", rp->first, rp->second);
}
Console::WriteLine();
Console::WriteLine("cliext::set contents:");
set<double>::generic_container ^aSet = theClass.GetSetCpp();
for each (double d in aSet)
{
Console::WriteLine(d);
}
Console::WriteLine();
Console::WriteLine("cliext::vector contents:");
vector<int>::generic_container ^aVector = theClass.GetVectorCpp();
for each (int i in aVector)
{
Console::WriteLine(i);
}
Console::WriteLine();
return 0;
}
出力
cliext::deque contents:
a
b
cliext::list contents:
3.14159
2.71828
cliext::map contents:
0 Hello
1 World
cliext::set contents:
2.71828
3.14159
cliext::vector contents:
10
20
例 4
説明
この例では、例 1 と例 2 で作成したクラス ライブラリを使う C# クライアントを作成します。 このクライアントでは、STL/CLR コンテナーの ICollection<T> メソッドを使ってコンテナーを反復処理し、その内容を表示します。
コード
// CsConsoleApp.cs
// compile with: /r:Microsoft.VisualC.STLCLR.dll /r:StlClrClassLibrary.dll /r:System.dll
using System;
using System.Collections.Generic;
using StlClrClassLibrary;
using cliext;
namespace CsConsoleApp
{
class Program
{
static int Main(string[] args)
{
StlClrClass theClass = new StlClrClass();
Console.WriteLine("cliext::deque contents:");
ICollection<char> iCollChar = theClass.GetDequeCs();
foreach (char c in iCollChar)
{
Console.WriteLine(c);
}
Console.WriteLine();
Console.WriteLine("cliext::list contents:");
ICollection<float> iCollFloat = theClass.GetListCs();
foreach (float f in iCollFloat)
{
Console.WriteLine(f);
}
Console.WriteLine();
Console.WriteLine("cliext::map contents:");
IDictionary<int, string> iDict = theClass.GetMapCs();
foreach (KeyValuePair<int, string> kvp in iDict)
{
Console.WriteLine("{0} {1}", kvp.Key, kvp.Value);
}
Console.WriteLine();
Console.WriteLine("cliext::set contents:");
ICollection<double> iCollDouble = theClass.GetSetCs();
foreach (double d in iCollDouble)
{
Console.WriteLine(d);
}
Console.WriteLine();
Console.WriteLine("cliext::vector contents:");
ICollection<int> iCollInt = theClass.GetVectorCs();
foreach (int i in iCollInt)
{
Console.WriteLine(i);
}
Console.WriteLine();
return 0;
}
}
}
出力
cliext::deque contents:
a
b
cliext::list contents:
3.14159
2.71828
cliext::map contents:
0 Hello
1 World
cliext::set contents:
2.71828
3.14159
cliext::vector contents:
10
20