XInterop NGen++‘s amazing abilities of wrapping C++ class in C# .NET automatically
The bridge between C++ and C# .NET world is getting much more advanced than ever.
.NET P/Invoke technology has been around since the beginning of .NET framework. We all know that P/Invoke can be used to call C-style methods in a native DLL from C#. It is a misunderstanding that P/Invoke is only good for C-style DLLs, and popular with people who want to call into the Windows API. The fact is that P/Invoke can also be used to call member methods of a C++ class from C# as well. P/Invoke is designed with the ability of calling any method written in C/C++ in the native un-managed world.
The way we use to call C++ class and method via P/Invoke from C# is really new. When I just started writing xInterop NGen++(A C# wrapper generator for native C++ DLL), I really did not have this feature in mind and I could not image that I would end up with such a powerful tool with so many great features with which allows me to create C# wrapper class for whatever template class appearing in any of the methods of the native C++ classes. Other C# wrapper generators for C++, such as SWIG, do not support template class very well, you will have to find the instantiated template classes first by yourself and then write additional script manually to support creating the C# wrapper class.
In C++, you can use instantiated template class in the C++ class once the template class is defined.
Let me give you an example of how powerful xInterop NGen++ is when dealing with the instantiated template class. We have a SimpleObject containing one method to initialize the data contained in the class.
public EXPORT_API class SimpleObject { public: SimpleObject() { } void Initialize(std::map<std::string, std::vector<std::vector<std::string>>>& data) { /* code to initialize the SimpleClass by using the given data. } };
By exporting the class of SimpleObject, the method of Initialize gets exported in the native C++ DLL with a very long mangled name and a ordinal number. Developers do not have to worry about the mangled name when using xInterop NGen++ since it is handled automatically by the tool.
Looking at the preceding C++ code carefully, you would notice the input parameter of method of Initialize is very complicated, it is a map, the key of the map is std::string while the value of map is vector of vector of string, vector<vector<string>>. The template classes do not get exported automatically, and the compiler(Visual C++ compiler) actually outputs a warning of the classes being used in the interfaces are not exported. Considering there may be many instantiated template classes being used in all different interfaces, just finding the instantiated template class, exporting them and maintaining such a list could be a big effort in a long run.
Normally, you would need to export all the instantiated template classes in the interfaces if you want to use them. But in the case of using xInterop NGen++ to create C# wrapper class for the native C++ class, developers don’t need to do anything with any instantiated template classes, all they need to do is just define the instantiated template class in their interface.
xInterop NGen++ automatically looks into all the methods, fields, it iterates each type of all the parameters, and discovers that all the instantiated template classes which do not get exported already in the original native C++ DLL, and export them in a supplement native c++ DLL which links to the original native C++ DLL.
xInterop NGen++ automatically generates friendly name of the C# wrapper class for the corresponding native instantiated template class.
Let’s take the following example of an instantiated template class,
std::map<std::string, std::vector<std::vector<std::string>>>
xInterop NGen++ generates a C# wrapper class of StdStringStdStringVectorVectorMap.
public class StdStringStdStringVectorVectorMap : SimpleLibraryWin32SupplementBaseClass, IDictionary<StdString, StdStringVectorVector>, ICollection<KeyValuePair<StdString, StdStringVectorVector>>, IEnumerable<KeyValuePair<StdString, StdStringVectorVector>>, IEnumerable { public StdStringStdStringVectorVectorMap(); public StdStringStdStringVectorVectorMap(StdStringStdStringVectorVectorMap that); public StdStringStdStringVectorVectorMap(StdStringStdStringVectorVectorMapStruct objectStruct); public StdStringStdStringVectorVectorMap(IntPtr thisObject, IntPtr thatObject, bool initialize, bool allocate = false); public static implicit operator StdStringStdStringVectorVectorMapStruct(StdStringStdStringVectorVectorMap o); public int Count { get; } public bool IsReadOnly { get; } public ICollection<StdString> Keys { get; } public override string NativeClassName { get; } protected override int StorageSize { get; } public override string TemplateClassName { get; } public ICollection<StdStringVectorVector> Values { get; } public StdStringVectorVector this[StdString index] { get; set; } public void Add(KeyValuePair<StdString, StdStringVectorVector> item); public void Add(StdString key, StdStringVectorVector value); public StdStringStdStringVectorVectorMap assign(StdStringStdStringVectorVectorMap thatObject); public StdStringVectorVector at(StdString keyval); public StdStringStdStringVectorVectorMapConstIterator begin(); public StdStringStdStringVectorVectorMapConstIterator cbegin(); public StdStringStdStringVectorVectorMapConstIterator cend(); protected override void CleanupNativeResource(); public void Clear(); public void clear(); public bool Contains(KeyValuePair<StdString, StdStringVectorVector> item); public bool ContainsKey(StdString key); public void CopyTo(KeyValuePair<StdString, StdStringVectorVector>[] array, int arrayIndex); public long count(StdString keyval); public bool empty(); public StdStringStdStringVectorVectorMapConstIterator end(); public StdStringStdStringVectorVectorMapConstIteratorStdStringStdStringVectorVectorMapConstIteratorPair equal_range(StdString keyval); public long erase(StdString keyval); public StdStringStdStringVectorVectorMapIterator erase(StdStringStdStringVectorVectorMapConstIterator where); public void erase(StdString first, StdString last); public StdStringStdStringVectorVectorMapIterator erase(StdStringStdStringVectorVectorMapConstIterator first, StdStringStdStringVectorVectorMapConstIterator last); public StdStringStdStringVectorVectorMapConstIterator find(StdString keyval); public StdStringStdStringVectorVectorPairAllocator get_allocator(); public StdStringStdStringVectorVectorMapIteratorBoolPair insert(StdStringStdStringVectorVectorPair val); public StdStringStdStringVectorVectorMapIteratorBoolPair insert(StdString first, StdStringVectorVector second); public StdStringStdStringVectorVectorMapIterator insert(StdStringStdStringVectorVectorMapConstIterator where, StdStringStdStringVectorVectorPair val); public StdStringStdStringVectorVectorMapIteratorBoolPair insert(StdStringStdStringVectorVectorPair val, bool leftish); public StdStringLess key_comp(); public StdStringStdStringVectorVectorMapConstIterator lower_bound(StdString keyval); public long max_size(); protected IntPtr operator_get_set(StdString keyval); public bool Remove(KeyValuePair<StdString, StdStringVectorVector> item); public bool Remove(StdString key); public long size(); public void swap(StdStringStdStringVectorVectorMap right); public bool TryGetValue(StdString key, out StdStringVectorVector value); public StdStringStdStringVectorVectorMapConstIterator upper_bound(StdString keyval); public StdStringStdStringVectorVectorMapTraits value_comp(); }
Not only does the C# map class support the interface(methods and fields) of the corresponding native C++ class, it also implements IDictionary interface, so you will be able to manipulate the C# map class just like a C# native dictionary class. With IDictionary interface implemented for the C# wrapper class, developers can even use LINQ to manipulate the C# wrapper class of the C++ native map class.
The template class actually contains several other template classes, corresponding C# wrapper class get generated.
std::vector<std::vector<std::string>> ==> StdStringVectorVector (Supports IEnumerable) std::vector<std::string> ==> StdStringVector (Supports IEnumerable) std::string ==> StdString