www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Calling C functions

reply Denis <noreply noserver.lan> writes:
I have a two questions about calling C functions from D.

(1) When passing a D callback to a C function, is there a way to 
write the code without having to prefix the callback declaration 
with "extern(C)"?

It's not a big deal adding the prefix to the D function 
declaration. It just seems odd to me to prefix D code with 
"extern(C)". For example, the following code works:

   extern(C) void cfunc(void function(int));
   extern(C) void dcallback(int x) {...}		<-- Why extern(C)?
   cfunc(&dcallback);

Can this be rewritten, dropping the prefix from the second line? 
If not, it would be helpful to know why "extern(C)" is needed 
here too.

(2) Is there a way to restrict the invocation of a linked C 
function to one specific D function?

If the C header is defined in one of the core.stdc libraries, the 
import statement can either be global or inside a specific D 
function -- both work. In contrast, when the C function prototype 
is written directly into the D program (as above), the linker 
complains unless this declaration is made global. If it's 
possible to restrict the scope of the C function to just one D 
function, I'll take advantage.

(I'm using dmd, if that makes a difference.)

Thanks
Jun 25 2020
parent reply Jacob Carlborg <doob me.com> writes:
On Friday, 26 June 2020 at 00:30:22 UTC, Denis wrote:
 I have a two questions about calling C functions from D.

 (1) When passing a D callback to a C function, is there a way 
 to write the code without having to prefix the callback 
 declaration with "extern(C)"?

 It's not a big deal adding the prefix to the D function 
 declaration. It just seems odd to me to prefix D code with 
 "extern(C)". For example, the following code works:

   extern(C) void cfunc(void function(int));
   extern(C) void dcallback(int x) {...}		<-- Why extern(C)?
   cfunc(&dcallback);

 Can this be rewritten, dropping the prefix from the second 
 line? If not, it would be helpful to know why "extern(C)" is 
 needed here too.
No, it cannot be dropped. `extern(C)` is required because C and D are using different calling conventions (D functions are also mangled). For example, D (at least DMD and LDC) are passing the arguments to the function in reverse.
 (2) Is there a way to restrict the invocation of a linked C 
 function to one specific D function?

 If the C header is defined in one of the core.stdc libraries, 
 the import statement can either be global or inside a specific 
 D function -- both work. In contrast, when the C function 
 prototype is written directly into the D program (as above), 
 the linker complains unless this declaration is made global. If 
 it's possible to restrict the scope of the C function to just 
 one D function, I'll take advantage.
For functions nested in a D language construct (class, struct, function) the compiler will always use the D mangling, instead of the C mangling. In theory it would be possible to workaround that by forcing the mangled name using `pragma(mangle)`, but for some reason the compiler doesn't allow `pragma(mangle)` inside a function body, on a nested function declaration. You can wrap up everything in a struct, as follows: struct printf { pragma(mangle, "printf") extern (C) private static int printf(in char*, ...); static int opCall(Args...)(Args args) { return printf(args); } } void main() { printf("asd\n".ptr); } The `printf` function can be called from anywhere within the module, but not outside the module. -- /Jacob Carlborg
Jun 26 2020
next sibling parent reply Denis <noreply noserver.lan> writes:
On Friday, 26 June 2020 at 08:15:27 UTC, Jacob Carlborg wrote:
 On Friday, 26 June 2020 at 00:30:22 UTC, Denis wrote:
   extern(C) void cfunc(void function(int));
   extern(C) void dcallback(int x) {...}		<-- Why extern(C)?
   cfunc(&dcallback);

 Can this be rewritten, dropping the prefix from the second 
 line? If not, it would be helpful to know why "extern(C)" is 
 needed here too.
No, it cannot be dropped. `extern(C)` is required because C and D are using different calling conventions (D functions are also mangled). For example, D (at least DMD and LDC) are passing the arguments to the function in reverse.
OK, now this makes sense. I tested calling the same callback function directly from D: it compiled and worked correctly. So at least prefixing the callback function with `extern(C)` doesn't prevent the rest of the D program from calling it too.
 (2) Is there a way to restrict the invocation of a linked C 
 function to one specific D function?
[...]
 For functions nested in a D language construct (class, struct, 
 function) the compiler will always use the D mangling, instead 
 of the C mangling. In theory it would be possible to workaround 
 that by forcing the mangled name using `pragma(mangle)`, but 
 for some reason the compiler doesn't allow `pragma(mangle)` 
 inside a function body, on a nested function declaration.

 You can wrap up everything in a struct, as follows:
I see. Thank you very much for these explanations and code -- the insights are very helpful. Denis
Jun 26 2020
parent Jacob Carlborg <doob me.com> writes:
On 2020-06-26 18:54, Denis wrote:

 OK, now this makes sense.
 
 I tested calling the same callback function directly from D: it compiled 
 and worked correctly. So at least prefixing the callback function with 
 `extern(C)` doesn't prevent the rest of the D program from calling it too.
No, of course not. How would you otherwise call your `cfunc` function from your original example ;) -- /Jacob Carlborg
Jun 26 2020
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 6/26/20 4:15 AM, Jacob Carlborg wrote:
 On Friday, 26 June 2020 at 00:30:22 UTC, Denis wrote:
 I have a two questions about calling C functions from D.

 (1) When passing a D callback to a C function, is there a way to write 
 the code without having to prefix the callback declaration with 
 "extern(C)"?

 It's not a big deal adding the prefix to the D function declaration. 
 It just seems odd to me to prefix D code with "extern(C)". For 
 example, the following code works:

   extern(C) void cfunc(void function(int));
   extern(C) void dcallback(int x) {...}        <-- Why extern(C)?
   cfunc(&dcallback);

 Can this be rewritten, dropping the prefix from the second line? If 
 not, it would be helpful to know why "extern(C)" is needed here too.
