| Thread overview | |||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
January 19, 2016 extern(C++) multiple inheritence | ||||
|---|---|---|---|---|
| ||||
I'm repeating this here from the depths of my prior whinge thread, since this is kind of a new topic.
D's multiple inheritance solution is single-inheritance + interfaces. Rightly so, it's a great solution, and this matches what all the C++ programmers I've ever worked with do anyway. Trouble is, it doesn't seem to work with extern(C++) at the moment.
Note: this is my current hard-blocker. I can't progress without this.
Situation:
-------------------
In C++:
class Base
{
virtual ~Base() {}
size_t x;
};
class Interface
{
virtual void A() = 0;
virtual void B() = 0;
virtual void C() = 0;
virtual void Method() = 0;
};
class Derived : public Base, public Interface
{
size_t y;
};
-------------------
D:
extern(C++) class Base
{
~this() {}
size_t x;
}
extern(C++) interface Interface
{
void A();
void B();
void C();
void Method();
}
extern(C++) class Derived : Base, Interface
{
size_t y;
}
-------------------
This code should work.
In C++, Derived is laid out:
{
void *__Base_vtable;
size_t x;
void *__Interface_vtable;
size_t y;
}
This is as I expect.
In D, according to the debuginfo output by DMD, Derived is reported as:
{
void *__Base_vtable;
size_t x;
size_t y;
}
* The actual binary may not match the debuginfo; I didn't check, but there is some evidence for this.
Surprisingly, this compiles, but calling Method generates wrong code:
00007FF72DB7381B mov rbx,qword ptr [this]
00007FF72DB7381F mov rcx,qword ptr [rbx+10h] // rcx is now
__Interface_vtable!
00007FF72DB73823 mov rcx,qword ptr [rcx]
00007FF72DB73826 sub rsp,20h
00007FF72DB7382A mov rax,qword ptr [rcx]
00007FF72DB7382D call qword ptr [rax+18h] // 18h is the
offset of Method, rax is wrong. [[rbx+10h]+18h] is the correct call.
We load rbx = [this], then we load rcx = [rbx+10h]. This looks good,
[rbx+10h] is the correct offset of __Interface_vtable in C++, even
though the debuginfo claims that's the offset of 'y'.
Then it gets weird; rcx = [rcx], this is effectively getting the first
function pointer in the vtable; A().
Then further compounding with rax = [rcx], loading the first 8 bytes
of program code from the function A()!
Finally it calls [rax+18h]. If rax were still __Interface_vtable, 18h
is the correct offset of the function I'm calling (Method), and it was
correct after the second opcode, but those 2 weird dereferences broke
it.
Hoping this can be supported, I can't move forward without this.
| ||||
January 19, 2016 Re: extern(C++) multiple inheritence | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Manu | On 19/01/2016 4:10 PM, Manu via Digitalmars-d wrote:
> I'm repeating this here from the depths of my prior whinge thread,
> since this is kind of a new topic.
>
> D's multiple inheritance solution is single-inheritance + interfaces.
> Rightly so, it's a great solution, and this matches what all the C++
> programmers I've ever worked with do anyway. Trouble is, it doesn't
> seem to work with extern(C++) at the moment.
>
Yeah, it never has. No attempt has ever been made to make it work.
If you want to take a look, it's probably not much more complicated than fixing the layout. The code should mostly be somewhere in todt.c (when ClassDeclaration::cpp is non-zero).
| |||
January 19, 2016 Re: extern(C++) multiple inheritence | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Daniel Murphy | On 1/19/2016 12:34 AM, Daniel Murphy wrote:
> Yeah, it never has. No attempt has ever been made to make it work.
Actually, as I recall it was made to match what DMC++ generates for Win32.
| |||
January 19, 2016 Re: extern(C++) multiple inheritence | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Daniel Murphy | On 01/19/2016 03:34 AM, Daniel Murphy wrote:
> On 19/01/2016 4:10 PM, Manu via Digitalmars-d wrote:
>> I'm repeating this here from the depths of my prior whinge thread,
>> since this is kind of a new topic.
>>
>> D's multiple inheritance solution is single-inheritance + interfaces.
>> Rightly so, it's a great solution, and this matches what all the C++
>> programmers I've ever worked with do anyway. Trouble is, it doesn't
>> seem to work with extern(C++) at the moment.
>>
>
> Yeah, it never has. No attempt has ever been made to make it work.
>
> If you want to take a look, it's probably not much more complicated than
> fixing the layout. The code should mostly be somewhere in todt.c (when
> ClassDeclaration::cpp is non-zero).
Is there an open issue? -- Andrei
| |||
January 20, 2016 Re: extern(C++) multiple inheritence | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On 19 January 2016 at 22:36, Andrei Alexandrescu via Digitalmars-d <digitalmars-d@puremagic.com> wrote: > > Is there an open issue? -- Andrei There is: https://issues.dlang.org/show_bug.cgi?id=15579 | |||
January 19, 2016 Re: extern(C++) multiple inheritence | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Manu | On 01/19/2016 09:02 AM, Manu via Digitalmars-d wrote:
> On 19 January 2016 at 22:36, Andrei Alexandrescu via Digitalmars-d
> <digitalmars-d@puremagic.com> wrote:
>>
>> Is there an open issue? -- Andrei
>
> There is: https://issues.dlang.org/show_bug.cgi?id=15579
Thanks, awesome. I've noticed this a while ago, but slipped off my mind before I reported it. -- Andrei
| |||
January 19, 2016 Re: extern(C++) multiple inheritence | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Tuesday, 19 January 2016 at 15:25:53 UTC, Andrei Alexandrescu wrote: > On 01/19/2016 09:02 AM, Manu via Digitalmars-d wrote: >> On 19 January 2016 at 22:36, Andrei Alexandrescu via Digitalmars-d >> <digitalmars-d@puremagic.com> wrote: >>> >>> Is there an open issue? -- Andrei >> >> There is: https://issues.dlang.org/show_bug.cgi?id=15579 > > Thanks, awesome. I've noticed this a while ago, but slipped off my mind before I reported it. -- Andrei For reference: https://mentorembedded.github.io/cxx-abi/abi.html#vtable | |||
January 20, 2016 Re: extern(C++) multiple inheritence | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | On 19/01/2016 8:04 PM, Walter Bright wrote:
> On 1/19/2016 12:34 AM, Daniel Murphy wrote:
>> Yeah, it never has. No attempt has ever been made to make it work.
>
> Actually, as I recall it was made to match what DMC++ generates for Win32.
>
Wasn't that from before we had extern(C++) classes? I did the extern(C++) single inheritance class layout but didn't touch interfaces.
| |||
January 19, 2016 Re: extern(C++) multiple inheritence | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Daniel Murphy | On 1/19/2016 1:59 PM, Daniel Murphy wrote:
> On 19/01/2016 8:04 PM, Walter Bright wrote:
>> On 1/19/2016 12:34 AM, Daniel Murphy wrote:
>>> Yeah, it never has. No attempt has ever been made to make it work.
>>
>> Actually, as I recall it was made to match what DMC++ generates for Win32.
>>
>
> Wasn't that from before we had extern(C++) classes? I did the extern(C++)
> single inheritance class layout but didn't touch interfaces.
We had COM, which was an earlier form of C++ class support.
| |||
January 22, 2016 Re: extern(C++) multiple inheritence | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | On Wednesday, 20 January 2016 at 00:45:34 UTC, Walter Bright wrote:
> On 1/19/2016 1:59 PM, Daniel Murphy wrote:
>> On 19/01/2016 8:04 PM, Walter Bright wrote:
>>> On 1/19/2016 12:34 AM, Daniel Murphy wrote:
>>>> Yeah, it never has. No attempt has ever been made to make it work.
>>>
>>> Actually, as I recall it was made to match what DMC++ generates for Win32.
>>>
>>
>> Wasn't that from before we had extern(C++) classes? I did the extern(C++)
>> single inheritance class layout but didn't touch interfaces.
>
> We had COM, which was an earlier form of C++ class support.
BTW, in docs I saw the following example:
Calling D Virtual Functions From C++
```
Given D code like:
extern (C++) int callE(E);
extern (C++) interface E
{
int bar(int i, int j, int k);
}
class F : E
{
extern (C++) int bar(int i, int j, int k)
{
writefln("i = %s", i);
writefln("j = %s", j);
writefln("k = %s", k);
return 8;
}
}
void main()
{
F f = new F();
callE(f);
}
```
```
The C++ code to access it looks like:
class E
{
public:
virtual int bar(int i, int j, int k);
};
int callE(E *e)
{
return e->bar(11,12,13);
}
```
In this example C++ class E hasn't fields and another stuff. What happens if I change E?
class E
{
std::string s;
public:
virtual int bar(int i, int j, int k);
int strageHash()
{
return s.length() + bar(0, 0, 0);
}
};
int callE(E *e)
{
return e->strageHash();
}
AFAIK, D can't generate correct F layout. D doesn't know sizeof(E), D doesn't know E ctor (the second one is fixable).
As result, new F() object can be smaller that E and this code has an unexpected behaviour.
Ok, we aren't gods and we can't suggest something better (exclusive of moveing multiple inheritance and C++ layout to D :).
However we may and should alert user about this limitation and about another limitations like this.
And when we add a new C++-related feature, we should declare C++-side limitations, in the first place, because they can't be enforced by D compiler.
And have someone any ideas about C++ to D interface generator?
| |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply