www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Fun with extern(C++)

reply Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
Okay, new issues:

  extern(C++) class Base {}
  class Derived : Base {}

It seems that 'Derived' is implicitly a C++ class... I guess that
seems okay, but this leads to a series of edges where things start
breaking down...

  pragma(msg, BaseClassesTuple!Derived);  -> Error: tuple index 0 exceeds 0

Okay... that's broken. But I need to know it's base class.

  pragma(msg, BaseTypeTuple!ComponentType);  -> (in Base)

Base is reported as an interface? That's weird... and get's me wondering:

  interface Iface {}
  class Derived2 : Base, Iface {}

-> Error: C++ class 'Derived2' cannot implement D interface 'Iface'

Hmmm. I wonder if this will cause problems...
Is it impossible to support a C++ class implementing a D interface?
Users will need to declare extern(C++) interfaces anytime they want to
use it on any class with a C++ base class?

I kinda hoped that having a C++ class at the bottom of the stack would
just be a detail once D classes are derived from it, but it seems that
any class derived from a C++ base is placed into a separate world.

At very least, I need BaseClassesTuple to work with C++ classes. I
suspect this is probably fixable?
Jan 26
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2016-01-26 13:59, Manu via Digitalmars-d wrote:

 Hmmm. I wonder if this will cause problems...
 Is it impossible to support a C++ class implementing a D interface?
No that I know how the C++ compatibility works but I would guess the compiler needs to know at compile time how to call a method. If it would be possible to implement a D interface as either a D class or C++ class. The compiler cannot know which runtime type a variable which is declared as an interface can hold. interface A { void foo(); } class B : A { void foo(); } extern(C++) class C : A { void foo(); } // assuming this works A a = new B; A b = new C; // assuming this works a.foo(); b.foo(); When the compiler sees "a" or "b", how should it know it should call "foo" as a D method or C++ method? It only knows about the static type which is A, a D interface. -- /Jacob Carlborg
Jan 26
next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 1/26/16 8:58 AM, Jacob Carlborg wrote:
 On 2016-01-26 13:59, Manu via Digitalmars-d wrote:

 Hmmm. I wonder if this will cause problems...
 Is it impossible to support a C++ class implementing a D interface?
No that I know how the C++ compatibility works but I would guess the compiler needs to know at compile time how to call a method. If it would be possible to implement a D interface as either a D class or C++ class. The compiler cannot know which runtime type a variable which is declared as an interface can hold. interface A { void foo(); } class B : A { void foo(); } extern(C++) class C : A { void foo(); } // assuming this works A a = new B; A b = new C; // assuming this works a.foo(); b.foo(); When the compiler sees "a" or "b", how should it know it should call "foo" as a D method or C++ method? It only knows about the static type which is A, a D interface.
The short answer to this question is, no you cannot. The layouts are different. It's why I have always said, D interfaces should implicitly convert to Object and support all Object methods. Only D classes can implement D interfaces. -Steve
Jan 26
parent reply Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 27 January 2016 at 01:38, Steven Schveighoffer via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 1/26/16 8:58 AM, Jacob Carlborg wrote:
 On 2016-01-26 13:59, Manu via Digitalmars-d wrote:

 Hmmm. I wonder if this will cause problems...
 Is it impossible to support a C++ class implementing a D interface?
No that I know how the C++ compatibility works but I would guess the compiler needs to know at compile time how to call a method. If it would be possible to implement a D interface as either a D class or C++ class. The compiler cannot know which runtime type a variable which is declared as an interface can hold. interface A { void foo(); } class B : A { void foo(); } extern(C++) class C : A { void foo(); } // assuming this works A a = new B; A b = new C; // assuming this works a.foo(); b.foo(); When the compiler sees "a" or "b", how should it know it should call "foo" as a D method or C++ method? It only knows about the static type which is A, a D interface.
The short answer to this question is, no you cannot. The layouts are different. It's why I have always said, D interfaces should implicitly convert to Object and support all Object methods. Only D classes can implement D interfaces.
Interface is just a vtable... what makes the layout different? Surely any class can take a vtable definition and populate it with some function pointers.
Jan 26
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 1/26/16 10:57 AM, Manu via Digitalmars-d wrote:
 On 27 January 2016 at 01:38, Steven Schveighoffer via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 On 1/26/16 8:58 AM, Jacob Carlborg wrote:
 On 2016-01-26 13:59, Manu via Digitalmars-d wrote:

 Hmmm. I wonder if this will cause problems...
 Is it impossible to support a C++ class implementing a D interface?
