digitalmars.D.learn - Can't assign extern(C) function pointer to D variable?
- XavierAP (14/14) Nov 22 2022 I was surprised when it didn't compile, though I immediately
I was surprised when it didn't compile, though I immediately found it understandable... Already read through https://dlang.org/spec/interfaceToC.html and https://wiki.dlang.org/Bind_D_to_C Is it really the case (that an extern(C) function pointer cannot be assigned to a D variable)? Or is it a matter of annotating with the right attributes? If so, how? Otherwise I'm interested in the best or most concise workaround. Is there a better one? I came up with a template solution: https://github.com/XavierAP/game-king/blob/master/source/scope_cleanup.d The problem I had was that this ScopeCleanup struct could not be constructed passing a pointer to a function imported from C (from the SDL library; you can browse around the same repo to see its usage; it's just a toy project that's barely started).
Nov 22 2022
On Tuesday, 22 November 2022 at 21:11:37 UTC, XavierAP wrote:I was surprised when it didn't compile, though I immediately found it understandable... Already read through https://dlang.org/spec/interfaceToC.html and https://wiki.dlang.org/Bind_D_to_C [...]You need to create an alias containing your callback type. ```d alias DCallback = extern(C) void function(); DCallback cb; cb = yourCFunction; ```
Nov 22 2022
On Tuesday, 22 November 2022 at 21:32:43 UTC, Hipreme wrote:You need to create an alias containing your callback type.Thanks both!! I have all the pieces of the puzzle. I'm actually staying with the wrapping template solution. (Because the strongly typed one turns out too convoluted, and because it allows the remaining ScopeCleanup struct to be more general purpose, for non-C functions, and for functions that don't return void but an error code which I want to discard.) The first problem was indeed that a C function pointer "is not" a D one. So annotating the variable with extern(C) can indeed solve it. I had actually tried this, but it was not compiling for another reason. The next reason (as you see in the GitHub link) is that the variable in question is a (constructor) parameter. D can't seem to compile extern(C) inlined somewhere else. Indeed aliasing takes care of this second problem: alias CFunction = extern(C) void function(); /// RAII object that does nothing but calling, when destructed, the function passed at construction. struct ScopeCleanup { disable this(); this(CFunction cleanup) { this.cleanup = cleanup; } ~this() { cleanup(); } CFunction cleanup; } Now this module compiles. BUT the code trying to call this constructor doesn't compile, when called with C function such as SDL_Quit imported from the SDL lib, or IMG_Quit imported from the SDL_image lib. From the compiler error I learn that the imported function is not only extern(C) but also nothrow nogc. Fair enough, I add it to the alias. BUT still no good, because (as I learn from the same compiler error) this binding imports these functions as extern(C) void function() nothrow nogc* with this final "*" this turns out, from the D point of view, a "pointer to a function pointer" XD so it has to be called/de-referenced in this way (in destructor): alias CFunctionPtr = extern(C) void function() nothrow nogc*; /// RAII object that does nothing but calling, when destructed, the function passed at construction. struct ScopeCleanup { disable this(); this(CFunctionPtr cleanup) { this.cleanup = cleanup; } ~this() { (*cleanup)(); } CFunctionPtr cleanup; } Thanks guys for the learning, I'm staying with the template solution (thanks D), but let me know if you have more insights.
Nov 22 2022
On 22.11.22 22:11, XavierAP wrote:I was surprised when it didn't compile, though I immediately found it understandable... Already read through https://dlang.org/spec/interfaceToC.html and https://wiki.dlang.org/Bind_D_to_C Is it really the case (that an extern(C) function pointer cannot be assigned to a D variable)? Or is it a matter of annotating with the right attributes? If so, how?Works for me: import core.stdc.stdio: puts; auto p1 = &puts; extern (C) int function(const char* s) p2 = &puts; If you're trying to assign an `extern (C)` function pointer to an `extern (D)` one (the default), that cannot work. The compiler would emit code using D's calling convention, but the called function would assume C's calling convention.
Nov 22 2022