Thread overview
C++ base class needs at least one virtual method
Feb 05, 2019
ezneh
Feb 06, 2019
evilrat
Feb 06, 2019
ezneh
Feb 06, 2019
evilrat
February 05, 2019
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 ?
February 06, 2019
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


February 06, 2019
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.
February 06, 2019
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)')