digitalmars.D.learn - C style callbacks fix for member callbacks
- IntegratedDimensions (54/54) May 19 2018 I have a member callback that I want to use as a C callback.
- MGW (4/5) May 20 2018 http://www.agner.org/optimize/calling_conventions.pdf
- IntegratedDimensions (15/21) May 20 2018 Sorry, I can't understand Russian(wish I could!). It also does
- ag0aep6g (41/54) May 20 2018 Unless I'm misunderstanding it, the spec seems to say that the `this`
- IntegratedDimensions (5/61) May 20 2018 I tried this. Your code crashes in windows dmd x86 x64.
- ag0aep6g (3/4) May 20 2018 Hm. Works for me in a virtual machine. But I'm not surprised that it's
- IntegratedDimensions (6/10) May 21 2018 https://run.dlang.io/is/CMNnJY
- IntegratedDimensions (10/10) May 25 2018 Investigating further, this does not seem to be pushed on the
I have a member callback that I want to use as a C callback. This is impossible due to the `hidden this` passed as the "first" parameter. The callback already makes it's last value a user pointer which I use as a "this". If I make my method static extern(C) then there is no crash and everything works. The problem is that it is now a static function within the class which I want to avoid. Because there is actually a "this pointer" I can make it a member function and everything works as long as the calling convention is right. I was initially wrapping the callback in a delegate that made it all work out but I want to avoid that level of indirection since it should not be necessary. I have tried manually reversing the arguments, using various calling conventions, etc but everything crashes except when I use extern(C) static without modifying the order. extern(C) static foo(a,b,c,d) puts the parameters on the stack as d,c,b,a foo(a,b,c,d) is d,c,b,a,this (? The ABI does not make it clear what order is pushed on the stack. It uses the terminology "passes" and I assume that the calling convention is C'ish although extern(C) matters). works extern(C) static foo(a,b,c,d) does not work static foo(d,c,b,a) So something else is going on "The last parameter is passed in EAX rather than being pushed on the stack if the following conditions are met: It fits in EAX. It is not a 3 byte struct. It is not a floating point type. " Can someone clarify the exact calling convention process for C vs D along with any potential solutions to the above problem(using static is not a solution). Just to make sure we are on the same page: class { void extern(C) static foo(a,b,c,mythis); } works while class { void extern(C) foo(a,b,c); } fails. class { void foo(c,b,a); } also fails.
May 19 2018
On Saturday, 19 May 2018 at 23:52:58 UTC, IntegratedDimensions wrote:I have a member callback that I want to use as a C callback.http://www.agner.org/optimize/calling_conventions.pdf https://www.youtube.com/watch?v=xhDS377mAc4
May 20 2018
On Sunday, 20 May 2018 at 08:40:57 UTC, MGW wrote:On Saturday, 19 May 2018 at 23:52:58 UTC, IntegratedDimensions wrote:Sorry, I can't understand Russian(wish I could!). It also does not seem applicable for my problem. Although It is a useful idea here(using D in C++). alias callback = extern(C) int function(const(void) a, void *b, uint c, void* context); Where context acts as this. I would like to assign a D method to this callback. class { callback c; /*extern(C) static*/ int foo(const(void) a, void *b, uint c, void* context); this() { c = cast(callback)&foo; } }I have a member callback that I want to use as a C callback.http://www.agner.org/optimize/calling_conventions.pdf https://www.youtube.com/watch?v=xhDS377mAc4
May 20 2018
On 05/20/2018 06:48 PM, IntegratedDimensions wrote:alias callback = extern(C) int function(const(void) a, void *b, uint c, void* context);(I'm assuming that `a` is supposed to be a `const(void)*`.)Where context acts as this. I would like to assign a D method to this callback. class { callback c; /*extern(C) static*/ int foo(const(void) a, void *b, uint c, void* context); this() { c = cast(callback)&foo; } }Unless I'm misunderstanding it, the spec seems to say that the `this` pointer is passed as if it was an additional parameter past the last one [1]. But that doesn't seem to be true in the implementation. At least on Linux x86-64, `this` seems to be a hidden first parameter. So when a method is called as a `callback`, `a` becomes `this`, `b` becomes the first explicit parameter, `c` the second, and `context` the third. So this works: ---- import std.stdio; alias Callback = extern(C) int function(const(void)* a, void* b, uint c, void* context); class C { int field = 43; extern(C) int foo(void* b, uint c, C this_) { const(void)* a = cast(void*) this; writeln(a, " ", b, " ", c, " ", this_.field); return 0; } } void main() { void* a = new int; void* b = new int; uint c = 42; auto obj = new C; Callback cb = cast(Callback) (&obj.foo).funcptr; cb(a, b, c, cast(void*) obj); writeln(a, " ", b, " ", c, " ", obj.field); /* For comparison. Should print the same. */ } ---- This is all very hacky, of course. And I don't really know what I'm doing there. So obviously, I don't recommend doing this. But other than hacking it like that, I don't think you can pass a method as a `callback` directly. [1] https://dlang.org/spec/abi.html#parameters
May 20 2018
On Sunday, 20 May 2018 at 23:05:47 UTC, ag0aep6g wrote:On 05/20/2018 06:48 PM, IntegratedDimensions wrote:I tried this. Your code crashes in windows dmd x86 x64. It really shouldn't be hacky. The only difference is the "this" is implicit normally when in this case it is explicit and possibly in a different location than one expects.alias callback = extern(C) int function(const(void) a, void *b, uint c, void* context);(I'm assuming that `a` is supposed to be a `const(void)*`.)Where context acts as this. I would like to assign a D method to this callback. class { callback c; /*extern(C) static*/ int foo(const(void) a, void *b, uint c, void* context); this() { c = cast(callback)&foo; } }Unless I'm misunderstanding it, the spec seems to say that the `this` pointer is passed as if it was an additional parameter past the last one [1]. But that doesn't seem to be true in the implementation. At least on Linux x86-64, `this` seems to be a hidden first parameter. So when a method is called as a `callback`, `a` becomes `this`, `b` becomes the first explicit parameter, `c` the second, and `context` the third. So this works: ---- import std.stdio; alias Callback = extern(C) int function(const(void)* a, void* b, uint c, void* context); class C { int field = 43; extern(C) int foo(void* b, uint c, C this_) { const(void)* a = cast(void*) this; writeln(a, " ", b, " ", c, " ", this_.field); return 0; } } void main() { void* a = new int; void* b = new int; uint c = 42; auto obj = new C; Callback cb = cast(Callback) (&obj.foo).funcptr; cb(a, b, c, cast(void*) obj); writeln(a, " ", b, " ", c, " ", obj.field); /* For comparison. Should print the same. */ } ---- This is all very hacky, of course. And I don't really know what I'm doing there. So obviously, I don't recommend doing this. But other than hacking it like that, I don't think you can pass a method as a `callback` directly. [1] https://dlang.org/spec/abi.html#parameters
May 20 2018
I tried this. Your code crashes in windows dmd x86 x64.Hm. Works for me in a virtual machine. But I'm not surprised that it's fragile. It might be completely wrong, and it just happens to look alright on my machine.
May 20 2018
On Monday, 21 May 2018 at 02:23:27 UTC, ag0aep6g wrote:https://run.dlang.io/is/CMNnJY Shows the static version produces the same code as the non-static. The code was probably compiled on linux. I can't tell though on my machine what is going on since I cannot disassemble properly and what I do see is far more complex.I tried this. Your code crashes in windows dmd x86 x64.Hm. Works for me in a virtual machine. But I'm not surprised that it's fragile. It might be completely wrong, and it just happens to look alright on my machine.
May 21 2018
Investigating further, this does not seem to be pushed on the stack but set in EAX. Hence no amount of parameter placement manipulation will work. It actually becomes an easy situation although this will be invalid as it will be be whatever value is in EAX used by the caller. One cannot set this directly though but one does not have to use it. Therefor, simply using a member function here is the same as a static and no changes have to be made. Quite an easy fix. I do not know how safe it is. The docs say this is pushed and that is probably generally true.
May 25 2018