digitalmars.D - Interfacing with XPCOM
- John Reimer (109/109) Nov 30 2008 As the title suggests, this post is about D interfaces, specifically as ...
- Walter Bright (2/2) Nov 30 2008 So are you saying that XPCOM will work on Linux with D if only the
- John Reimer (19/22) Nov 30 2008 Yes, I think so. But I should probably test the theory with dmd 2.0 fir...
- Walter Bright (7/36) Nov 30 2008 The system linkage was devised to match the Microsoft "syscall"
- John Reimer (6/44) Nov 30 2008 So extern(System) does not translate to extern(C)? Does that mean that...
- Walter Bright (3/8) Nov 30 2008 No, extern(System) does on linux just what it does on windows, it's just...
- John Reimer (9/20) Nov 30 2008 Okay. I've found it very useful on linux, especially when I'm doing win...
- Tomas Lindquist Olsen (8/17) Nov 30 2008 I thought the whole point of extern(System) was to provide a workaround ...
- John Reimer (3/29) Nov 30 2008 Yeah, that was it. I think we just had some miscommunication going on.
- Kagamin (2/2) Dec 01 2008 G++ uses C++ abstract class for nsISupports, so its methods calling conv...
- John Reimer (13/18) Dec 01 2008 Yes, I had a peek at the XULRunner SDK, and it looked something like you...
- Walter Bright (4/7) Dec 01 2008 Unfortunately, extern(C++), even if ported to D1, will not work for COM
- John Reimer (20/30) Dec 01 2008 Er... Yes, actually my statement above was meant to emphasize the potent...
- Walter Bright (6/20) Dec 02 2008 There is one other problem. COM objects are not collected by the garbage...
- Denis Koroskin (3/21) Dec 02 2008 Well, C++ objects shouldn't be garbage-collected either, should they?
- John C (3/24) Dec 02 2008 But IUnknown.Release does no such thing. If you're talking about the imp...
- Walter Bright (3/9) Dec 02 2008 There does appear to be a bug there, because the allocator allocates it
- John Reimer (9/32) Dec 02 2008 Okay, that is an important detail for me to know. But I assumed that a ...
- Walter Bright (3/12) Dec 02 2008 No. The COM object is special because part of its interface is to do its...
- Sergey Gromov (18/24) Dec 04 2008 But what's the typical use case for extern(C++) objects ? I think it's
- Jason House (2/16) Dec 06 2008 while extern(C++) will accept that code, but it won't work right.
- John Reimer (36/53) Dec 06 2008 (Sorry, I've been away for a bit)
- John Reimer (4/7) Dec 06 2008 Last line above meant to say: "D code should NOT touch this"
- Sergey Gromov (19/60) Dec 08 2008 This is one possible use. But the opposite use is also perfectly
- John Reimer (4/71) Dec 08 2008 Yes, you are correct here, as far as I understand it. My apologies for ...
- Walter Bright (2/25) Dec 01 2008 I goofed, you're right.
- John Reimer (7/10) Nov 30 2008 Just to make this clear, I'm banking on that the fact that extern(C++) o...
- John Reimer (13/26) Nov 30 2008 One more detail:
- Walter Bright (3/15) Nov 30 2008 Neither affects the vtable layout. They affect the calling convention
- John Reimer (17/33) Nov 30 2008 Okay, I'm confused then. While I understand that the compiler uses spec...
- Walter Bright (2/5) Nov 30 2008 I think you're right, I'd forgotten.
- Walter Bright (5/45) Dec 01 2008 You're right, I was wrong.
- John Reimer (3/50) Dec 01 2008 Yes, that would be the fastest way to fix it. Thanks.
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
So are you saying that XPCOM will work on Linux with D if only the extern(Windows) was actually extern(C++) ?
Nov 30 2008
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
John Reimer wrote:Hello Walter,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 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).
Nov 30 2008
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 -JJRHello Walter,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 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).
Nov 30 2008
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. :DNo, 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
Hello Walter,John Reimer wrote: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. -JJRSo 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. :DNo, 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
Walter Bright wrote:John Reimer wrote: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"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. :DNo, 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
Hello Tomas,Walter Bright wrote:Yeah, that was it. I think we just had some miscommunication going on. -JJRJohn Reimer wrote: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"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. :DNo, 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
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
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.htmlYes, 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
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
Hello Walter,John Reimer wrote: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. ;-) -JJRIt 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
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
On Tue, 02 Dec 2008 13:23:55 +0300, Walter Bright <newshound1 digitalmars.com> wrote:John Reimer wrote:Well, C++ objects shouldn't be garbage-collected either, should they?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
Walter Bright Wrote:John Reimer 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. John C.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
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
Hello Walter,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? -JJRIn 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
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
Tue, 02 Dec 2008 22:32:33 -0800, Walter Bright wrote:John Reimer 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; } 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.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 04 2008
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
Hello Jason,Sergey Gromov wrote:(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. -JJRBut 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
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
Sun, 7 Dec 2008 07:04:22 +0000 (UTC), John Reimer wrote:Hello Jason,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.Sergey Gromov wrote:(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):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.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
Hello Sergey,Sun, 7 Dec 2008 07:04:22 +0000 (UTC), John Reimer wrote:Yes, you are correct here, as far as I understand it. My apologies for forming the implication that you misunderstood. -JJRHello Jason,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.Sergey Gromov wrote:(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):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.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
Tomas Lindquist Olsen wrote:Walter Bright wrote:I goofed, you're right.John Reimer wrote: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"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. :DNo, extern(System) does on linux just what it does on windows, it's just that there's no point to it on linux.
Dec 01 2008
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
Hello John,Hello Walter,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++). -JJRSo 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
John Reimer wrote:Hello Walter,Neither affects the vtable layout. They affect the calling convention (register usage, parameter order, stack cleaning, return value).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. :-)
Nov 30 2008
Hello Walter,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: 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? -JJRHello Walter,Neither affects the vtable layout. They affect the calling convention (register usage, parameter order, stack cleaning, return value).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. :-)
Nov 30 2008
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
John Reimer wrote:Hello Walter,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.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: 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?Hello Walter,Neither affects the vtable layout. They affect the calling convention (register usage, parameter order, stack cleaning, return value).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. :-)
Dec 01 2008
Hello Walter,John Reimer wrote:Yes, that would be the fastest way to fix it. Thanks. -JJRHello Walter,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.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: 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?Hello Walter,Neither affects the vtable layout. They affect the calling convention (register usage, parameter order, stack cleaning, return value).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. :-)
Dec 01 2008