No, it cannot be dropped. `extern(C)` is required because C and D are using different calling conventions (D functions are also mangled). For example, D (at least DMD and LDC) are passing the arguments to the function in reverse.
Are you sure? On the ABI page [1] , it says "The extern (C) and extern (D) calling convention matches the C calling convention used by the supported C compiler on the host system." I'm pretty sure you can use function pointers to D functions for C callbacks, and it should work. -Steve [1] https://dlang.org/spec/abi.html#function_calling_conventions
Jun 29 2020
parent reply Jacob Carlborg <doob me.com> writes:
On Monday, 29 June 2020 at 16:34:33 UTC, Steven Schveighoffer 
wrote:

 Are you sure? On the ABI page [1] , it says "The extern (C) and 
 extern (D) calling convention matches the C calling convention 
 used by the supported C compiler on the host system."
In that case the documentation is wrong. Here's an example showing the differences: $ cat foo.c #include <stdio.h> void foo(int a, int b) { printf("a=%d b=%d\n", a, b); } $ clang -c foo.c $ cat main.d pragma(mangle, "foo") extern (D) void foo_extern_d(int, int); pragma(mangle, "foo") extern (C) void foo_extern_c(int, int); void main() { foo_extern_d(1, 2); foo_extern_c(1, 2); } $ dmd main.d foo.o $ ./main a=2 b=1 a=1 b=2 This is on macOS. -- /Jacob Carlborg
Jun 29 2020
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 6/29/20 1:50 PM, Jacob Carlborg wrote:
 On Monday, 29 June 2020 at 16:34:33 UTC, Steven Schveighoffer wrote:
 
 Are you sure? On the ABI page [1] , it says "The extern (C) and extern 
 (D) calling convention matches the C calling convention used by the 
 supported C compiler on the host system."
In that case the documentation is wrong. Here's an example showing the differences:
Yep, for sure. I'll file an issue. Anyone know why the calling convention would differ? -Steve
Jun 29 2020
parent reply Kagamin <spam here.lot> writes:
On Monday, 29 June 2020 at 19:55:59 UTC, Steven Schveighoffer 
wrote:
 Yep, for sure. I'll file an issue. Anyone know why the calling 
 convention would differ?
It's easier to enforce left to right evaluation order this way: arguments are pushed to stack as they are evaluated, which is pascal calling convention.
Jun 30 2020
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 6/30/20 3:00 AM, Kagamin wrote:
 On Monday, 29 June 2020 at 19:55:59 UTC, Steven Schveighoffer wrote:
 Yep, for sure. I'll file an issue. Anyone know why the calling 
 convention would differ?
It's easier to enforce left to right evaluation order this way: arguments are pushed to stack as they are evaluated, which is pascal calling convention.
Easier, or more efficient? The cost seems high if it doesn't provide any efficiency benefits (i.e. one cannot use extern(D) functions for C callbacks). In any case, I filed an issue: https://issues.dlang.org/show_bug.cgi?id=20993 -Steve
Jun 30 2020
parent Jacob Carlborg <doob me.com> writes:
On Tuesday, 30 June 2020 at 12:22:15 UTC, Steven Schveighoffer 
wrote:

 (i.e. one cannot use extern(D) functions for C callbacks).
I don't think that's a big issue. Honestly, I don't think it's an issue at all. BTW, the order of arguments is not the only thing. Variadic functions in D and C are completely different. I don't think it's possible to implement a C style variadic function with D linkage (the language doesn't provide a syntax for it). There's also D specific types which C cannot handle (like arrays and delegates). I'm sure there are other differences in the ABIs. -- /Jacob Carlborg
Jun 30 2020