www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Interfacing with XPCOM

reply John Reimer <terminal.node gmail.com> writes:
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
Nov 30 2008
parent reply Walter Bright <newshound1 digitalmars.com> writes:
So are you saying that XPCOM will work on Linux with D if only the 
extern(Windows) was actually extern(C++) ?
Nov 30 2008
next sibling parent reply John Reimer <terminal.node gmail.com> writes:
Hello Walter,

 So are you saying that XPCOM will work on Linux with D if only the
 extern(Windows) was actually extern(C++) ?
 
Yes, I think so. But I should probably test the theory with dmd 2.0 first just to make sure. This is just about over my head since I'm not too familiar with C++ internals. In terms of XPCOM, I believe that when an interface is assigned an implementation pointer and the interface method is called, the most important detail is that the /parameter/ signature at a specific vtable index match that of the implementation's at the same index: that is, the method names are not important except as an alias for client to use; it is assumed that the interface methods will match the calling convention and argument types of the implementation which was assigned to it). So my question would be, in terms /calling conventions/ in an /interface/ (not a class), does extern(System) differ in any way from extern(C++). Does a "this" pointer get prepended to the argument list of a C++ method call? I apologize if this is an ignorant question. I'm almost clueless here. I'm assuming that the only difference in the extern(C++) is that it: (1) changes the interface to have a "normal" vtable (as XPCOM needs) and (2) allows for a system calling convention (method name not being important other than that the parameters of interface and implementation match). -JJR
Nov 30 2008
parent reply Walter Bright <newshound1 digitalmars.com> writes:
John Reimer wrote:
 Hello Walter,
 
 So are you saying that XPCOM will work on Linux with D if only the
 extern(Windows) was actually extern(C++) ?
Yes, I think so. But I should probably test the theory with dmd 2.0 first just to make sure. This is just about over my head since I'm not too familiar with C++ internals. In terms of XPCOM, I believe that when an interface is assigned an implementation pointer and the interface method is called, the most important detail is that the /parameter/ signature at a specific vtable index match that of the implementation's at the same index: that is, the method names are not important except as an alias for client to use; it is assumed that the interface methods will match the calling convention and argument types of the implementation which was assigned to it). So my question would be, in terms /calling conventions/ in an /interface/ (not a class), does extern(System) differ in any way from extern(C++). Does a "this" pointer get prepended to the argument list of a C++ method call? I apologize if this is an ignorant question. I'm almost clueless here. I'm assuming that the only difference in the extern(C++) is that it: (1) changes the interface to have a "normal" vtable (as XPCOM needs) and (2) allows for a system calling convention (method name not being important other than that the parameters of interface and implementation match).
The system linkage was devised to match the Microsoft "syscall" convention. It really has no meaning on linux, so we can make it whatever it needs to be. The C++ convention is designed to match whatever C++ does on the target platform. Since Linux doesn't have any Windows or System calling convention, it would seem that C++ is the one.
Nov 30 2008
parent reply John Reimer <terminal.node gmail.com> writes:
Hello Walter,

 John Reimer wrote:
 
 Hello Walter,
 
 So are you saying that XPCOM will work on Linux with D if only the
 extern(Windows) was actually extern(C++) ?
 
Yes, I think so. But I should probably test the theory with dmd 2.0 first just to make sure. This is just about over my head since I'm not too familiar with C++ internals. In terms of XPCOM, I believe that when an interface is assigned an implementation pointer and the interface method is called, the most important detail is that the /parameter/ signature at a specific vtable index match that of the implementation's at the same index: that is, the method names are not important except as an alias for client to use; it is assumed that the interface methods will match the calling convention and argument types of the implementation which was assigned to it). So my question would be, in terms /calling conventions/ in an /interface/ (not a class), does extern(System) differ in any way from extern(C++). Does a "this" pointer get prepended to the argument list of a C++ method call? I apologize if this is an ignorant question. I'm almost clueless here. I'm assuming that the only difference in the extern(C++) is that it: (1) changes the interface to have a "normal" vtable (as XPCOM needs) and (2) allows for a system calling convention (method name not being important other than that the parameters of interface and implementation match).
The system linkage was devised to match the Microsoft "syscall" convention. It really has no meaning on linux, so we can make it whatever it needs to be. The C++ convention is designed to match whatever C++ does on the target platform. Since Linux doesn't have any Windows or System calling convention, it would seem that C++ is the one.
So extern(System) does not translate to extern(C)? Does that mean that all extern(System)'s in my code are defaulting to extern(D)? That'd be a shocker to me. :) If that's true, it would certainly change my understanding of what I thought was making my code work. :D -JJR
Nov 30 2008
parent reply Walter Bright <newshound1 digitalmars.com> writes:
John Reimer wrote:
 So extern(System) does not translate to extern(C)?   Does that mean that 
 all extern(System)'s in my code are defaulting to extern(D)?
 
 That'd be a shocker to me. :)  If that's true, it would certainly change 
 my understanding of what I thought was making my code work. :D
