Thread overview
extern(C++): Unresolved scalar deleting destructor
Jul 24, 2018
Stefan Koch
Jul 24, 2018
12345swordy
Jul 24, 2018
12345swordy
July 24, 2018
After reading 2.081 patchnotes about improvements with binding to cpp classes, I'm trying to test it - with simple examples and Qt as cpp library.

My naive approach is to bind just a couple of used methods of specific classes (omitting others) and try to use it directly in D just with linking of default Qt5Core.lib. Sometimes it works just fine:

---app.d
import std.conv;
import std.stdio;

extern(C++) {
    class QVariant {
        this();
        this(int);
        this(double);
        final ~this();

        final int toInt(bool *ok = null) const;
        final double toDouble(bool *ok = null) const;
    }
}

void main()
{
    QVariant a = new QVariant(5);
    QVariant b = new QVariant(5.5);
    writeln("a: " ~ a.toInt().to!string);
    writeln("b: " ~ b.toDouble().to!string);
    readln();
}
---

This example compiles and works like a charm.

Other classes like QObject or QCoreApplication do not link with error like this:

cpptest.obj : error LNK2001: unresolved external symbol ""public: virtual void * __cdecl QObject::`scalar deleting destructor'(unsigned int)" (??_GQObject@@UEAAPEAXI@Z)"

There is no such symbol in Qt5Core.lib, obviously. Is it my mistake somewhere? Why do some classes require this destructor when it doesnt actually exist in lib? And why do other classes work without it?
July 24, 2018
On Tuesday, 24 July 2018 at 15:48:28 UTC, Vladimir Marchevsky wrote:
> After reading 2.081 patchnotes about improvements with binding to cpp classes, I'm trying to test it - with simple examples and Qt as cpp library.
>
> My naive approach is to bind just a couple of used methods of specific classes (omitting others) and try to use it directly in D just with linking of default Qt5Core.lib. Sometimes it works just fine:
>
> ---app.d
> import std.conv;
> import std.stdio;
>
> extern(C++) {
>     class QVariant {
>         this();
>         this(int);
>         this(double);
>         final ~this();
>
>         final int toInt(bool *ok = null) const;
>         final double toDouble(bool *ok = null) const;
>     }
> }
>
> void main()
> {
>     QVariant a = new QVariant(5);
>     QVariant b = new QVariant(5.5);
>     writeln("a: " ~ a.toInt().to!string);
>     writeln("b: " ~ b.toDouble().to!string);
>     readln();
> }
> ---
>
> This example compiles and works like a charm.
>
> Other classes like QObject or QCoreApplication do not link with error like this:
>
> cpptest.obj : error LNK2001: unresolved external symbol ""public: virtual void * __cdecl QObject::`scalar deleting destructor'(unsigned int)" (??_GQObject@@UEAAPEAXI@Z)"
>
> There is no such symbol in Qt5Core.lib, obviously. Is it my mistake somewhere? Why do some classes require this destructor when it doesnt actually exist in lib? And why do other classes work without it?

Seems like it's virtual destructor could that be?

this qt5 binding: https://github.com/MGWL/QtE5/blob/master/source/qte5.d
seems to always define the constructor.

July 24, 2018
On Tuesday, 24 July 2018 at 16:30:41 UTC, Stefan Koch wrote:
> Seems like it's virtual destructor could that be?
>
> this qt5 binding: https://github.com/MGWL/QtE5/blob/master/source/qte5.d
> seems to always define the constructor.

No, simple virtual (or final) destructor binds just fine with "~this()" or "final ~this()". "Scalar deleting destructor" is special thing, afaik, and my C++ knowledge is not enough to determine why is it required by D side and not required in C++ or not available in compiled lib.

QtE5, DQml or similar projects mostly wrap cpp-objects - create them on cpp side with helper function, save returned pointer on d side and operate via wrapper-functions that call methods on cpp side. But it looks I should be mostly able to interact with cpp-defined classes directly now, from 2.081 patchnotes:

"This release also includes ABI fixes where destructors are now correctly added to the virtual table, and constructor/destructor calling semantics now match C++. With this, mixed-language class hierarchies are working, with construction/destruction being supported in either language."
July 24, 2018
On Tuesday, 24 July 2018 at 15:48:28 UTC, Vladimir Marchevsky wrote:
> After reading 2.081 patchnotes about improvements with binding to cpp classes, I'm trying to test it - with simple examples and Qt as cpp library.
>
> [...]

Have you tried @disable ~this()?

Alexander
July 24, 2018
On Tuesday, 24 July 2018 at 19:04:50 UTC, 12345swordy wrote:
> Have you tried @disable ~this()?

Just tried:
1) no changes, still error about unresolved "QObject::`scalar deleting destructor'(unsigned int)".
2) marking destructor as @disable will actually disable it, right? But I want to use to not receive a memory leak. Afaik, previous approach was to re-implement ctor/dtor in D but now they can be used as they are.
July 24, 2018
On Tuesday, 24 July 2018 at 19:14:26 UTC, Vladimir Marchevsky wrote:
> On Tuesday, 24 July 2018 at 19:04:50 UTC, 12345swordy wrote:
>> Have you tried @disable ~this()?
>
> Just tried:
> 1) no changes, still error about unresolved "QObject::`scalar deleting destructor'(unsigned int)".
> 2) marking destructor as @disable will actually disable it, right? But I want to use to not receive a memory leak. Afaik, previous approach was to re-implement ctor/dtor in D but now they can be used as they are.
I found similar error messages regarding c++:
https://stackoverflow.com/questions/42449063/vs-c-dll-scalar-deleteing-destructor
July 24, 2018
On Tuesday, 24 July 2018 at 19:30:49 UTC, 12345swordy wrote:
> I found similar error messages regarding c++:
> https://stackoverflow.com/questions/42449063/vs-c-dll-scalar-deleteing-destructor

Hmm, so looks like it's really because of virtual destructor, like Stefan mentioned before. Implementing empty destructor in D removes error. Gonna check what C++ does in similar case and any possible problems...

Thanks to everyone for pointing out the direction to dig :)
July 25, 2018
Back to questions after homework done :) What I've tested:

Made test class in CPP:

---testlib.cpp
class __declspec(dllexport) Foo {
    int *a;

public:
    Foo(int value) {
        a = new int(value);
    }

    ~Foo() {
        delete a;
    }
};
---

Binding on D side:

---
extern(C++) {
    class Foo {
        this(int value);
        final ~this();    //btw, is this a correct way to bind a non-virtual member?
                          //What about inheritance and so on?..
    }
}
---

Binding works perfect, both ctor and dtor work.

Changed dtor to be virtual (and removed final from D side) - anyway, compiles and works perfect. I can provide D implementation of destructor which replaces original virtual one (is it correct? Or can I call original one from D-specified?) - making it empty leads to memory leak as expected. So I see no problems here.

Back to QObject. Its destructor is "virtual ~QObject();" but having "~this();" on D side again leads to "unresolved scalar deleting destructor" of QObject. It looks like actual destructor binds correct (I would have another error if I make binding incorrect with "final ~this()") but for some reason it wants this another thing... Why?
July 25, 2018
On Wednesday, 25 July 2018 at 12:04:16 UTC, Vladimir Marchevsky wrote:
> What I've tested:

Even more: if I create another trivial class in cpp (say, Bar) and new/delete it in first one ("b = new Bar();" in ctor, "delete b;" in dtor of Foo) - D app with bound Foo just crashes silently for some reason.