Jump to page: 1 2 3
Thread overview
How does inheritance and vtables work wrt. C++ and interop with D? Fns w/ Multiple-inheritance args impossible to bind to?
May 23, 2021
Gavin Ray
May 23, 2021
Gavin Ray
May 24, 2021
Alain De Vos
May 24, 2021
Gavin Ray
May 24, 2021
Guillaume Piolat
May 24, 2021
sighoya
May 25, 2021
Gavin Ray
May 25, 2021
evilrat
May 25, 2021
Gavin Ray
May 25, 2021
evilrat
May 25, 2021
Gavin Ray
May 25, 2021
evilrat
May 25, 2021
Adam D. Ruppe
May 25, 2021
sighoya
May 25, 2021
evilrat
May 25, 2021
Gavin Ray
May 24, 2021
sighoya
May 23, 2021

So one of the problems with generating D code for bindings to C++ is that there's no true/direct multiple inheritance.

If anyone happens to understand well how vtables work and the way the compiler treats these things, is there a way to hackily make semantically-equivalent objects?

An example:

class Kickable {
  void nonVirtual() { /* impl */ }
  virtual void kick();
}

class Throwable {
  virtual void throw();
}

class KickableThrowable : Kickable, Throwable {}

// KickableThrowable or KickableThrowable*, not really sure which is more realistic
void takesKickableThrowable(KickableThrowable* thing) {}

Would making a class/struct that has the methods nonVirtual(), kick(), and throw() be usable as an argument to takesKickableThrowable(), since it would contain identical members/layout?

extern (C++)
class KickableThrowable {
   void nonVirtual();
   /* override */ void kick() {}
   /* override */ void kick() {}
}

extern (C++)
void takesKickableThrowable(KickableThrowable thing);

takesKickableThrowable(new KickableThrowable());

adr had shown me a way to mimic multiple inheritance using interfaces and mixin templates, would this be a viable approach?

interface Abstract1 {
    /* virtual */ void overrideMe1();

    void sayHello();
    mixin template Abstract1Impl() {
        void sayHello() { import std.stdio; writeln("Hello"); }
    }
}

interface Abstract2 {
    /* virtual */ void overrideMe2();

    void sayGoodbye();
    mixin template Abstract2Impl() {
        void sayGoodbye() { import std.stdio; writeln("Goodbye"); }
    }
}

class MyClass : Abstract1, Abstract2 {
  mixin Abstract1Impl;
  mixin Abstract2Impl;
  override void overrideMe1() { writeln("overrode 1"); }
  override void overrideMe2() { writeln("overrode 2"); }
}

Also, what is the best way to debug and learn about vtables and their impact on interopability in D?

Visual Studio has the "Struct Layout" extension (pictured below), and clang and cl.exe have options to dump vtable/record layouts:

$ cl.exe test.cpp /d1reportSingleClassLayoutMEOW

class BatMEOW   size(40):
        +---
 0      | +--- (base class Mammal)
 0      | &Mammal::Breathe

BatMEOW::$vftable@WingedAnimal@:
        | -16
 0      | &WingedAnimal::Flap

<SNIPPED>

BatMEOW::{dtor} this adjustor: 32
BatMEOW::__delDtor this adjustor: 32
BatMEOW::__vecDelDtor this adjustor: 32
vbi:       class  offset o.vbptr  o.vbte fVtorDisp
          Animal      32       8       4 0
$ clang -cc1 -fdump-vtable-layouts -emit-llvm  test.cpp

VFTable for 'Animal' (3 entries).
   0 | Animal RTTI
   1 | Animal::~Animal() [scalar deleting]
   2 | void Animal::Eat()

<SNIPPED>
$ clang -cc1 -fdump-record-layouts -emit-llvm  test.cpp

*** Dumping AST Record Layout
         0 | struct Animal
         0 |   (Animal vftable pointer)
           | [sizeof=8, align=8,
           |  nvsize=8, nvalign=8]

*** Dumping AST Record Layout
         0 | struct Mammal
         0 |   (Mammal vftable pointer)
         8 |   (Mammal vbtable pointer)
        16 |   struct Animal (virtual base)
        16 |     (Animal vftable pointer)
           | [sizeof=24, align=8,
           |  nvsize=16, nvalign=8]
<SNIPPED>

vtable inspector cpp

May 23, 2021

On Sunday, 23 May 2021 at 19:44:01 UTC, Gavin Ray wrote:

>

So one of the problems with generating D code for bindings to C++ is that there's no true/direct multiple inheritance.

If anyone happens to understand well how vtables work and the way the compiler treats these things, is there a way to hackily make semantically-equivalent objects?

I believe Clang and MSVC are using different layouts.
If I am not wrong clang/gcc are using the Itanium ABI, but I could be wrong.

https://itanium-cxx-abi.github.io/cxx-abi/abi.html#vtable

Maybe ask in the LDC forum as they follow Clang? In my opinion MI ought to be supported for binding, but... not sure if anyone has considered it.

May 23, 2021

On Sunday, 23 May 2021 at 20:16:17 UTC, Ola Fosheim Grostad wrote:

>

On Sunday, 23 May 2021 at 19:44:01 UTC, Gavin Ray wrote:

>

So one of the problems with generating D code for bindings to C++ is that there's no true/direct multiple inheritance.

If anyone happens to understand well how vtables work and the way the compiler treats these things, is there a way to hackily make semantically-equivalent objects?

