digitalmars.D.learn - Problems with function as parameter
- Josh (35/35) Sep 21 2017 I'm trying to write a callback function for SDL_mixer through
- Matt Jones (8/13) Sep 21 2017 Make it a C function, not a D function:
- Mike Parker (3/17) Sep 21 2017 It should be extern(C) and, ideally, nothrow. The binding should
- Mike Parker (6/10) Sep 21 2017 To expand on this, D allows functions to be called without
- Josh (3/26) Sep 21 2017 Why should the binding force nothrow? I don't understand why you
- Mike Parker (7/10) Sep 21 2017 Yes, it's because D exceptions are not guaranteed to propagate
- Mike Parker (8/11) Sep 21 2017 Yes, it's because D exceptions are not guaranteed to propagate
- Josh (14/28) Sep 21 2017 Thanks for the help, but when I try that, I get:
- Mike Parker (11/23) Sep 21 2017 The error message indicates that `channelDone` is a member of a
I'm trying to write a callback function for SDL_mixer through Derelict, but this is the first time I've tried to use a function as a parameter, and so I think I'm just making a minor mistake somewhere. The C SDL docs say: **************************************************** // make a channelDone function void channelDone(int channel) { printf("channel %d finished playing.\n", channel); } ... // set the callback for when a channel stops playing Mix_ChannelFinished(channelDone); **************************************************** And my D code is: **************************************************** void unmuteAfterPlaySound() { Mix_ChannelFinished(channelDone); } void channelDone(int channel) { unmuteMusic(); } **************************************************** But DMD seems to be trying to run channelDone() i.e. with no parameters and parentheses omitted, and is giving this error: Error: function Mixer.channelDone (int channel) is not callable using argument types () Error: function pointer Mix_ChannelFinished (extern (C) void function(int channel)) is not callable using argument types (_error_) Is anyone able to see what I've done wrong? Any help would be appreciated, thanks.
Sep 21 2017
On Thursday, 21 September 2017 at 20:21:58 UTC, Josh wrote:I'm trying to write a callback function for SDL_mixer through Derelict, but this is the first time I've tried to use a function as a parameter, and so I think I'm just making a minor mistake somewhere. [...]Make it a C function, not a D function: extern (C) void channelDone(int channel) { unmuteMusic(); } and use & to reference the function instead of calling it: Mix_ChannelFinished(&channelDone);
Sep 21 2017
On Thursday, 21 September 2017 at 22:05:22 UTC, Matt Jones wrote:On Thursday, 21 September 2017 at 20:21:58 UTC, Josh wrote:It should be extern(C) and, ideally, nothrow. The binding should be enforcing nothrow, but currently doesn't. I'll change that now.I'm trying to write a callback function for SDL_mixer through Derelict, but this is the first time I've tried to use a function as a parameter, and so I think I'm just making a minor mistake somewhere. [...]Make it a C function, not a D function: extern (C) void channelDone(int channel) { unmuteMusic(); } and use & to reference the function instead of calling it: Mix_ChannelFinished(&channelDone);
Sep 21 2017
On Friday, 22 September 2017 at 02:18:34 UTC, Mike Parker wrote:To expand on this, D allows functions to be called without parentheses. `channelDone` is actually a function call, whereas in C, it's treated as a function pointer. In D, you have to take the address of a function just as you would any other type, hence `&channelDone`.and use & to reference the function instead of calling it: Mix_ChannelFinished(&channelDone);
Sep 21 2017
On Friday, 22 September 2017 at 02:18:34 UTC, Mike Parker wrote:On Thursday, 21 September 2017 at 22:05:22 UTC, Matt Jones wrote:Why should the binding force nothrow? I don't understand why you HAVE to not throw exceptions. Is it because of the C -> D aspect?On Thursday, 21 September 2017 at 20:21:58 UTC, Josh wrote:It should be extern(C) and, ideally, nothrow. The binding should be enforcing nothrow, but currently doesn't. I'll change that now.I'm trying to write a callback function for SDL_mixer through Derelict, but this is the first time I've tried to use a function as a parameter, and so I think I'm just making a minor mistake somewhere. [...]Make it a C function, not a D function: extern (C) void channelDone(int channel) { unmuteMusic(); } and use & to reference the function instead of calling it: Mix_ChannelFinished(&channelDone);
Sep 21 2017
On Friday, 22 September 2017 at 02:32:56 UTC, Josh wrote:Why should the binding force nothrow? I don't understand why you HAVE to not throw exceptions. Is it because of the C -> D aspect?Yes, it's because D exceptions are not guaranteed to propagate through the C callstack. It works on some compiler/platforms, but not others. I wrote an article at Gamedev.net (which apparently no longer picks up the site's stylesheets) which shows one way of dealing with it. I've modified my approach since then (and should write a better article about it), but the basic idea still holds.
Sep 21 2017
On Friday, 22 September 2017 at 02:32:56 UTC, Josh wrote:Why should the binding force nothrow? I don't understand why you HAVE to not throw exceptions. Is it because of the C -> D aspect?Yes, it's because D exceptions are not guaranteed to propagate through the C callstack. It works on some compiler/platforms, but not others. I wrote an article at Gamedev.net (which apparently no longer picks up the site's stylesheets) which shows one way of dealing with it. I've modified my approach since then (and should write a better article about it), but the basic idea still holds. https://www.gamedev.net/articles/programming/general-and-gameplay-programming/d-exceptions-and-c-callbacks-r3323/
Sep 21 2017
On Thursday, 21 September 2017 at 22:05:22 UTC, Matt Jones wrote:On Thursday, 21 September 2017 at 20:21:58 UTC, Josh wrote:Thanks for the help, but when I try that, I get: src\mixer.d(80,22): Error: function pointer Mix_ChannelFinished (extern (C) void function(int channel)) is not callable using argument types (extern (C) void delegate(int channel)) Code: void unmuteAfterPlaySound() { Mix_ChannelFinished(&channelDone); } extern (C) void channelDone(int channel) { unmuteMusic(); }I'm trying to write a callback function for SDL_mixer through Derelict, but this is the first time I've tried to use a function as a parameter, and so I think I'm just making a minor mistake somewhere. [...]Make it a C function, not a D function: extern (C) void channelDone(int channel) { unmuteMusic(); } and use & to reference the function instead of calling it: Mix_ChannelFinished(&channelDone);
Sep 21 2017
On Friday, 22 September 2017 at 02:22:46 UTC, Josh wrote:src\mixer.d(80,22): Error: function pointer Mix_ChannelFinished (extern (C) void function(int channel)) is not callable using argument types (extern (C) void delegate(int channel)) Code: void unmuteAfterPlaySound() { Mix_ChannelFinished(&channelDone); } extern (C) void channelDone(int channel) { unmuteMusic(); }The error message indicates that `channelDone` is a member of a class or a struct. A pointer to a member function is a delegate (or closure), not a function pointer. Free functions, static nested functions, and static member functions all produce function pointer. Non-static member functions and non-static nested functions all produce delegates/closures. See the docs: https://dlang.org/spec/function.html#closures If you need to manipulate instance members from a C callback, you'll need a way to implement a mechanism to work out which instance you need.
Sep 21 2017
On Friday, 22 September 2017 at 03:26:36 UTC, Mike Parker wrote:On Friday, 22 September 2017 at 02:22:46 UTC, Josh wrote:Perfect, that's the info I needed. As these functions were in a class, setting channelDone and unmuteMusic to static worked. As an aside, in that doc it says "The .funcptr property of a delegate will return the function pointer value as a function type". So I also tried Mix_ChannelFinished((&channelDone).funcptr); and this compiled, but caused a segfault when the callback ran. What would have caused this? Is it because it's a C function?src\mixer.d(80,22): Error: function pointer Mix_ChannelFinished (extern (C) void function(int channel)) is not callable using argument types (extern (C) void delegate(int channel)) Code: void unmuteAfterPlaySound() { Mix_ChannelFinished(&channelDone); } extern (C) void channelDone(int channel) { unmuteMusic(); }The error message indicates that `channelDone` is a member of a class or a struct. A pointer to a member function is a delegate (or closure), not a function pointer. Free functions, static nested functions, and static member functions all produce function pointer. Non-static member functions and non-static nested functions all produce delegates/closures. See the docs: https://dlang.org/spec/function.html#closures If you need to manipulate instance members from a C callback, you'll need a way to implement a mechanism to work out which instance you need.
Sep 21 2017
On Friday, 22 September 2017 at 04:32:08 UTC, Josh wrote:As an aside, in that doc it says "The .funcptr property of a delegate will return the function pointer value as a function type". So I also tried Mix_ChannelFinished((&channelDone).funcptr); and this compiled, but caused a segfault when the callback ran. What would have caused this? Is it because it's a C function?No it's because in this function are used variables that are specific to the class instance (the this). If there weren't this would work, even if it's not a good idea to do that: struct Foo { int a; void needThisYeahReally(){a = 0;} void needThisButWorkWithout(){} } void main() { Foo foo; { auto dg = &foo.needThisButWorkWithout; dg.funcptr(); } { auto dg = &foo.needThisYeahReally; // dg.funcptr(); // segfault because of access to this.a } } Using a pointer to a static member function was the right thing to do.
Sep 22 2017