Jump to page: 1 2
Thread overview
betterC: new operator
Nov 22, 2020
Dibyendu Majumdar
Nov 22, 2020
Dennis
Nov 22, 2020
rikki cattermole
Nov 22, 2020
Guillaume Piolat
Nov 22, 2020
Basile B.
Nov 22, 2020
Basile B.
Nov 24, 2020
9il
Nov 24, 2020
9il
Nov 24, 2020
9il
November 22, 2020
I had assumed that new operator is not available in betterC.

But in following code, it seems the only way to initialize the object correctly ... is there another way I am missing? How should one cleanup objects?

import core.stdc.stdio : printf;

extern (C++) abstract class A {
    void sayHello();
}

extern (C++) class B : A {
    override void sayHello() {
        printf("hello\n");
    }
}

extern (C) void main() {
    scope b = new B;
    b.sayHello();
}

November 22, 2020
On Sunday, 22 November 2020 at 11:54:01 UTC, Dibyendu Majumdar wrote:
> But in following code, it seems the only way to initialize the object correctly ... is there another way I am missing?

Check out:
https://p0nce.github.io/d-idioms/#Placement-new-with-emplace

November 23, 2020
On 23/11/2020 12:57 AM, Dennis wrote:
> Check out:
> https://p0nce.github.io/d-idioms/#Placement-new-with-emplace

The question was about -betterC.

This won't work, it depends on TypeInfo.

https://github.com/dlang/phobos/blob/master/std/conv.d#L5197
November 22, 2020
On Sunday, 22 November 2020 at 12:11:39 UTC, rikki cattermole wrote:
>
> The question was about -betterC.
>
> This won't work, it depends on TypeInfo.

I think Adam knows how to do it?
November 22, 2020
On Sunday, 22 November 2020 at 11:54:01 UTC, Dibyendu Majumdar wrote:
> I had assumed that new operator is not available in betterC.
>
> But in following code, it seems the only way to initialize the object correctly ... is there another way I am missing? How should one cleanup objects?
>
> import core.stdc.stdio : printf;
>
> extern (C++) abstract class A {
>     void sayHello();
> }
>
> extern (C++) class B : A {
>     override void sayHello() {
>         printf("hello\n");
>     }
> }
>
> extern (C) void main() {
>     scope b = new B;
>     b.sayHello();
> }

You can recreate the vtable using static introspection, POC:

---
#!dmd -betterC
module a;

import core.stdc.stdlib : malloc;
import core.stdc.stdio  : printf;
import std.traits;

void setVtable(CT)(ref void** memory)
{
    alias members = __traits(allMembers, CT);
    void** entries = cast(void**) malloc(members.length + 1);
    size_t i;
    static foreach(m; members)
        static if (isSomeFunction!(__traits(getMember, CT, m)))
            static foreach (ov; __traits(getVirtualMethods, CT, m))
    {
        entries[i++] = &ov;
    }

    memory[0] = *entries;   // pointer to vtable
    memory[1] = null;       // monitor
    memory[2] = null;       // interfaces
}

CT New(CT, A...)(A a) @trusted
if (is(CT == class))
{
    enum size    = __traits(classInstanceSize, CT);
    void** memory = cast(void**) malloc(size);
    setVtable!CT(memory);
    CT result = cast(CT) &memory;
    static if (__traits(hasMember, CT, "__ctor"))
        result.__ctor(a);
    return result;
}

extern (C++) class A {
    abstract void sayHello() {}
}

extern (C++) class B : A {
    override void sayHello() {
        printf("hello\n");
    }
}

extern (C) void main() {
    B b = New!B();
    b.sayHello();
}
---

however note
1. not well tested (e.g overloads)
2. static init of fields is not done because that really rquires TypeInfo, so a ctor has to be used instead.

But that's a good start to get things done more properly
November 22, 2020
On Sunday, 22 November 2020 at 16:58:03 UTC, Basile B. wrote:
> On Sunday, 22 November 2020 at 11:54:01 UTC, Dibyendu Majumdar wrote:
>> I had assumed that new operator is not available in betterC.
>>
>> But in following code, it seems the only way to initialize the object correctly ... is there another way I am missing? How should one cleanup objects?
> however note
> 1. not well tested (e.g overloads)
> 2. static init of fields is not done because that really rquires TypeInfo, so a ctor has to be used instead.
>
> But that's a good start to get things done more properly

And the leaks...

More simple solution adapted from

http://dpldocs.info/this-week-in-d/Blog.Posted_2020_07_27.html#zero-runtime-classes

---
#!dmd -betterC
extern(C) int printf(const char*, ...);

extern(C++) class A  {
        int omg() { return 12; }
}

extern(C++) class B : A {
        override int omg() { return 34; }
}

template New(T) {
        pragma(mangle, "_D" ~ T.mangleof[1..$] ~ "6__initZ")
        __gshared extern immutable ubyte[__traits(classInstanceSize, T)] initializer;

        T New(ref ubyte[__traits(classInstanceSize, T)] memory) {
                foreach(idx, ref b; memory) {
                        b = initializer.ptr[idx];
                }
                return cast(T) memory.ptr;
        }
}

extern(C) int main() {
        ubyte[__traits(classInstanceSize, A)] buffer;
        A a = New!B(buffer);
        printf("hi %d\n", a.omg());
        return 0;
}
---
November 24, 2020
On Sunday, 22 November 2020 at 11:54:01 UTC, Dibyendu Majumdar wrote:
> I had assumed that new operator is not available in betterC.
>
> But in following code, it seems the only way to initialize the object correctly ... is there another way I am missing? How should one cleanup objects?
>
> import core.stdc.stdio : printf;
>
> extern (C++) abstract class A {
>     void sayHello();
> }
>
> extern (C++) class B : A {
>     override void sayHello() {
>         printf("hello\n");
>     }
> }
>
> extern (C) void main() {
>     scope b = new B;
>     b.sayHello();
> }

Mir's RC classes doesn't use TypeInfo and DRuntime,

http://mir-algorithm.libmir.org/mir_rc_ptr.html

can be used from C++

https://github.com/libmir/mir-algorithm/blob/master/include/mir/rcptr.h

and C#

https://github.com/libmir/mir.net



November 24, 2020
On Sunday, 22 November 2020 at 16:58:03 UTC, Basile B. wrote:
> [..]
>
> But that's a good start to get things done more properly

Awesome work! I knew it must be possible, but I never tried implementing that.

Small bug fix:

> void** entries = cast(void**) malloc(members.length + 1);

Here you're allocating space for (members.length + 1) bytes, while I suppose you actually want to allocate this number of vtbl slots (each being void*.sizeof bytes):

> auto entries = cast(const(void*)*) malloc((members.length + 1) * (void*).sizeof);

I also added `const` for good measure, as the code that the function pointers point to is stored in read-only memory (AFAIK).
November 24, 2020
On Tuesday, 24 November 2020 at 05:40:21 UTC, 9il wrote:
> On Sunday, 22 November 2020 at 11:54:01 UTC, Dibyendu Majumdar wrote:
>> I had assumed that new operator is not available in betterC.
>>
>> But in following code, it seems the only way to initialize the object correctly ... is there another way I am missing? How should one cleanup objects?
>>
>> import core.stdc.stdio : printf;
>>
>> extern (C++) abstract class A {
>>     void sayHello();
>> }
>>
>> extern (C++) class B : A {
>>     override void sayHello() {
>>         printf("hello\n");
>>     }
>> }
>>
>> extern (C) void main() {
>>     scope b = new B;
>>     b.sayHello();
>> }
>
> Mir's RC classes doesn't use TypeInfo and DRuntime,
>
> http://mir-algorithm.libmir.org/mir_rc_ptr.html
>
> can be used from C++
>
> https://github.com/libmir/mir-algorithm/blob/master/include/mir/rcptr.h
>
> and C#
>
> https://github.com/libmir/mir.net

Nice! However, how do you initialize the vtbl of the extern (C++) class? I briefly had a look and mir.rc.ptr.createRC [1] calls mir.conv.emplace [2], which is a public import of core.lifetime.emplace, which as far as I can see uses typeinfo for that [3]. You also have a custom typeinfo implementation in mir.typeinfo [4], but I didn't see anything regarding initializing the vtbl pointer in there, just about the pointer to the destructor.

Anyway, impressive work on making the interoperability between D, C++ and .NET more seamless! As a former C# developer, mir.net looks much simpler to use then other marshaling techniques for e.g. C/C++. What is the relation between mir.net and autowrap's support for .NET [5]?

[1]: https://github.com/libmir/mir-algorithm/blob/2fa78ddb64d5343ae97a8d5acb0012a37d2ae558/source/mir/rc/ptr.d#L263

[2]: https://github.com/libmir/mir-core/blob/8568bf2fceb361ef7804161b1efc5a96e588c24c/source/mir/conv.d#L9

[3]: https://github.com/dlang/druntime/blob/b948c26b8d86be8f058bf538b75df4d422e2a98f/src/core/lifetime.d#L109

[4]: https://github.com/libmir/mir-algorithm/blob/2fa78ddb64d5343ae97a8d5acb0012a37d2ae558/source/mir/type_info.d#L17

[5]: https://github.com/symmetryinvestments/autowrap#generating-net-interfaces
November 24, 2020
On Tuesday, 24 November 2020 at 11:22:30 UTC, Petar Kirov [ZombineDev] wrote:
> On Tuesday, 24 November 2020 at 05:40:21 UTC, 9il wrote:
>> On Sunday, 22 November 2020 at 11:54:01 UTC, Dibyendu Majumdar wrote:
>>> I had assumed that new operator is not available in betterC.
>>>
>>> But in following code, it seems the only way to initialize the object correctly ... is there another way I am missing? How should one cleanup objects?
>>>
>>> import core.stdc.stdio : printf;
>>>
>>> extern (C++) abstract class A {
>>>     void sayHello();
>>> }
>>>
>>> extern (C++) class B : A {
>>>     override void sayHello() {
>>>         printf("hello\n");
>>>     }
>>> }
>>>
>>> extern (C) void main() {
>>>     scope b = new B;
>>>     b.sayHello();
>>> }
>>
>> Mir's RC classes doesn't use TypeInfo and DRuntime,
>>
>> http://mir-algorithm.libmir.org/mir_rc_ptr.html
>>
>> can be used from C++
>>
>> https://github.com/libmir/mir-algorithm/blob/master/include/mir/rcptr.h
>>
>> and C#
>>
>> https://github.com/libmir/mir.net
>
> Nice! However, how do you initialize the vtbl of the extern (C++) class? I briefly had a look and mir.rc.ptr.createRC [1] calls mir.conv.emplace [2], which is a public import of core.lifetime.emplace, which as far as I can see uses typeinfo for that [3]. You also have a custom typeinfo implementation in mir.typeinfo [4], but I didn't see anything regarding initializing the vtbl pointer in there, just about the pointer to the destructor.

`emplace` ref uses type info in compile time. This may not work with betterC flag, I don't know. But it is a compile-time constant, so TypeInfo isn't required to be generated into executable.

C++ generates its own vtables. Order of methods should mutch in D and C++, the implementation of a method may be either on the D side or the C++ side.

Sometimes you may need to force the C++ compiler to generate vtable using at least one abstract method.

vtable for extern(C++) classes is independent of TypeInfo class. But I am not sure how the D compiler generates them.

BTW, to make this work, it should be really an extern(C++) interface or abstract classes. So both C++ and D compilers generate vtables. And D has some mangling bugs... but finally we have ported a really huge private C++/C#/protobuffer codebase to D/C# codebase.

> Anyway, impressive work on making the interoperability between D, C++ and .NET more seamless! As a former C# developer, mir.net looks much simpler to use than another marshaling techniques for e.g. C/C++. What is the relation between mir.net and autowrap's support for .NET [5]?

They are independent projects.
« First   ‹ Prev
1 2