digitalmars.D.learn - How does inheritance and vtables work wrt. C++ and interop with D? Fns
- Gavin Ray (108/108) May 23 2021 So one of the problems with generating D code for bindings to C++
- Ola Fosheim Grostad (8/13) May 23 2021 I believe Clang and MSVC are using different layouts.
- Gavin Ray (49/63) May 23 2021 I guess that's maybe a better way of what I'm asking -- whether
- Ola Fosheim Grostad (4/7) May 23 2021 Yes, in the structure of the vtable, which is why the spec is so
- Alain De Vos (5/5) May 24 2021 Multiple inheritance is hard.
- Gavin Ray (10/17) May 24 2021 Yeah agreed, multiple inheritance is asking for trouble.
- Guillaume Piolat (8/29) May 24 2021 AFAIK multiple inheritance is described in this book
- Ola Fosheim Grostad (6/8) May 24 2021 It is described in the link I gave, or? If I tried to give an
- Ola Fosheim Grostad (5/7) May 24 2021 Btw clang docs say they strive to match msvsc, so apparently it
- sighoya (10/13) May 24 2021 I think classes annotated with extern is your only high level
- Gavin Ray (87/100) May 24 2021 The below seems to work at least, which is encouraging:
- evilrat (40/68) May 24 2021 That's just LDC thing, should work with DMD.
- Gavin Ray (54/61) May 25 2021 Ahh, that works!
- evilrat (5/21) May 25 2021 That last one with someInt is what I warned about. D ctor messed
- Gavin Ray (12/16) May 25 2021 Ah =/
- Ola Fosheim Grostad (5/7) May 25 2021 You can do anything you want with structs, raw memory, and
- evilrat (23/39) May 25 2021 Both this features uses mixins, but does conceptually different
- Adam D. Ruppe (8/10) May 25 2021 Like I said in chat, these are NOT the same thing. The C++
- sighoya (5/6) May 25 2021 Awesome!
- sighoya (6/7) May 24 2021 Here is a more informal report how it works in C++:
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: ```cpp 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? ```d 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? - See: https://run.dlang.io/is/3rJyMt ```d 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](https://i.imgur.com/Xqw6jKG.gif)
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: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): ```cpp 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 🤔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 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 23 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: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 thisI 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
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: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.On Sunday, 23 May 2021 at 21:02:31 UTC, Gavin Ray wrote: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 thisI 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
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.
May 24 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: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 Base1 QEAAHXZ) ``` 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 Base2 QEAA?BHXZ") int getOtherThing(); } ``` ```sh main.d(29): Error: Function type does not match previously declared function with the same mangled name: `?getOtherThing Base2 QEAA?BHXZ` main.d(29): Previous IR type: i32 (%main.Base2*) main.d(29): New IR type: i32 (%main.Derived*) ```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.
May 24 2021
On Tuesday, 25 May 2021 at 02:47:19 UTC, Gavin Ray wrote: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 Base1 QEAAHXZ) 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 Base2 QEAA?BHXZ") int getOtherThing(); } ``` ```sh main.d(29): Error: Function type does not match previously declared function with the same mangled name: `?getOtherThing Base2 QEAA?BHXZ` main.d(29): Previous IR type: i32 (%main.Base2*) main.d(29): New IR type: i32 (%main.Derived*) ```That's just LDC thing, should work with DMD. Are you sure `getOtherThing` marked final? Because in your C++ class it is not virtual and in your example it is not final as well. In C++ having multiple bases with final method means that both Base1 and Base2 have their own instances of that method. Anyway in order to call it you'll have to cast manually to target base. i.e. if you want to call `Base1::getOtherThing` and you have `Derived` you'll have to cast to `Base1`. ```d Derived d; // d.getOtherThing(); // likely won't link (cast(Base1)d) .getOtherThing(); // will call Base1 variant (cast(Base2)d) .getOtherThing(); // will call Base2 variant ``` It is also possible that you'll have to put them in using mixin template so it will create scope for them that doesn't collide with each other, though I think it's more of a hack than a feature. Something like ```d class Derived { mixin base1; // has final getOtherThing mixin base2; // has final getOtherThing } ``` then you can call it almost like in C++ ```d d.base1.getOtherThing(); d.base2.getOtherThing(); ``` Anyway all this stuff requires thorough research & testing as such ABI tinkering is very easy to mess up and very hard to debug, for example if you mess up the order(functions layout) it can land on another final method call that seemingly work but in debugger you'll see that you can't hit breakpoint in that method. Happy debugging time!
May 24 2021
On Tuesday, 25 May 2021 at 06:02:55 UTC, evilrat wrote:Anyway all this stuff requires thorough research & testing as such ABI tinkering is very easy to mess up and very hard to debug, for example if you mess up the order(functions layout) it can land on another final method call that seemingly work but in debugger you'll see that you can't hit breakpoint in that method. Happy debugging time!Ahh, that works! It also works without the `cast(BaseType)` if you use `alias` in the class: ```d import core.stdc.stdio : printf; extern (C++) { void takesADerived(Derived derived); Derived createTestDerivedCPP(); extern interface Base1 { void overrideMe1(); pragma(mangle, "?getSomething Base1 QEAA?BHXZ") final int getSomething(); pragma(mangle, "?inheritedFunctionWithArgs Base1 QEAAHHH Z") final int inheritedFunctionWithArgs(int x, int y); } extern interface Base2 { void overrideMe2(); pragma(mangle "?getOtherThing Base2 QEAA?BHXZ") final int getOtherThing(); } extern class Derived : Base1, Base2 { int someInt; alias getSomething = Base1.getSomething; alias inheritedFunctionWithArgs = Base1.inheritedFunctionWithArgs; 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() { Derived dlangDerived = new Derived(123); printf("[D] Derived.Base1::getSomething() = %d \n", dlangDerived.getSomething()); printf("[D] Derived.Base2::getOtherThing() = %d \n", dlangDerived.getOtherThing()); printf("[D] Derived.Base1::inheritedFunctionWithArgs(5, 10) = %d \n", dlangDerived.inheritedFunctionWithArgs(5, 10)); printf("[D] Calling C++ takesADerived() with D Derived* \n"); takesADerived(dlangDerived); } ``` ![output](https://i.imgur.com/Plbtlow.png) Just you wait, I'll learn enough to be of any use contributing to OMG yet! haha
May 25 2021
On Tuesday, 25 May 2021 at 17:52:14 UTC, Gavin Ray wrote:```d void main() { Derived dlangDerived = new Derived(123); printf("[D] Derived.Base1::getSomething() = %d \n", dlangDerived.getSomething()); printf("[D] Derived.Base2::getOtherThing() = %d \n", dlangDerived.getOtherThing()); printf("[D] Derived.Base1::inheritedFunctionWithArgs(5, 10) = %d \n", dlangDerived.inheritedFunctionWithArgs(5, 10)); printf("[D] Calling C++ takesADerived() with D Derived* \n"); takesADerived(dlangDerived); } ``` ![output](https://i.imgur.com/Plbtlow.png)That last one with someInt is what I warned about. D ctor messed up class layout, in this simple case where class data isn't used it almost works, but will be practically unusable in real scenarios.
May 25 2021
On Tuesday, 25 May 2021 at 18:03:00 UTC, evilrat wrote:That last one with someInt is what I warned about. D ctor messed up class layout, in this simple case where class data isn't used it almost works, but will be practically unusable in real scenarios.Ah =/ You have this in your code example: ```d static assert(Derived.someInt.offsetof == 16); // that's important otherwise D ctor will mess up everything ``` Would this fix it, or is it just not super viable to hack around C++ multiple inheritance in D? Maybe generating some wrapper C++ code to be linked could help too? I'm not sure though/don't really know enough to say
May 25 2021
On Tuesday, 25 May 2021 at 18:12:27 UTC, Gavin Ray wrote:Would this fix it, or is it just not super viable to hack around C++ multiple inheritance in D?You can do anything you want with structs, raw memory, and casting, so it is viable, if you have a strong interest for this. But if you are not a low level programmer you might find it tedious.
May 25 2021
On Tuesday, 25 May 2021 at 18:12:27 UTC, Gavin Ray wrote:On Tuesday, 25 May 2021 at 18:03:00 UTC, evilrat wrote:Both this features uses mixins, but does conceptually different things, my example with manual vtable casts is simpler but more mechanical, and it basically mimics how it actually works. Anyway the problem is with D side, compiler emits incorrect type info/layouts (no wonder, it is clearly stated multiple inheritance not supported), and then everything explodes. So one of the possible workarounds is to separate class layout and API with mixins/templates. No idea how it will affect usability though, it's quite possible that extending such horrible construct from D side would be cumbersome, too much PITA to do real job using that thing. Hard to say for sure without experiments. Also like I said, D already does half of the work, it just needs to collect all base classes and arrange vtable. One of the potential blockers could be frontend architecture, I don't know how much stuff such change would break, I think pretty much everything was designed with single base class in mind. Like this one, it has 'this' pointer, ctor/dtor and fields, but no mention about layouts anywhere. https://github.com/dlang/dmd/blob/master/src/dmd/aggregate.d#L86 The closest what it could be is https://github.com/dlang/dmd/blob/master/src/dmd/aggregate.d#L279That last one with someInt is what I warned about. D ctor messed up class layout, in this simple case where class data isn't used it almost works, but will be practically unusable in real scenarios.Ah =/ You have this in your code example: ```d static assert(Derived.someInt.offsetof == 16); // that's important otherwise D ctor will mess up everything ``` Would this fix it, or is it just not super viable to hack around C++ multiple inheritance in D? Maybe generating some wrapper C++ code to be linked could help too? I'm not sure though/don't really know enough to say
May 25 2021
On Tuesday, 25 May 2021 at 17:52:14 UTC, Gavin Ray wrote:void takesADerived(Derived derived); extern class Derived : Base1, Base2Like I said in chat, these are NOT the same thing. The C++ Derived is a *sibling* class, not a parent, child, nor binding to the D Derived. All your attempts to pass one off as the other is problematic. If you want to bind to the C++ derived, don't provide any bodies for the functions - just bind. If you want to inherit from it, you must first declare the binding in D, then inherit.
May 25 2021
On Tuesday, 25 May 2021 at 02:47:19 UTC, Gavin Ray wrote:The below seems to work at least, which is encouraging:Awesome! At least, it becomes problematic with fields in base classes, it would be nice if we could map them to property annotated functions in D interfaces.
May 25 2021
On Tuesday, 25 May 2021 at 08:10:25 UTC, sighoya wrote:On Tuesday, 25 May 2021 at 02:47:19 UTC, Gavin Ray wrote:I did some basic testing with code above, it seems class layout is recursively linear at least on Windows, and D follows C++ rules close enough, at least it works if one comments out all but the first base, and the rest can be hand crafted using structs and templates. With this code it is possible to pass back C++ class received from C++ code and call it. So it seems support for multiple inheritance shouldn't be that hard to implement (for Windows), no idea how it works on Linux though. ```d // replacement for second base template iBase2() { // final is needed or it will call overrideMe1 instead of this final void overrideMe2() { static struct _layout { void* b1; void* b2; int someInt; } _layout* base = cast(_layout*) cast(void*) this; alias fn = extern(C++) void function(void*); fn _fn = *cast(fn*) base.b2; // vtable for Base2, vtable[0] is overrideMe2 _fn(cast(void*)this); } } class Derived : Base1 //, Base2 { mixin iBase2 __base2; alias overrideMe2 = __base2.overrideMe2; static assert(Derived.someInt.offsetof == 16); // that's important otherwise D ctor will mess up everything } ``` Maybe I'll be able to create templated base class that does all this stuff with mixins and templates. like this ```d extern(C++) abstract class MultipleInheritance(T...) { // implementation // } class Derived : MultipleInheritance!(Base1, Base2) { ... } ```The below seems to work at least, which is encouraging:Awesome! At least, it becomes problematic with fields in base classes, it would be nice if we could map them to property annotated functions in D interfaces.
May 25 2021
On Tuesday, 25 May 2021 at 11:38:03 UTC, evilrat wrote:I did some basic testing with code above, it seems class layout is recursively linear at least on Windows, and D follows C++ rules close enough, at least it works if one comments out all but the first base, and the rest can be hand crafted using structs and templates. With this code it is possible to pass back C++ class received from C++ code and call it. So it seems support for multiple inheritance shouldn't be that hard to implement (for Windows), no idea how it works on Linux though. Maybe I'll be able to create templated base class that does all this stuff with mixins and templates. like this ```d extern(C++) abstract class MultipleInheritance(T...) { // implementation // } class Derived : MultipleInheritance!(Base1, Base2) { ... } ```Oh this is brilliant! Absolutely incredible work + findings! Is this conceptually similar at all to this bit of code that adr had given to me? ```d // Same thing repeated for Abstract2 interface Abstract1 { /* virtual */ void overrideMe1(); void sayHello(); mixin template Abstract1Impl() { void sayHello() { import std.stdio; writeln("Hello"); } } } class MyClass : Abstract1, Abstract2 { mixin Abstract1Impl; mixin Abstract2Impl; override void overrideMe1() { writeln("overrode 1"); } override void overrideMe2() { writeln("overrode 2"); } } void main() { auto it = new MyClass(); it.sayHello(); it.sayGoodbye(); it.overrideMe1(); it.overrideMe2(); } ``` --- Also, I wonder if there's a good way to build a quick CLI visualization + debugging tool for this using D's `.vtble[]` method. I would find it really useful for learning =D There are also some automatic ABI compliance checker tools/libraries, which could be good for programmatically verifying that the class produced by D is valid with the ABI of a method/class from a C++ library file. (I know for example, that Qt does this with their Python codegen tool, `Shiboken`, for auto-testing the generated code) - https://sourceware.org/libabigail/manual/libabigail-overview.html - https://github.com/lvc/abi-compliance-checker (this is what Shiboken is/was using)... libabigail provides facilities to perform deep comparisons of two ABIs. That is, it can compare the types of two sets of functions or variables and represents the result in a way that allows it to emit textual reports about the differences.This allows us to write tools like `abidiff` that can compare the ABI of two shared libraries and represent the result in a meaningful enough way to help us spot ABI incompatibilities. There are several other tools that are built using the Abigail framwork.```sh $ g++ -g -Wall -shared -o libtest-v0.so test-v0.cc $ g++ -g -Wall -shared -o libtest-v1.so test-v1.cc $ $ ../build/tools/abidiff libtest-v0.so libtest-v1.so Functions changes summary: 0 Removed, 1 Changed, 0 Added function Variables changes summary: 0 Removed, 0 Changed, 0 Added variable 1 function with some indirect sub-type change: [C]'function void foo(S0&)' has some indirect sub-type changes: parameter 0 of type 'S0&' has sub-type changes: in referenced type 'struct S0': size changed from 32 to 64 bits 1 data member insertion: 'char S0::inserted_member', at offset 0 (in bits) 1 data member change: 'int S0::m0' offset changed from 0 to 32 ```
May 25 2021
On Monday, 24 May 2021 at 17:39:38 UTC, Gavin Ray wrote:I'd be grateful for solid information on thisHere is a more informal report how it works in C++: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.23.4735&rep=rep1&type=pdf But in the end, I think a byte code analysis is needed to be sure. The best tools to visualize have already been proposed by you, so I think you are on a good path.
May 24 2021