No, extern(System) does on linux just what it does on windows, it's just that there's no point to it on linux.
Nov 30 2008
next sibling parent John Reimer <terminal.node gmail.com> writes:
Hello Walter,

 John Reimer wrote:
 
 So extern(System) does not translate to extern(C)?   Does that mean
 that all extern(System)'s in my code are defaulting to extern(D)?
 
 That'd be a shocker to me. :)  If that's true, it would certainly
 change my understanding of what I thought was making my code work. :D
 
No, extern(System) does on linux just what it does on windows, it's just that there's no point to it on linux.
Okay. I've found it very useful on linux, especially when I'm doing windows/linux library projects. It does simplify things greatly because you don't have to declare extern(C) and extern(Windows) under different version statements for projects that are going to support windows and linux. And, at least, it is different than extern(D) because it removes the name mangling. So I'd say it has important usefulness even on Linux. -JJR
Nov 30 2008
prev sibling parent reply Tomas Lindquist Olsen <tomas famolsen.dk> writes:
Walter Bright wrote:
 John Reimer wrote:
 So extern(System) does not translate to extern(C)?   Does that mean 
 that all extern(System)'s in my code are defaulting to extern(D)?

 That'd be a shocker to me. :)  If that's true, it would certainly 
 change my understanding of what I thought was making my code work. :D
No, extern(System) does on linux just what it does on windows, it's just that there's no point to it on linux.
I thought the whole point of extern(System) was to provide a workaround when... version(Windows) extern(Windows): else extern(C): ...stopped working ?!? from spec: "System is the same as Windows on Windows platforms, and C on other platforms"
Nov 30 2008
next sibling parent reply John Reimer <terminal.node gmail.com> writes:
Hello Tomas,

 Walter Bright wrote:
 
 John Reimer wrote:
 
 So extern(System) does not translate to extern(C)?   Does that mean
 that all extern(System)'s in my code are defaulting to extern(D)?
 
 That'd be a shocker to me. :)  If that's true, it would certainly
 change my understanding of what I thought was making my code work.
 :D
 
No, extern(System) does on linux just what it does on windows, it's just that there's no point to it on linux.
I thought the whole point of extern(System) was to provide a workaround when... version(Windows) extern(Windows): else extern(C): ...stopped working ?!? from spec: "System is the same as Windows on Windows platforms, and C on other platforms"
Yeah, that was it. I think we just had some miscommunication going on. -JJR
Nov 30 2008
parent reply Kagamin <spam here.lot> writes:
G++ uses C++ abstract class for nsISupports, so its methods calling convention
is C++ as implemented by g++:
http://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Compatibility.html
Dec 01 2008
parent reply John Reimer <terminal.node gmail.com> writes:
Hello Kagamin,

 G++ uses C++ abstract class for nsISupports, so its methods calling
 convention is C++ as implemented by g++:
 
 http://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Compatibility.html
 
