As the title suggests, this post is about D interfaces, specifically as supported in D 1.0.
Here's some background:

I've been working on porting the SWT Browser package to DWT (on Linux) over the last few months. I'm happy to say that I've mostly succeeded despite some rather annoying problems. The important issues have been ironed out. What remains now is to get down to some heavy debugging sessions.

SWT implements its Browser code based on the mozilla XPCOM interface. What this means is that in order to use the SWT Browser in your project, you need to install a mozilla package called XULRunner that provides all the components that make up a working browser. XULRunner is basically the Mozilla Team's way of separating the internals of Mozilla Firefox into a shared package that serves any number of applications that want to embed browser components. As of Firefox 3.0, the Mozilla Foundation has moved to this XPCOM component server system; it seems that they now include the XULRunner package within the Firefox installation instead of making it an integrated component of the browser.

Obviously this scheme is useful for all sorts of apps that want to embed a comprehensive browser. The only requirement is that development language know how to interface to the Mozilla components. So the key here is that a language must be compatible with XPCOM.

This is no problem for D on windows machines because D provides a mechanism of direct COM interface support on that platform. XPCOM happens to be binary compatible with the vtable layout of the COM structure, so, on windows, all one has to do is alias the XPCOM's base interface nsISupports to the COM IUknown. The D compiler takes care of the rest since it automatically adjusts the vtable when it sees a reference to the IUnknown COM type.

But how does one do this on Linux? Can we use D to interface with XPCOM there? The problem is that this will not work on Linux since COM has no meaningful implementation on the platform primarily because all COM components are declared extern(Windows) internally by the D compiler. This would appear to mean that Linux is left out in the cold when it comes to interfacing with anything like XPCOM. D interfaces obviously won't work because they are constitute a new type, independent of any relationship to any other language interface (D intefaces insert a vtable entry at index 0 that references RTTI).

There is, in fact, a solution for Linux:

We can use the COM interface alias technique just as we would on windows.

[code]

alias IUknown nsISupports;

[/code]

Even on linux, the D compiler will create a COM clean vtable with windows calling convention for the methods. You don't actually notice that the compiler has done this until you try to implement some classes based on the interface, at which point the compiler protests loudly. Now on linux, we need to get rid of the windows calling convention in order to make the interfaces XPCOM compatible. The best way to do this is to directly override the compiler by declaring each interface method extern(C); this is something XPCOM wants to see.

[code]

interface IUnknown
{
   static const char[] IID_STR = NS_ISUPPORTS_IID_STR;
   static const nsIID IID = NS_ISUPPORTS_IID;

extern(System):
   nsresult QueryInterface( nsIID* uuid, void **result);
   nsrefcnt AddRef();
   nsrefcnt Release();
}

[/code]

I've used extern(System) to demonstrate a quick way to do this for the particular platform so the code is portable. Now we have a simple COM interface with extern(C) calling convention that will interface acceptably with Linux XPCOM.

There's still a problem here, however. Since we are using IUnknown, the D compiler is going to secretly force all /class/ methods that implement the contract methods to also use extern(Windows). So within any class that implements the interface, we also have to apply the extern(System) decoration:

[code]

class A : nsISupports {
extern(System) nsresult QueryInterface( nsIID* uuid, void **result) {} ...
}

[/code]

But what about the class methods that don't implement the interface? Well, they have to be explicitly declared extern(D) in order to keep the compiler from complaining again.

[code]

class A : nsISupports {
extern(System) nsresult QueryInterface( nsIID* uuid, void **result) {} ...
   extern(D) void handleFolderEvent( Event event );
}

[/code]

The above code worked in porting the Browser code to DWT specifically for D version 1.0. But I have to admit that I remain a little uncomfortable implementing the fix this way. It's a hack. Unfortunately, it's the only way I know that simply and effectively solves this problem.
Here are a couple of thoughts I have:

(1) I'm wondering if the implicit COM support (triggered by the IUnknown symbol only) implemented in D is such a good idea. Would such a feature be more correctly supported as a Pragma wherever an interface vtable type is augmented in a system specific way? pragma("COM") {} or something to that effect.

(2) As an alternate solution to my trick with COM on linux, there is also the option of using extern(C++), except that it only exists in D 2.0. I think using extern(C++) might work in this situation because mozilla XPCOM interfaces are often represented in C++ using single inheritance C++ class structures. The vtables should match unless I'm missing some important detail. Porting extern(C++) to D 1.0 should not constitute a spec change, and it could mean simpler support for Mozilla XPCOM without ugly workarounds. Does this sound reasonable?

For more information, I've also covered the same material here:

http://www.dsource.org/projects/dwt/wiki/PortingJournal

Here's a link to the Walter's discription of D 2.0 Interfaces and the addition of extern(C++) along with its caveats:

http://www.digitalmars.com/d/2.0/cpp_interface.html

Notice that in the above link Walter says COM only works on windows. ;)

-JJR


Reply via email to