www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - C++ base class needs at least one virtual method

reply ezneh <petitv.isat gmail.com> writes:
Hello

While trying to make a 1:1 binding to a C/C++ lib, I got the 
following issue:

I only have access to the .h header file, and in there I have 
this:

class someclass {};
class otherclass : public someclass {};

When trying to translate that header file to D, I ended with the 
following code:

extern(C++):
class someclass {}
class otherclass : someclass {}

But when compiling, the following error comes up:

Error: class `modulename.otherclass` C++ base class someclass 
needs at least one virtual function


Am I missing something or is there a bug somewhere ?
I don't have access to the .cpp file and cannot see what are the 
methods of someclass / otherclass are.

Any suggestion ?
Feb 05 2019
parent reply evilrat <evilrat666 gmail.com> writes:
On Tuesday, 5 February 2019 at 14:23:00 UTC, ezneh wrote:
 Hello

 While trying to make a 1:1 binding to a C/C++ lib, I got the 
 following issue:

 I only have access to the .h header file, and in there I have 
 this:

 class someclass {};
 class otherclass : public someclass {};

 When trying to translate that header file to D, I ended with 
 the following code:

 extern(C++):
 class someclass {}
 class otherclass : someclass {}

 But when compiling, the following error comes up:

 Error: class `modulename.otherclass` C++ base class someclass 
 needs at least one virtual function


 Am I missing something or is there a bug somewhere ?
 I don't have access to the .cpp file and cannot see what are 
 the methods of someclass / otherclass are.

 Any suggestion ?
No surprises here, D can only inherit from virtual C++ classes and that message says exactly that(class must have at least one virtual method). This someclass looks for me as just a strange design decision, if it's not just a POD data object (i.e. simple struct that is declared as a class) then normally the class will have at least virtual destructor to allow proper destruction. In case it is really just the POD class you can declare it as struct, add it as a member and 'alias this'[1] it to provide sort-of-inheritance chain (i.e. it will be implicitly accepted using that alias part) extern(C++) struct someclass {} extern(C++) class otherclass { someclass baseclass; // <- "inherited" data must go before other members to preserve data layout alias baseclass this; // <- pseudo inheritance alias ... } Though in some cases depending on semantic it is possible that it might not work as expected. (Though I can't remeber any) If however otherclass is just a POD as well then you should turn it into a struct as well, same for all descendants... Just in case, there is few tools for generating bindings available - dstep(AFAIK no C++)[2], dpp[3], gentool[4], and more info on the wiki[5]. [1] https://dlang.org/spec/class.html#alias-this [2] https://github.com/jacob-carlborg/dstep [3] https://github.com/atilaneves/dpp [4] https://github.com/Superbelko/ohmygentool [5] https://wiki.dlang.org/Bindings
Feb 05 2019
parent reply ezneh <petitv.isat gmail.com> writes:
On Wednesday, 6 February 2019 at 02:36:33 UTC, evilrat wrote:
 No surprises here, D can only inherit from virtual C++ classes 
 and that message says exactly that(class must have at least one 
 virtual method).

 This someclass looks for me as just a strange design decision, 
 if it's not just a POD data object (i.e. simple struct that is 
 declared as a class) then normally the class will have at least 
 virtual destructor to allow proper destruction.

 In case it is really just the POD class you can declare it as 
 struct, add it as a member and 'alias this'[1] it to provide 
 sort-of-inheritance chain (i.e. it will be implicitly accepted 
 using that alias part)

     extern(C++)
     struct someclass {}

     extern(C++)
     class otherclass {
         someclass baseclass; // <- "inherited" data must go 
 before other members to preserve data layout
         alias baseclass this; // <- pseudo inheritance alias
         ...
     }

 Though in some cases depending on semantic it is possible that 
 it might not work as expected. (Though I can't remeber any)

 If however otherclass is just a POD as well then you should 
 turn it into a struct as well, same for all descendants...
Thanks for the trick, I'll try it and see how it goes. Since the class have nothing in them, I just made some "alias otherclass = baseclass" statements and it seems it is working (at least it's compiling, have to really test that too). From what I saw about the code is that those types are just "placeholders" because they aren't used directly by the API.
 Just in case, there is few tools for generating bindings 
 available - dstep(AFAIK no C++)[2], dpp[3], gentool[4], and 
 more info on the wiki[5].
I know about those tools, but usually (and sadly enough) they either just ignore any extern(C++) or don't fully work on Windows. I didn't test gentool yet, so I might have more luck with it, but for the other ones they didn't provide full translation of the headers yet.
Feb 05 2019
parent evilrat <evilrat666 gmail.com> writes:
On Wednesday, 6 February 2019 at 07:38:04 UTC, ezneh wrote:
 Thanks for the trick, I'll try  it and see how it goes.
 Since the class have nothing in them, I just made some "alias 
 otherclass = baseclass" statements and it seems it is working 
 (at least it's compiling, have to really test that too). From 
 what I saw about the code is that those types are just 
 "placeholders" because they aren't used directly by the API.
It still might be necessary to add empty struct with 'extern(C++, class)' because alias is, well, just alias, and it needs a real type because C++ signatures do contains parameters types, and obviously there will be no methods that accepts derived class so it will fail to link. That's what that 'alias this' is for. And since this is not direct inheritance it will most likely won't work when object expected to be passed by value (at least not without casting to void* then to base*, i.e. 'someFunc(*cast(baseclass*)cast(void*)&derivedObj)')
Feb 06 2019