www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - betterC: new operator

reply Dibyendu Majumdar <mobile majumdar.org.uk> writes:
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();
}
Nov 22 2020
next sibling parent reply Dennis <dkorpel gmail.com> writes:
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
Nov 22 2020
parent reply rikki cattermole <rikki cattermole.co.nz> writes:
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
Nov 22 2020
parent Guillaume Piolat <first.name guess.com> writes:
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?
Nov 22 2020
prev sibling next sibling parent reply Basile B. <b2.temp gmx.com> writes:
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: --- 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
Nov 22 2020
next sibling parent Basile B. <b2.temp gmx.com> writes:
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 --- 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; } ---
Nov 22 2020
prev sibling parent Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
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).
Nov 24 2020
prev sibling parent reply 9il <ilyayaroshenko gmail.com> writes:
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 https://github.com/libmir/mir.net
Nov 23 2020
parent reply Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
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 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, 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
Nov 24 2020
parent reply 9il <ilyayaroshenko gmail.com> writes:
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 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
 Anyway, impressive work on making the interoperability between 

 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.
Nov 24 2020
parent 9il <ilyayaroshenko gmail.com> writes:
On Tuesday, 24 November 2020 at 17:02:27 UTC, 9il wrote:
 On Tuesday, 24 November 2020 at 11:22:30 UTC, Petar Kirov 
 [ZombineDev] wrote:>>
 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.
Nevermind. Hmm, yes, you are right, it uses typeinfo to initialize the payload :/
Nov 24 2020