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);
}
```

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