Generic Interfaces in STL/CLR
Today I would like to start introducing parts in STL/CLR library which are not present in STL. In this post, I give a short overview of generic interfaces implemented in STL/CLR. They solve problem of providing access to STL/CLR container across .Net assembly boundary. I start with showing the problem first followed by overview of a solution to the problem implemented in STL/CLR.
If one attempts compiling main.cpp file attached to this post, the compiler is going to issue a warning and one of my favorite errors:
main.cpp(8) : warning C4677: 'PrintOutSet': signature of non-private member contains assembly private type 'cliext::set<_Key
1_t>'
with
[
_Key1_t=int
]
main.cpp(24) : error C2664: 'MyClass::PrintOutSet' : cannot convert parameter 1 from 'cliext::set<_Key1_t>' to 'cliext::set<_Key1_t> ^'
with
[
_Key1_t=int
]
No user-defined-conversion operator available, or
No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
Don't you love C2664? Cannot convert from A to A – the world clearest error message. OK, I stop being sarcastic and I am not going to discuss the error message in this post. Let's focus on the warning. In this sample, the following method is causing this warning:
void PrintOutSet(cliext::set<int>^ dataSet)
{
...
}
The root cause of the warning is the fact that this method is public in the class that is accessible from an assembly other then the assembly with definition of this class. As I mentioned before, cliext::set<> is a template ref class, not a generic ref class. Which means that it is instantiated in compile time and cliext::set<> in two different assemblies is actually two different types. To expose a handle to a STL/CLR container from an assembly that populates the container to another assembly that operates on this container, there should be another non-typedef and non-template based solution. STL/CLR uses generic interfaces to provide a solution to this problem. There two scenarios here:
Scenario 1 - Interop with assemblies written in C#/VB. In this scenario STL/CLR container is populated with initial data inside .Net assembly written in C++, then a handler is passed into another assembly written in C++ or C# or VB or any other .Net language.
Scenario 2 - Interop with assemblies written in C++. In this scenario STL/CLR container are populated with initial data in C++ .Net assembly, then a handler is passed into another assembly written in C++.
To address Scenario#1, all STL/CLR containers implement generic interfaces from the .Net Framework Base Class library (BCL). For example, cliext::set<> implements System::Collections::Generic::ICollection<T> and
System::Collections::Generic::IEnumerable<T> interfaces. In MyAssembly.h and MyAssembly.cpp I am defining a method that exposes cliext::map<> to another assembly using System::Collections::Generic::ICollection<T>:
public:
System::Collections::Generic::ICollection<int>^ GetMapCollection()
{
return (System::Collections::Generic::ICollection<int>^) m_mapData;
}
I can now work with data stored in cliext::map<> from C# assembly,
ICollection<int> collVector= aClass.GetVectorCollection();
foreach (int iA in collVector)
{
...
}
Similar solution is provided for Scenario#2. STL/CLR library provides a set of generic interfaces that can be used to build data passing across assembly boundary when both assemblies are written in C++. There is pretty much one interface for each container and iterator. As one may see in my example of passing data from cliext::map<>, both sides have to include <cliext\map> so handy typedefs cliext::map<>:generic_container and cliext::map<>::generic_iterator are available:
public:
cliext::map<int,int>::generic_container^ GetMap()
{
return m_mapData;
}
cliext::map<int,int>::generic_iterator GetMapBegin();
{
return m_mapData->begin();
}
cliext::map<int,int>::generic_iterator GetMapEnd();
{
return m_mapData->end();
}
In assembly where I would like to access data I continue using generic interfaces to the container:
cliext::map<int,int>::generic_container^ iColl = aClass->GetMap();
cliext::map<int,int>::generic_iterator iter1;
for (iter1 = iColl->begin(); iter1 != iColl->end(); iter1++)
{
...
}
One of advantages in using this approach versus using System::Collection generic interfaces is that one can keep using STL/CLR algorithms on generic_container and generic_interfaces. We are expecting only C++ developers use STL/CLR generic interfaces. There was discussion about supporting access to STL/CLR containers from C#/VB code using STL/CLR interfaces. However we are not clear on how many C# developer will decide to use these interfaces vs. System::Collection interfaces. We have decided to think about investing in this area after we receive initial feedback on STL/CLR from CTPs.
This is all for today. I am attaching sample code snippets. You won't be able to compile them till release of STL/CLR in a CTP later this summer. Also please be aware that it is possible that implementation of STL/CLR may change before it is release. It is not guaranteed that samples are going to work with the version of STL/CLR released in CTP.