Generating C# wrapper class for the native C++ class with multiple inheritance
xInterop NGen++ fully supports wrapping the native C++ class with multiple inheritance by using C# .NET Explicit P/Invoke.
Let’s talk about the concept of C++ multiple inheritance first briefly. In C++, you can derive a class from any number of base classes. Deriving a class from more than one direct base class is called multiple inheritance.
On the other hand, C# unfortunately only supports deriving from a single base class.
Take a look at the following class diagram for example. The following classes are part of SFML implementation with our version of SFML C# Wrapper libraries as I discussed in the previous blog.
In the preceding example, class Window, RenderTarget are direct base classes for the derived class RenderWindow:
class Window { /* ... */ } class RenderTarget { /* ... */ } class RenderWindow : public Window, public RenderTarget { /* ... */ }
We will be able to wrap each class one-to-one in C# for class RenderWindow, class Window and RenderTarget. While in C#, we won’t be able to do the following like in C++ because C# only supports single inheritance.
public class RenderWindow : Window, RenderTarget { /* ... */ }
The solution we use in xInterop NGen++ is to let class RenderWindow derive from the first class, in this case, class Window and then generate code to implement the public methods of all other classes in a way of interface, more clearly, explicit interface, so the implementation of class RenderWindow in C# shall look like the following,
public class RenderWindow : Window, IRenderTarget { /* ... */ }
In the C# wrapper library, IRenderTarget defines all the public methods in the wrapper class of RenderTarget. The reason we chose to implement C# explicit interface instead of C# implicit interface is that we want to guarantee that there is no method with the same name and the same signature in class Window and the interface of IRenderTarget since generating C# wrapper code is an automated process.
public interface IRenderTarget { void clear(Color color); void draw(Drawable drawable, RenderStates states); void draw(Vertex vertices, int vertexCount, PrimitiveType type, RenderStates states); View getDefaultView(); UIntVector2 getSize(); View getView(); IntRect getViewport(View view); IntVector2 mapCoordsToPixel(FloatVector2 point); IntVector2 mapCoordsToPixel(FloatVector2 point, View view); FloatVector2 mapPixelToCoords(IntVector2 point); FloatVector2 mapPixelToCoords(IntVector2 point, View view); void popGLStates(); void pushGLStates(); void resetGLStates(); void setView(View view); }
This does introduce inconvenience because the method implemented in class RenderWindow via explicit interface is private, you will have to type-cast RenderWindow to IRenderTarget before you can access the methods of IRenderTarget.
// Create the render window. RenderWindow renderWindow = new RenderWindow(); renderWindow.create(new VideoMode(800, 600, 24), new NString("SFML works!"), 4, contextSettings); // type-cast render window to IRenderTarget inteface. IRenderTarget target = (IRenderTarget)renderWindow;