No that I know how the C++ compatibility works but I would guess the compiler needs to know at compile time how to call a method. If it would be possible to implement a D interface as either a D class or C++ class. The compiler cannot know which runtime type a variable which is declared as an interface can hold. interface A { void foo(); } class B : A { void foo(); } extern(C++) class C : A { void foo(); } // assuming this works A a = new B; A b = new C; // assuming this works a.foo(); b.foo(); When the compiler sees "a" or "b", how should it know it should call "foo" as a D method or C++ method? It only knows about the static type which is A, a D interface.
The short answer to this question is, no you cannot. The layouts are different. It's why I have always said, D interfaces should implicitly convert to Object and support all Object methods. Only D classes can implement D interfaces.
Interface is just a vtable... what makes the layout different? Surely any class can take a vtable definition and populate it with some function pointers.
I believe the D interface mechanism depends on the vtable layout being a certain way in the object. The C++ layout conflicts with that. I could imagine one could construct an appropriate TypeInfo pointer inside the C++ class, with the proper offset stored in a mock TypeInfo class, such that you could actually get the virtual function to call. But you would run into other problems. For example, if you wanted to cast to another interface, you would likely segfault. This would be a nightmare to do in a library. -Steve
Jan 26
prev sibling parent reply Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 26 January 2016 at 23:58, Jacob Carlborg via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 2016-01-26 13:59, Manu via Digitalmars-d wrote:

 Hmmm. I wonder if this will cause problems...
 Is it impossible to support a C++ class implementing a D interface?
No that I know how the C++ compatibility works but I would guess the compiler needs to know at compile time how to call a method. If it would be possible to implement a D interface as either a D class or C++ class. The compiler cannot know which runtime type a variable which is declared as an interface can hold. interface A { void foo(); } class B : A { void foo(); } extern(C++) class C : A { void foo(); } // assuming this works A a = new B; A b = new C; // assuming this works a.foo(); b.foo(); When the compiler sees "a" or "b", how should it know it should call "foo" as a D method or C++ method? It only knows about the static type which is A, a D interface.
I'm not sure there's a difference in the calling convention... the function being called may perform adjustment of the 'this' pointer such that it's correct relative to the class that implemented the function, but that's internal to the function being called.
Jan 26
parent reply Jacob Carlborg <doob me.com> writes:
On 2016-01-26 16:49, Manu via Digitalmars-d wrote:

 I'm not sure there's a difference in the calling convention... the
 function being called may perform adjustment of the 'this' pointer
 such that it's correct relative to the class that implemented the
 function, but that's internal to the function being called.
Isn't the layout of the vtables different? -- /Jacob Carlborg
Jan 26
parent reply Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 27 January 2016 at 02:01, Jacob Carlborg via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 2016-01-26 16:49, Manu via Digitalmars-d wrote:

 I'm not sure there's a difference in the calling convention... the
 function being called may perform adjustment of the 'this' pointer
 such that it's correct relative to the class that implemented the
 function, but that's internal to the function being called.