Yes, I had a peek at the XULRunner SDK, and it looked something like you say. Thank you for the link. It looks like the most significant details of the C++ ABI in this case (for XPCOM to D interfacing) are: (1) name mangling (which won't be an issue in XPCOM from what I understand) (2) layout and alignment of virtual tables (*important and effectively handled by extern(C++) /IF/ the C++ classes meet the criteria specified in the D spec). It would be nice if extern(C++) were still ported to D 1.0, but the other alternative of enabling extern(System) in COM support (instead of extern(Windows) ) will also work. Extern(C++) might turn out to be just another alternative which would be nice to have. -JJR
Dec 01 2008
parent reply Walter Bright <newshound1 digitalmars.com> writes:
John Reimer wrote:
 It would be nice if extern(C++) were still ported to D 1.0, but the 
 other alternative of enabling extern(System) in COM support (instead of 
 extern(Windows) ) will also work.
Unfortunately, extern(C++), even if ported to D1, will not work for COM objects. COM objects, to be compatible with Windows, need to be the Windows calling convention.
Dec 01 2008
parent reply John Reimer <terminal.node gmail.com> writes:
Hello Walter,

 John Reimer wrote:
 
 It would be nice if extern(C++) were still ported to D 1.0, but the
 other alternative of enabling extern(System) in COM support (instead
 of extern(Windows) ) will also work.
 
Unfortunately, extern(C++), even if ported to D1, will not work for COM objects. COM objects, to be compatible with Windows, need to be the Windows calling convention.
Er... Yes, actually my statement above was meant to emphasize the potential usefulness of extern(C++) for XPCOM support on Linux among many things. That said, I'm guessing that even extern(C++) would work fine for COM support on windows if one directly declares the interface methods with extern(Windows) attribute. Similar to COM, C++ XPCOM on win32 seems to do this as well: it applies stdcall to the XPCOM class (interface) methods. In D (windows) it would look like so (assuming you have done away with the compilers internal detection of COM interfaces): extern(C++) interface IUnknown: { extern(Windows): int QueryInterface(nsIID* uuid, void **result); int AddRef(); int Release(); } I bet it would work fine and would be a good way to avoid the special-cased interfaces providing COM support in D. Instead we've special-cased for C++, which is, at least, a more general case. ;-) -JJR
Dec 01 2008
parent reply Walter Bright <newshound1 digitalmars.com> writes:
John Reimer wrote:
 In D (windows) it would look like so (assuming you have done away with 
 the compilers internal detection of COM interfaces):
 
 extern(C++) interface IUnknown:
 {
     extern(Windows):
         int QueryInterface(nsIID* uuid, void **result);
         int AddRef();
         int Release();
 }
 
 I bet it would work fine and would be a good way to avoid the 
 special-cased interfaces providing COM support in D.  Instead we've 
 special-cased for C++, which is, at least, a more general case. ;-)
There is one other problem. COM objects are not collected by the garbage collector because they are reference counted. Deriving from IUnknown means its a COM object which sets a flag in the ClassInfo, directing the garbage collector to not collect it. In fact, COM objects are allocated using malloc(). IUnknown.Release() uses free() to deallocate them.
Dec 02 2008
next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Tue, 02 Dec 2008 13:23:55 +0300, Walter Bright  
<newshound1 digitalmars.com> wrote:

 John Reimer wrote:
 In D (windows) it would look like so (assuming you have done away with  
 the compilers internal detection of COM interfaces):
  extern(C++) interface IUnknown:
 {
     extern(Windows):
         int QueryInterface(nsIID* uuid, void **result);
         int AddRef();
         int Release();
 }
  I bet it would work fine and would be a good way to avoid the  
 special-cased interfaces providing COM support in D.  Instead we've  
 special-cased for C++, which is, at least, a more general case. ;-)
There is one other problem. COM objects are not collected by the garbage collector because they are reference counted. Deriving from IUnknown means its a COM object which sets a flag in the ClassInfo, directing the garbage collector to not collect it. In fact, COM objects are allocated using malloc(). IUnknown.Release() uses free() to deallocate them.
Well, C++ objects shouldn't be garbage-collected either, should they?
Dec 02 2008
prev sibling next sibling parent reply John C <johnch_atms hotmail.com> writes:
Walter Bright Wrote:

 John Reimer wrote:
 In D (windows) it would look like so (assuming you have done away with 
 the compilers internal detection of COM interfaces):
 
 extern(C++) interface IUnknown:
 {
     extern(Windows):
         int QueryInterface(nsIID* uuid, void **result);
         int AddRef();
         int Release();
 }
 
 I bet it would work fine and would be a good way to avoid the 
 special-cased interfaces providing COM support in D.  Instead we've 
 special-cased for C++, which is, at least, a more general case. ;-)