I believe Clang and MSVC are using different layouts.
If I am not wrong clang/gcc are using the Itanium ABI, but I could be wrong.

https://itanium-cxx-abi.github.io/cxx-abi/abi.html#vtable

Maybe ask in the LDC forum as they follow Clang? In my opinion MI ought to be supported for binding, but... not sure if anyone has considered it.

I guess that's maybe a better way of what I'm asking -- whether the vtable is all that matters.

Because I've seen C ABI's for C++ API's that have multiple inheritance, and the code was like this (I'll just post a single inheritance example but you get the point):

class FUnknown {
    tresult queryInterface(void*, const TUID, void**);
    uint32* addRef(void *);
    uint32* release(void *);
};

class IComponentHandler : FUnknown {
    tresult beginEdit(void *, ParamID);
    tresult performEdit(void *, ParamID, ParamValue);
    tresult endEdit(void *, ParamID);
    tresult restartComponent(void *, int32);
};

#ifdef __cplusplus
extern "C" {
#endif
typedef struct FUnknownVTable {
    tresult (*queryInterface)(void *, const TUID, void **);
    uint32 (*addRef)(void *);
    uint32 (*release)(void *);
} FUnknownVTable;

typedef struct SFUnknown {
    FUnknownVTable *vtable;
} SFUnknown;

typedef struct IComponentHandlerVTable {
    FUnknownVTable FUnknown;
    tresult (*beginEdit)(void *, ParamID);
    tresult (*performEdit)(void *, ParamID, ParamValue);
    tresult (*endEdit)(void *, ParamID);
    tresult (*restartComponent)(void *, int32);
} IComponentHandlerVTable;

typedef struct SIComponentHandler {
    IComponentHandlerVTable *vtable;
} SIComponentHandler;

#ifdef __cplusplus
}
#endif

I don't really know anything at all about compilers or low-level code -- but is there any high-level notion of "inheritance" after it's been compiled?

I felt like it had to turn into something like blocks of memory that have pointers to methods and/or to other blocks of memory with pointers to methods (vtables). But I also have no clue 🤔

May 23, 2021

On Sunday, 23 May 2021 at 21:02:31 UTC, Gavin Ray wrote:

>

I don't really know anything at all about compilers or low-level code -- but is there any high-level notion of "inheritance" after it's been compiled?

Yes, in the structure of the vtable, which is why the spec is so hard to read.

If possible stick to single inheritance in C++...

May 24, 2021

Multiple inheritance is hard.
If you know how it works, just add it to the dmd,ldc compilers.
If no, try to walk around it :)
With interfaces you can mimic a bit, but don't get Bjarne Stroustrup level.

May 24, 2021

On Sunday, 23 May 2021 at 21:08:06 UTC, Ola Fosheim Grostad wrote:

>

On Sunday, 23 May 2021 at 21:02:31 UTC, Gavin Ray wrote:

>

I don't really know anything at all about compilers or low-level code -- but is there any high-level notion of "inheritance" after it's been compiled?

Yes, in the structure of the vtable, which is why the spec is so hard to read.

If possible stick to single inheritance in C++...

Yeah agreed, multiple inheritance is asking for trouble.

But unfortunately when you're binding to existing libraries you don't have control over the API

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.

Also general explanation of what makes a compiled variable compatible in terms of vtable with what's expected as an argument

I'd be grateful for solid information on this

May 24, 2021

On Monday, 24 May 2021 at 17:39:38 UTC, Gavin Ray wrote:

>

On Sunday, 23 May 2021 at 21:08:06 UTC, Ola Fosheim Grostad wrote:

>

On Sunday, 23 May 2021 at 21:02:31 UTC, Gavin Ray wrote:

>

I don't really know anything at all about compilers or low-level code -- but is there any high-level notion of "inheritance" after it's been compiled?

Yes, in the structure of the vtable, which is why the spec is so hard to read.

If possible stick to single inheritance in C++...

Yeah agreed, multiple inheritance is asking for trouble.

But unfortunately when you're binding to existing libraries you don't have control over the API

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.

Also general explanation of what makes a compiled variable compatible in terms of vtable with what's expected as an argument

I'd be grateful for solid information on this

AFAIK multiple inheritance is described in this book https://www.amazon.com/Inside-Object-Model-Stanley-Lippman/dp/0201834545

Multiple inheritance is a rare topic here, I doubt too many people know how it works internally.
Java and COM stuck on single-inheritance because it gives you 99% bang for the buck, also v-table dispatch in case of multiple inheritance is not as straightforward.

May 24, 2021

On Monday, 24 May 2021 at 18:46:00 UTC, Guillaume Piolat wrote:

>

Multiple inheritance is a rare topic here, I doubt too many people know how it works internally.

It is described in the link I gave, or? If I tried to give an informal description I would probably be inaccurate and that would be worse than reading the spec youself.

If an informal description is needed then the best option is to search the Clang mailing list.

May 24, 2021

On Monday, 24 May 2021 at 18:52:22 UTC, Ola Fosheim Grostad wrote:

>

If an informal description is needed then the best option is to search the Clang mailing list.

Btw clang docs say they strive to match msvsc, so apparently it is platform dependent. The only sensible option is to check with Clang people. If using an informal reference such as a book, make sure to get an errata, such books tend to have errors...

May 24, 2021

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.

« First   ‹ Prev
1 2 3