Isn't the layout of the vtables different?
Probably, but the layout of the vtable is defined by the interface, and the interface type is always known, so I don't see why there should be any problem. Whether it's extern(C++) or extern(D), the class populating the vtable with functions knows the layout. I think it all comes down to this conversion to Object thing. If an interface must do that, then that's probably an issue without jamming an Object instance in the class somewhere.
Jan 26
parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
On Tuesday, 26 January 2016 at 16:13:55 UTC, Manu wrote:
 Probably, but the layout of the vtable is defined by the 
 interface,
 and the interface type is always known, so I don't see why there
 should be any problem. Whether it's extern(C++) or extern(D), 
 the
 class populating the vtable with functions knows the layout.
 I think it all comes down to this conversion to Object thing. 
 If an
 interface must do that, then that's probably an issue without 
 jamming
 an Object instance in the class somewhere.
For a C++ class the first entry in the vtable is actually the first virtual function. (usually the destructor). For a D class the first entry in the vtable is the classinfo. Thus the problem if you derive a D class from a extern(C++) base class. I don't see any way to actually fix this, adjusting the this pointer won't help. Once you derive a D class from a extern(C++) base class it is no longer a fully functional D class. For example monitor (e.g. synchronized methods) won't work.
Jan 26
parent Igor <Vladamir.I google.com> writes:
On Tuesday, 26 January 2016 at 16:25:35 UTC, Benjamin Thaut wrote:
 On Tuesday, 26 January 2016 at 16:13:55 UTC, Manu wrote:
 Probably, but the layout of the vtable is defined by the 
 interface,
 and the interface type is always known, so I don't see why 
 there
 should be any problem. Whether it's extern(C++) or extern(D), 
 the
 class populating the vtable with functions knows the layout.
 I think it all comes down to this conversion to Object thing. 
 If an
 interface must do that, then that's probably an issue without 
 jamming
 an Object instance in the class somewhere.
For a C++ class the first entry in the vtable is actually the first virtual function. (usually the destructor). For a D class the first entry in the vtable is the classinfo. Thus the problem if you derive a D class from a extern(C++) base class. I don't see any way to actually fix this, adjusting the this pointer won't help. Once you derive a D class from a extern(C++) base class it is no longer a fully functional D class. For example monitor (e.g. synchronized methods) won't work.
Why couldn't D have been designed to extend the C++ class layout with the vtable at the start and the new stuff at the bottom? Would that have worked? If so, why not allow for a new class layout like "extern(C++) class X { }"
Jan 26
prev sibling next sibling parent reply Kagamin <spam here.lot> writes:
On Tuesday, 26 January 2016 at 12:59:35 UTC, Manu wrote:
 Hmmm. I wonder if this will cause problems...
 Is it impossible to support a C++ class implementing a D 
 interface?
D interfaces are implicitly convertible to Object.
 I kinda hoped that having a C++ class at the bottom of the 
 stack would just be a detail once D classes are derived from 
 it, but it seems that any class derived from a C++ base is 
 placed into a separate world.
D classes are derived from Object class.
Jan 26
parent reply Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 27 January 2016 at 00:37, Kagamin via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On Tuesday, 26 January 2016 at 12:59:35 UTC, Manu wrote:
 Hmmm. I wonder if this will cause problems...
 Is it impossible to support a C++ class implementing a D interface?
D interfaces are implicitly convertible to Object.
How does that work? I've never really used classes in D.
 I kinda hoped that having a C++ class at the bottom of the stack would
 just be a detail once D classes are derived from it, but it seems that any
 class derived from a C++ base is placed into a separate world.
D classes are derived from Object class.
So, you're saying that if you have an 'I' pointer, which may be implemented by a C++ class, it still needs to be able to cast to Object, and therefore fails? I wonder if a C++ class could somehow be promoted to a D class when being derived in D, by having an Object placed somewhere. 'this' adjustment might be required at that point though, and that's not really nice in C++.
Jan 26
next sibling parent reply Kagamin <spam here.lot> writes:
On Tuesday, 26 January 2016 at 15:53:39 UTC, Manu wrote:
 How does that work?
 I've never really used classes in D.
Interface vtable has a fixup, which added to the interface pointer gives object pointer.
 D classes are derived from Object class.
So, you're saying that if you have an 'I' pointer, which may be implemented by a C++ class, it still needs to be able to cast to Object, and therefore fails? I wonder if a C++ class could somehow be promoted to a D class when being derived in D, by having an Object placed somewhere.
I mean if you want to derive from two classes, that sounds nasty :)
Jan 26
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 1/26/16 12:08 PM, Kagamin wrote:
 On Tuesday, 26 January 2016 at 15:53:39 UTC, Manu wrote:
 How does that work?
 I've never really used classes in D.
Interface vtable has a fixup, which added to the interface pointer gives object pointer.
Technically, D interfaces only EXPLICITLY cast to Object. -Steve
Jan 26
prev sibling parent reply Elie Morisse <syniurge gmail.com> writes:
On Tuesday, 26 January 2016 at 15:53:39 UTC, Manu wrote:
 So, you're saying that if you have an 'I' pointer, which may be 
 implemented by a C++ class, it still needs to be able to cast 
 to Object, and therefore fails? I wonder if a C++ class could 
 somehow be promoted to a D class when being derived in D, by 
 having an Object placed somewhere.
It helps that Object is a just a handful of virtual methods. On Tuesday, 26 January 2016 at 16:25:35 UTC, Benjamin Thaut wrote:
 For a D class the first entry in the vtable is the classinfo. 
 Thus the problem if you derive a D class from a extern(C++) 
 base class. I don't see any way to actually fix this, adjusting 
 the this pointer won't help. Once you derive a D class from a 
 extern(C++) base class it is no longer a fully functional D 
 class. For example monitor (e.g. synchronized methods) won't 
 work.
Calypso has "hybrid" D classes inheriting from C++, with this layout: - D vptr - D monitor { start of C++ class } - C++ vptr - C++ fields... - might be other vptr and fields if the C++ class has more than one base { end of C++ class } - D fields... and when downcasted to the C++ base class the "this" pointer gets adjusted to point towards the start of the C++ class.
Jan 26
parent Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 27 Jan 2016 7:10 am, "Elie Morisse via Digitalmars-d" <
digitalmars-d puremagic.com> wrote:
 On Tuesday, 26 January 2016 at 15:53:39 UTC, Manu wrote:
 So, you're saying that if you have an 'I' pointer, which may be
implemented by a C++ class, it still needs to be able to cast to Object, and therefore fails? I wonder if a C++ class could somehow be promoted to a D class when being derived in D, by having an Object placed somewhere.
 It helps that Object is a just a handful of virtual methods.


 On Tuesday, 26 January 2016 at 16:25:35 UTC, Benjamin Thaut wrote:
 For a D class the first entry in the vtable is the classinfo. Thus the
problem if you derive a D class from a extern(C++) base class. I don't see any way to actually fix this, adjusting the this pointer won't help. Once you derive a D class from a extern(C++) base class it is no longer a fully functional D class. For example monitor (e.g. synchronized methods) won't work.
 Calypso has "hybrid" D classes inheriting from C++, with this layout:

  - D vptr
  - D monitor
   { start of C++ class }
  - C++ vptr
  - C++ fields...
  - might be other vptr and fields if the C++ class has more than one base
   { end of C++ class }
  - D fields...

 and when downcasted to the C++ base class the "this" pointer gets
adjusted to point towards the start of the C++ class. This is exactly what I had in mind. An alternative would be to place Object at the start of 'D fields', same fixup logic.
Jan 26
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
To understand this you have to step back and understand what interfaces
actually 
are.

They have one member, and that member is a pointer to its vtbl[]. The vtbl[] is 
populated with pointers to functions.

That is it.

There is no runtime type info there.

Interfaces are how different languages communicate with each other - Java, D, 
C++, Basic, etc., support this. This is one reason why there is no RTTI there. 
An interface is supposed to stand on its own.

So yes, D and C++ can communicate via interfaces.
Jan 26