contravariant argument types: wanna?

Steven Schveighoffer schveiguy at yahoo.com
Thu Sep 24 04:25:38 PDT 2009


On Tue, 22 Sep 2009 22:02:59 -0400, Jeremie Pelletier <jeremiep at gmail.com>  
wrote:


> Yeah most of my display interfaces would make use of covariant  
> arguments, I use main abstract factory for the entire package, and the  
> objects it creates contain factory methods themselves. I plan to have  
> implementations for all of win32, gdk, x11, quartz, cairo, pango, d2d,  
> dwrite, gl, gl3 and finally d3d7 up to d3d11. Most of the client code  
> will therefore see only the interfaces in order to maintain portability,  
> and to allow different implementations to live in the same executable  
> (for example win32/gl/cairo/pango for up to vista or  
> win32/d3d/d2d/dwrite if on win7 and up).
>
> Here is a watered down version of a few interfaces I use, which are used  
> by client code:
>
> interface IDrawable {}
> interface IWindow : IDrawable {} // onscreen drawable
> interface ISurface : IDrawable {} // offscreen drawable
> interface IDisplayContext {} // base of 2d-3d contextes
> interface IRenderContext {} // 3d context
> interface IWindowRenderContext {} // specialized onscreen 3d context
> interface IRenderer {
> 	IWindowRenderContext CreateRenderContext(IWindow);
> 	ISurfaceRenderContext CreateRenderContext(ISurface);
> }
>
> And some of their current implementation, which are all used within the  
> package:
>
> abstract class Win32Drawable : IDrawable {}
> final class Win32Window : Win32Drawable, IWindow {}
> final class Win32Surface : Win32Drawable, IWindow {}
>
> final class GLRenderer : IRenderer {
> 	GLWindowRenderContext CreateRenderContext(IWindow window) {
> 		if(auto win32Window = cast(Win32Window)window)
> 			return new GLWindowRenderContext(win32Window);
> 		else throw new Error();
> 	}
> 	GLSurfaceRenderContext CreateRenderContext(ISurface surface) {
> 		if(auto win32Surface = cast(Win32Surface)surface)
> 			return new GLSurfaceRenderContext(win32Surface);
> 		else throw new Error();
> 	}
> }
>
> abstract class GLRenderContext : IRenderContext {}
> final class GLWindowRenderContext : GLRenderContext,  
> IWindowRenderContext {
> 	this(Win32Window) {}
> }
> final class GLSurfaceRenderContext : GLRenderContext,  
> ISurfaceRenderContext {
> 	this(Win32Surface) {}
> }
>
> I have over a hundred of such methods doing dynamic casts across all the  
> different implementations like these twos in this package alone, a  
> display interface is quite a large beast.
>
> Of course if you can suggest a better way of doing methods expecting a  
> specific implementation of an object, while still allowing client code  
> to call them with the interface pointer, I'd be glad to implement it :)
>
> Jeremie

There are some possible solutions.  First, it looks like you are using  
interfaces to abstract the platform, which seems more appropriate for  
version statements.  I once wrote an OS abstraction library in C++, and in  
my fanatic attempt to avoid using the preprocessor for anything, I made  
everything interfaces (pure abstract classes).  I think with D, the  
version statements are a much better solution, and will reduce overhead  
quite a bit.

Second, Your IRenderer is the one responsible for creating a render  
context, but it depends on being "hooked up" with the appropriate IWindow  
or ISurface object.  However, the IRenderer implementation's methods are  
pretty much static (granted they might be trimmed down).  Why not move  
them into the IWindow and ISurface interfaces?

interface IWindow : IDrawable {
   IWindowRenderContext CreateRenderContext();
}

interface ISurface : IDrawable {
   ISurfaceRenderContext CreateRenderContext();
}

If you need an instance of IRenderer for some other reason not shown, then  
consider using a hidden singleton of the correct implementation.

-Steve



More information about the Digitalmars-d mailing list