There is one other problem. COM objects are not collected by the garbage collector because they are reference counted. Deriving from IUnknown means its a COM object which sets a flag in the ClassInfo, directing the garbage collector to not collect it. In fact, COM objects are allocated using malloc(). IUnknown.Release() uses free() to deallocate them.
But IUnknown.Release does no such thing. If you're talking about the implementation in std.c.windows.com.ComObject, then all it does is decrement the reference count. It has a comment saying "let the GC reap it" - which is incorrect, since the GC doesn't collect COM objects (as you say above). So anyone deriving from ComObject is leaking memory. John C.
Dec 02 2008
parent Walter Bright <newshound1 digitalmars.com> writes:
John C wrote:
 But IUnknown.Release does no such thing. If you're talking about the
 implementation in std.c.windows.com.ComObject, then all it does is
 decrement the reference count. It has a comment saying "let the GC
 reap it" - which is incorrect, since the GC doesn't collect COM
 objects (as you say above). So anyone deriving from ComObject is
 leaking memory.
There does appear to be a bug there, because the allocator allocates it with malloc().
Dec 02 2008
prev sibling parent reply John Reimer <terminal.node gmail.com> writes:
Hello Walter,

 John Reimer wrote:
 
 In D (windows) it would look like so (assuming you have done away
 with the compilers internal detection of COM interfaces):
 
 extern(C++) interface IUnknown:
 {
 extern(Windows):
 int QueryInterface(nsIID* uuid, void **result);
 int AddRef();
 int Release();
 }
 I bet it would work fine and would be a good way to avoid the
 special-cased interfaces providing COM support in D.  Instead we've
 special-cased for C++, which is, at least, a more general case. ;-)
 
