How does inheritance and vtables work wrt. C++ and interop with D? Fns w/ Multiple-inheritance args impossible to bind to?

Gavin Ray gavinray at site.com
Tue May 25 02:47:19 UTC 2021


On Monday, 24 May 2021 at 20:31:18 UTC, sighoya wrote:
> On Monday, 24 May 2021 at 17:39:38 UTC, Gavin Ray wrote:
>> Hence why I was asking how to make D structs/classes that have 
>> compatible or identical vtables to multiply inherited objects 
>> to pass as arguments to `extern (C++)` functions.
>
> I think classes annotated with extern is your only high level 
> guaranteed == type safe option to be compatible to other c++ 
> classes.
>
> But you seek for the general multiple inheritance case which 
> seems not to be supported with `extern`, sadly.
> So you stick with manual solutions like template 
> metaprogramming or/and raw pointer fiddling.
> Anyway, both solutions would require an unsafe cast in the end, 
> so you are on your own.

The below seems to work at least, which is encouraging:

```cpp
#include <cstdio>
#define DLL_EXPORT __declspec(dllexport)

class Base1 {
public:
     virtual void overrideMe1() = 0;
     const int getSomething() { return 42; }
};

class Base2 {
public:
     virtual void overrideMe2() = 0;
     const int getOtherThing() { return 99; }
};

class Derived : public Base1, public Base2 {
public:
     int someInt;
     Derived(int someIntVal) { this->someInt = someIntVal; }
     virtual void overrideMe1() override;
     virtual void overrideMe2() override;
};

DLL_EXPORT void takesADerived(Derived* derived)
{
     printf("[C++] Calling Derived::overrideMe1() \n");
     derived->overrideMe1();
     printf("[C++] Calling Derived::overrideMe2() \n");
     derived->overrideMe2();
     printf("[C++] Calling Derived::getSomething() = %d \n",
         derived->getSomething());
     printf("[C++] Calling Derived::getOtherThing() = %d \n",
         derived->getOtherThing());
     printf("[C++] Derived::someInt = %d \n", derived->someInt);
}
```

```d
import core.stdc.stdio : printf;

extern (C++)
{
   void takesADerived(Derived derived);

   interface Base1 { void overrideMe1(); }
   interface Base2 { void overrideMe2(); }
   class Derived : Base1, Base2
   {
     int someInt;
     this(int someIntVal) { this.someInt = someIntVal; }
     void overrideMe1() { printf("[D] Dlang Derived overrideMe1 
called \n"); }
     void overrideMe2() { printf("[D] Dlang Derived overrideMe1 
called \n"); }
   }
}

void main()
{
   auto dlangDerived = new Derived(123);
   printf("[D] Calling C++ takesADerived() with D Derived* \n");
   takesADerived(dlangDerived);
}
```

![example](https://media.discordapp.net/attachments/242122752436338688/846565538163195974/unknown.png)

Unfortunately, it does not work if I try to add `final int 
getSomething()` or the other one to the D interfaces, it throws a 
symbol error because the mangled names are slightly different:

```sh

unresolved external symbol "public: int __cdecl 
Base1::getSomething(void)"
                   (?getSomething at Base1@@QEAAHXZ)
0000000000000000 T ?getSomething at Base1@@QEAA?BHXZ # < "nm" output
```

If I use `nm` and list the symbols, and then try to manually use 
the mangling scheme, it almost works but because the return types 
differ it won't compile =/
```d
extern class Derived : Base1, Base2
{
   int someInt;

   pragma(mangle, "?getOtherThing at Base2@@QEAA?BHXZ")
   int getOtherThing();
}
```

```sh
main.d(29): Error: Function type does not match previously 
declared function with the same mangled name: 
`?getOtherThing at Base2@@QEAA?BHXZ`
main.d(29):        Previous IR type: i32 (%main.Base2*)
main.d(29):        New IR type:      i32 (%main.Derived*)
```




More information about the Digitalmars-d-learn mailing list