digitalmars.D.learn - Unexpected behavior when using both alias this and object pointer
- xiren7 (41/41) Jan 12 2017 The problem and the code:
- =?UTF-8?Q?Ali_=c3=87ehreli?= (48/48) Jan 12 2017 Hiding a Foo right after Impl can be a solution. However, you need to
- xiren7 (59/59) Jan 12 2017 Thanks. Ali.
The problem and the code: import std.stdio: writeln; import core.stdc.stdlib: malloc; struct Impl { ubyte[8] payload; } class Foo { Impl *impl; alias impl this; this() { impl = cast(Impl*) malloc(Impl.sizeof); } } class Foo2 { ubyte[8] payload; } void main() { // alias T = Foo2; alias T = Foo; auto t = new T(); // what I want to do is: // 1. cast t as pointer // 2. passe the pointer to a extern(C) callback function // 3. cast the pointer back to t in extern(C) callback function // 4. accesse the payload field auto payload = (cast(T) cast(void*) t).payload; // -> crashs // the right way to get the address of the t object writeln(*cast(void**) &t); // -> 278E3373000 // the unexpected behavior // the obvious(but wrong) way to get the address of the t object writeln(cast(void*) t); // -> 278E164DAB0 // because of alias this // cast(void*) t == cast(void*) t.payload writeln(cast(void*) t.payload); // -> 278E164DAB0 } My question is should let the compiler generate a warning about this unexpected(maybe) behavior?
Jan 12 2017
Hiding a Foo right after Impl can be a solution. However, you need to pass 't', not '&t' to the C function because - Although it may be unexpected, cast(void*) is the specified way of getting the address of a class object - Taking the address of a class reference (which 't' is one), is just the address of the reference itself So, you either have to do something similar to the following or pass void* to the C function. import std.stdio: writeln; import core.stdc.stdlib: malloc; struct Impl { ubyte[8] payload; } struct ImplWithOwner { Impl impl; Foo owner; } class Foo { ImplWithOwner *io; Impl *payload() { // Guaranteed by D for structs: assert(cast(void*)io == cast(void*)&io.impl); return &io.impl; } alias payload this; this() { io = cast(ImplWithOwner*) malloc(Impl.sizeof); io.owner = this; } } static Foo asFoo(Impl* p) { return (cast(ImplWithOwner*)p).owner; } extern(C) void actual_C_function(Impl* data, void function(Impl*) callback) { data.payload[0] = 42; callback(data); } extern(C) void myCallback(Impl* p) { auto foo = p.asFoo; assert(foo.io.impl.payload[0] == 42); } void main() { auto t = new Foo(); actual_C_function(t, &myCallback); } Ali
Jan 12 2017
Thanks. Ali. My previous post is not clear that I have to store class reference(object pointer) in void*. My actual code is try to use libuv in D. // genarated from uv.h, only two fields is used: 'data', 'type'. // the document of 'data': "Space for user-defined arbitrary data. libuv does not use this field". // uv_timer_t is the subclass of uv_handle_t in C. (uv_timer_t has all the fields defined in uv_handle_t) struct uv_handle_t {align(8): union { struct {void* data; void* _; uv_handle_type type;} ubyte[96] _payload; } } struct uv_timer_t {align(8): union { struct {void* data; void* _; uv_handle_type type;} ubyte[160] _payload; } } ... // try to resemble the libuv object defined in C. struct UVHandle { uv_handle_t _uvHandle; // subclass uv_handle_t alias _uvHandle this; void close() { // uv_close(&this) } bool isClosing() { // uv_is_closing(&this) } ... } // define Timer as 'final class', in order to force Timer allocated in gc. final class Timer { UVHandle* uvHandle // subclass UVHandle alias uvHandle this; ... this() { // malloc memory(nogc) for uv_timer_t. uvHandle = malloc(uv_timer_t.sizeof) // store the timer object reference in 'data', // but 'alias uvHandle this' also overrides 'cast', // this is the problem described as my previous post. uvHandle.data = cast(void*) this; } ~this() { free(uvHandle); } void start(...) { // uv_timer_start(this.uvHandle, ...) } void stop() { // uv_timer_stop(this.uvHandle) } ... } My current solution is using 'mixin template' instead of 'alias this' as the way to subclass: mixin template UVHandle() { ... } public final class Timer { mixin UVHandle; ... }
Jan 12 2017