There is one other problem. COM objects are not collected by the garbage collector because they are reference counted. Deriving from IUnknown means its a COM object which sets a flag in the ClassInfo, directing the garbage collector to not collect it. In fact, COM objects are allocated using malloc(). IUnknown.Release() uses free() to deallocate them.
Okay, that is an important detail for me to know. But I assumed that a C++ interface must not be allocated in D either. In must acquire a pointer from an external factory that handles the allocation outside of the gc range (C++ code must allocate/delete it's own objects). This seems to me to be a somewhat equivalent process as for a COM object. Is there a ClassInfo flag indicating no-gc-collection for extern(C++) interfaces too? -JJR
Dec 02 2008
parent reply Walter Bright <newshound1 digitalmars.com> writes:
John Reimer wrote:
 Okay, that is an important detail for me to know.  But I assumed that a 
 C++ interface must not be allocated in D either.  In must acquire a 
 pointer from an external factory that handles the allocation outside of 
 the gc range (C++ code must allocate/delete it's own objects).
 
 This seems to me to be a somewhat equivalent process as for a COM object.
 
 Is there a ClassInfo flag indicating no-gc-collection for extern(C++) 
 interfaces too?
No. The COM object is special because part of its interface is to do its own memory management, that isn't true in general for C++ interfaces.
Dec 02 2008
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Tue, 02 Dec 2008 22:32:33 -0800, Walter Bright wrote:

 John Reimer wrote:
 Is there a ClassInfo flag indicating no-gc-collection for extern(C++) 
 interfaces too?
No. The COM object is special because part of its interface is to do its own memory management, that isn't true in general for C++ interfaces.
But what's the typical use case for extern(C++) objects ? I think it's something like this: extern(C++) class CppInterfaceClass { ... } extern(C++) CppInterfaceClass create() { return new CppInterfaceClass(...); } extern(C++) void destroy(CppInterfaceClass obj) { delete obj; } Here, there are no references left in D code to the created interface object and GC is free to collect it even though it's still being used from within C++ code. I'll have to manage a list of created interface object simply to keep them alive, getting another multi-threading headache, or add them as GC roots. I think it's better to assume manual memory management for extern(C++) objects.
Dec 04 2008
parent reply Jason House <jason.james.house gmail.com> writes:
Sergey Gromov wrote:

 But what's the typical use case for extern(C++) objects ?  I think it's
 something like this:
 
   extern(C++) class CppInterfaceClass { ... }
 
   extern(C++) CppInterfaceClass create()
   {
     return new CppInterfaceClass(...);
   }
 
   extern(C++) void destroy(CppInterfaceClass obj)
   {
     delete obj;
   }
while extern(C++) will accept that code, but it won't work right.
Dec 06 2008
parent reply John Reimer <terminal.node gmail.com> writes:
Hello Jason,

 Sergey Gromov wrote:
 
 But what's the typical use case for extern(C++) objects ?  I think
 it's something like this:
 
 extern(C++) class CppInterfaceClass { ... }
 
 extern(C++) CppInterfaceClass create()
 {
 return new CppInterfaceClass(...);
 }
 extern(C++) void destroy(CppInterfaceClass obj)
 {
 delete obj;
 }
while extern(C++) will accept that code, but it won't work right.
(Sorry, I've been away for a bit) I don't think the example is how extern(C++) was designed to work either. When used for C++ classes, extern(C++), from what I gather, is intended to be applied to an /interface/ in the D code, NOT a D class: extern(C++) interface Foo { int foo(int a, b); } This interface represents the link to the external C++ class. An instance of that class is created by the C++ code using a factory function which D links to in its code (BUT does not implement): extern(C++) Foo create(); // in C++ code this is something like Foo* create() { return new Foo; } extern(C++) void destroy(Foo F); // also in C++ code since the same should destroy it In the D code, the factory function enforces the stricture that the C++ code is responsible for it's own object creation and destruction. D code should touch this. About the only thing that the D gc might monitor is the D interface reference created. Assignment to the extern(C++) interface in D like so: Foo F = create(); ensures that we have access to the C++ object. Perhaps a scope() statement may be used to call the C++ destroy method. Of course, the documentation on extern(C++) already discusses this. I just thought it should be made clear here since the prior code sample implies that the C++ object creation and destruction are done in D, which it cannot be. The fact that D 2.0 uses a special-case of D interface (kind of similar to COM interfaces) was primarily why I became interested in the possibility of using it as an opportunity to interface with XPCOM. But it seems XPCOM is more appropriatly implemented by taking advantage of the COM alias functionality as is. Also the extern(C++) technique is a D 2.0-only feature, so I will leave such experimentation for the such a time as DWT/XPCOM is ready to be ported to D 2.0. -JJR
Dec 06 2008
next sibling parent John Reimer <terminal.node gmail.com> writes:
 In the D code, the factory function enforces the stricture that the
 C++ code is responsible for it's own object creation and destruction.
 D code should touch this.  
Last line above meant to say: "D code should NOT touch this" as in, D code should not take any responsibility for creation/destruction of the object. -JJR
Dec 06 2008
prev sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Sun, 7 Dec 2008 07:04:22 +0000 (UTC), John Reimer wrote:

 Hello Jason,
 
 Sergey Gromov wrote:
 
 But what's the typical use case for extern(C++) objects ?  I think
 it's something like this:
 
 extern(C++) class CppInterfaceClass { ... }
 
 extern(C++) CppInterfaceClass create()
 {
 return new CppInterfaceClass(...);
 }
 extern(C++) void destroy(CppInterfaceClass obj)
 {
 delete obj;
 }
while extern(C++) will accept that code, but it won't work right.
(Sorry, I've been away for a bit) I don't think the example is how extern(C++) was designed to work either. When used for C++ classes, extern(C++), from what I gather, is intended to be applied to an /interface/ in the D code, NOT a D class: extern(C++) interface Foo { int foo(int a, b); } This interface represents the link to the external C++ class. An instance of that class is created by the C++ code using a factory function which D links to in its code (BUT does not implement):
This is one possible use. But the opposite use is also perfectly possible. The ultimate example of the opposite use is a COM object implemented in D.
 Of course, the documentation on extern(C++) already discusses this.  I just 
 thought it should be made clear here since the prior code sample implies 
 that the C++ object creation and destruction are done in D, which it cannot 
 be.
Of course it can. My example wasn't quite correct though. Assuming your definition of interface Foo, here is the implementation:
 extern(C++) interface Foo { 
     int foo(int a, int b);
 }
class FooImpl : Foo { override int foo(int a, int b) { // implicit extern(C++), correct VTAB placement return a + b; } } extern(C++) Foo createFoo() { return new FooImpl; } extern(C++) void destroyFoo(Foo foo) { delete foo; } The only problem with this code is that FooImpl will be GCed as soon as it leaves the createFoo().
Dec 08 2008
parent John Reimer <terminal.node gmail.com> writes:
Hello Sergey,

 Sun, 7 Dec 2008 07:04:22 +0000 (UTC), John Reimer wrote:
 
 Hello Jason,
 
 Sergey Gromov wrote:
 
 But what's the typical use case for extern(C++) objects ?  I think
 it's something like this:
 
 extern(C++) class CppInterfaceClass { ... }
 
 extern(C++) CppInterfaceClass create()
 {
 return new CppInterfaceClass(...);
 }
 extern(C++) void destroy(CppInterfaceClass obj)
 {
 delete obj;
 }
while extern(C++) will accept that code, but it won't work right.
(Sorry, I've been away for a bit) I don't think the example is how extern(C++) was designed to work either. When used for C++ classes, extern(C++), from what I gather, is intended to be applied to an /interface/ in the D code, NOT a D class: extern(C++) interface Foo { int foo(int a, b); } This interface represents the link to the external C++ class. An instance of that class is created by the C++ code using a factory function which D links to in its code (BUT does not implement):
This is one possible use. But the opposite use is also perfectly possible. The ultimate example of the opposite use is a COM object implemented in D.
 Of course, the documentation on extern(C++) already discusses this.
 I just thought it should be made clear here since the prior code
 sample implies that the C++ object creation and destruction are done
 in D, which it cannot be.
 
Of course it can. My example wasn't quite correct though. Assuming your definition of interface Foo, here is the implementation:
 extern(C++) interface Foo {
 int foo(int a, int b);
 }
class FooImpl : Foo { override int foo(int a, int b) { // implicit extern(C++), correct VTAB placement return a + b; } } extern(C++) Foo createFoo() { return new FooImpl; } extern(C++) void destroyFoo(Foo foo) { delete foo; } The only problem with this code is that FooImpl will be GCed as soon as it leaves the createFoo().
Yes, you are correct here, as far as I understand it. My apologies for forming the implication that you misunderstood. -JJR
Dec 08 2008
prev sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Tomas Lindquist Olsen wrote:
 Walter Bright wrote:
 John Reimer wrote:
 So extern(System) does not translate to extern(C)?   Does that mean 
 that all extern(System)'s in my code are defaulting to extern(D)?

 That'd be a shocker to me. :)  If that's true, it would certainly 
 change my understanding of what I thought was making my code work. :D
No, extern(System) does on linux just what it does on windows, it's just that there's no point to it on linux.
I thought the whole point of extern(System) was to provide a workaround when... version(Windows) extern(Windows): else extern(C): ...stopped working ?!? from spec: "System is the same as Windows on Windows platforms, and C on other platforms"
I goofed, you're right.
Dec 01 2008
prev sibling parent reply John Reimer <terminal.node gmail.com> writes:
Hello Walter,

 So are you saying that XPCOM will work on Linux with D if only the
 extern(Windows) was actually extern(C++) ?
 
Just to make this clear, I'm banking on that the fact that extern(C++) on an interface should also remove the necessity to alias to a COM interface on linux. I am guessing that extern(C++) makes a vtable the same as COM minus the extern(Windows) decoration. If this is not exactly true, then my hypothesis won't hold. :-) -JJR
Nov 30 2008
next sibling parent John Reimer <terminal.node gmail.com> writes:
Hello John,

 Hello Walter,
 
 So are you saying that XPCOM will work on Linux with D if only the
 extern(Windows) was actually extern(C++) ?
 
Just to make this clear, I'm banking on that the fact that extern(C++) on an interface should also remove the necessity to alias to a COM interface on linux. I am guessing that extern(C++) makes a vtable the same as COM minus the extern(Windows) decoration. If this is not exactly true, then my hypothesis won't hold. :-) -JJR
One more detail: I just want to emphasize that solution to this is important primarily for the sake of compiler portability. At this point, dwt Browser code compiles fine with dmd (at this point, only with dmd), but with the hacks I've used, there are certainly no guarantees that it will ever work with other compilers. Finding a clean way to implement this is therefore fairly important, I think. And since I'd consider XPCOM/Mozilla a significant niche in software development - especially in crossplatform software development - I think this only emphasizes the necessity of getting this working nicely. :) That said, I'll try to cook up an example using dmd 2.0 to see if this will work with extern(C++). -JJR
Nov 30 2008
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
John Reimer wrote:
 Hello Walter,
 
 So are you saying that XPCOM will work on Linux with D if only the
 extern(Windows) was actually extern(C++) ?
Just to make this clear, I'm banking on that the fact that extern(C++) on an interface should also remove the necessity to alias to a COM interface on linux. I am guessing that extern(C++) makes a vtable the same as COM minus the extern(Windows) decoration. If this is not exactly true, then my hypothesis won't hold. :-)
Neither affects the vtable layout. They affect the calling convention (register usage, parameter order, stack cleaning, return value).
Nov 30 2008
parent reply John Reimer <terminal.node gmail.com> writes:
Hello Walter,

 John Reimer wrote:
 
 Hello Walter,
 
 So are you saying that XPCOM will work on Linux with D if only the
 extern(Windows) was actually extern(C++) ?
 
Just to make this clear, I'm banking on that the fact that extern(C++) on an interface should also remove the necessity to alias to a COM interface on linux. I am guessing that extern(C++) makes a vtable the same as COM minus the extern(Windows) decoration. If this is not exactly true, then my hypothesis won't hold. :-)
Neither affects the vtable layout. They affect the calling convention (register usage, parameter order, stack cleaning, return value).
Okay, I'm confused then. While I understand that the compiler uses specifically uses extern(...) to describe the calling convention, I /assumed/ that the compiler was detecting this the extern(C++) like so: extern (C++) { interface D { int bar(int i, int j, int k); } D getD(); } And converting what would normal be a D interface (with RTTI reference in first element of vtable) to a regular vtable compatible with a C++ class type? How is the vtable made C++ compatible? -JJR
Nov 30 2008
next sibling parent Walter Bright <newshound1 digitalmars.com> writes:
John Reimer wrote:
 Okay, I'm confused then.  While I understand that the compiler uses 
 specifically uses extern(...) to describe the calling convention, I 
 /assumed/ that the compiler was detecting this the extern(C++) like so:
I think you're right, I'd forgotten.
Nov 30 2008
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
John Reimer wrote:
 Hello Walter,
 
 John Reimer wrote:

 Hello Walter,

 So are you saying that XPCOM will work on Linux with D if only the
 extern(Windows) was actually extern(C++) ?
Just to make this clear, I'm banking on that the fact that extern(C++) on an interface should also remove the necessity to alias to a COM interface on linux. I am guessing that extern(C++) makes a vtable the same as COM minus the extern(Windows) decoration. If this is not exactly true, then my hypothesis won't hold. :-)
Neither affects the vtable layout. They affect the calling convention (register usage, parameter order, stack cleaning, return value).
Okay, I'm confused then. While I understand that the compiler uses specifically uses extern(...) to describe the calling convention, I /assumed/ that the compiler was detecting this the extern(C++) like so: extern (C++) { interface D { int bar(int i, int j, int k); } D getD(); } And converting what would normal be a D interface (with RTTI reference in first element of vtable) to a regular vtable compatible with a C++ class type? How is the vtable made C++ compatible?
You're right, I was wrong. It looks like all I need to do is change the default linkage inside IUnknown from Windows to System, as System is implemented as Windows under windows, and C under linux.
Dec 01 2008
parent John Reimer <terminal.node gmail.com> writes:
Hello Walter,

 John Reimer wrote:
 
 Hello Walter,
 
 John Reimer wrote:
 
 Hello Walter,
 
 So are you saying that XPCOM will work on Linux with D if only the
 extern(Windows) was actually extern(C++) ?
 
Just to make this clear, I'm banking on that the fact that extern(C++) on an interface should also remove the necessity to alias to a COM interface on linux. I am guessing that extern(C++) makes a vtable the same as COM minus the extern(Windows) decoration. If this is not exactly true, then my hypothesis won't hold. :-)
Neither affects the vtable layout. They affect the calling convention (register usage, parameter order, stack cleaning, return value).
Okay, I'm confused then. While I understand that the compiler uses specifically uses extern(...) to describe the calling convention, I /assumed/ that the compiler was detecting this the extern(C++) like so: extern (C++) { interface D { int bar(int i, int j, int k); } D getD(); } And converting what would normal be a D interface (with RTTI reference in first element of vtable) to a regular vtable compatible with a C++ class type? How is the vtable made C++ compatible?
You're right, I was wrong. It looks like all I need to do is change the default linkage inside IUnknown from Windows to System, as System is implemented as Windows under windows, and C under linux.
Yes, that would be the fastest way to fix it. Thanks. -JJR
Dec 01 2008