al1-ce / clib

c++ stdlib and utils for D with betterC
GNU General Public License v3.0
3 stars 0 forks source link

Passing extern(C++) class as parent interface causes segfault due to dynamic cast #4

Closed fionalde closed 9 months ago

fionalde commented 9 months ago

Crashes when function arguments use interface types

al1-ce commented 9 months ago

Can you attach minimal reproduction code please?

fionalde commented 9 months ago

import core.stdc.stdio; import cppd.betterc; extern(C++) {

interface L{
    void testLFunc();
}

class Base:L{
    this(){
        puts("base constract");
    }

    override void testLFunc(){
        puts("call override function");
    }

    void testBaseFunc(){
        puts("call Base function");
    }

    ~this(){
        puts("base close");
    }
}

class Test:Base{
    int i;
    int y;

    this(int i,int y){
        this.i= i;
        this.y = y;
    }

    ~this(){
        puts("hello world");
    }
}

}

void test(L base){ base.testLFunc(); }

extern(C) int main() {

Test t = _new!Test(20,40);
printf("t.i is => %d  \n",t.i);
test(t);
_free(t);
return 0;

}

When running to printf, it will get stuck there and then I got this Error Program exited with code -1073741819 Runs normally when I use abstract classes

al1-ce commented 9 months ago

Thanks for reporting, I'll look into it today

al1-ce commented 9 months ago

Hmm, when I run this it gives me segfault when calling L.testLFunc

==3880== Memcheck, a memory error detector
==3880== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==3880== Using Valgrind-3.21.0 and LibVEX; rerun with -h for copyright info
==3880== Command: ./bin/cppd_test
==3880== 
Starting tests

Testing extern(C++) class
Hello world, 2, 1, 2

base construct
t.i is => 20
==3880== Invalid read of size 8
==3880==    at 0x10E31B: test.testLFunc(test.L) (test.d:64)
==3880==    by 0x10E438: main (test.d:82)
==3880==  Address 0x0 is not stack'd, malloc'd or (recently) free'd

Gotta look deeper into that

fionalde commented 9 months ago

I did not try to determine whether the pointer address is consistent if the parameter type is a parent or a subclass, at that time I tried custom object.d, in the self-implemented destroy In the function, the parameter type is the parent class and the subclass in the case of the parameter pointer address is different, I suspect that is this problem, want to solve and at least understand how to deal with the offset, before probably looked at object.d seems to be calculated by the type system, but I can not find this information, I am not a professional c++ Development, which is still not understood, stopped looking at it at that time

al1-ce commented 9 months ago

Ok, so, the problem is

(cast(L) t).testLFunc();

Which is a known problem with dynamic casting (https://issues.dlang.org/show_bug.cgi?id=21690), to work around it you can use static casting instead

(cast(L) cast(void*) t).testLFunc();

Which works as intended, but a bit ugly, so I've added new function (to now renamed module betterc->classes) _cast which does exactly that (commit 9b68957) and also added a readme segment dedicated to how to use cppd.classes

_cast!L(t).testLFunc();

And that's all that I can do with it as it is a "compiler bug" over of which I have no power.

Thanks again for bringing up this bug and I hope now it won't cause any problems