www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - extern(C) and name mangling

reply Dave P. <dave287091 gmail.com> writes:
I can’t find this in the spec, but from experimentation it seems 
like extern(C) only affects name mangling of functions at the top 
level scope. Thus extern(C) function templates would be mangled 
differently, but still use the C calling convention. Is this 
right?  I want to pass some templated functions as function 
pointers to some C code and wanted to confirm that would work 
(and that different versions would be mangled differently).

Also, is there any way to say you want the C calling convention, 
but don’t want C name mangling for top level functions?
Dec 15 2020
next sibling parent reply Mike Parker <aldacron gmail.com> writes:
On Tuesday, 15 December 2020 at 22:04:12 UTC, Dave P. wrote:
 I can’t find this in the spec, but from experimentation it 
 seems like extern(C) only affects name mangling of functions at 
 the top level scope. Thus extern(C) function templates would be 
 mangled differently, but still use the C calling convention. Is 
 this right?  I want to pass some templated functions as 
 function pointers to some C code and wanted to confirm that 
 would work (and that different versions would be mangled 
 differently).
Mangling does not play any role in passing and calling function pointers between D and C. It only plays a role in linking and loading. You can declare function pointers in D and C without any name whatsoever. What matters is that both sides agree on the number and type of parameters accepted by the function that's pointed to, and that they both agree on the calling convention. I don't believe extern(C) has any effect on templated functions. However, the D calling convention is defined to be identical to the C calling convention on the host system for everything except Windows x86. So theoretically, you should be able to pass a pointer to a templated free function to C as long as the types in the functions parameter list match those the C function expects. I don't know if I'd do that myself, though.
 Also, is there any way to say you want the C calling 
 convention, but don’t want C name mangling for top level 
 functions?
There's no such thing as C name mangling. C functions are *not* mangled (though some platforms do prepend an underscore to symbol names). What extern(C) does is to turn off D name mangling. So what you're asking for is a way to retain the D name mangling on an extern C function. The way to do that is with `pragma(mangle, "new_name")`. To match the original D function mangling, declare the function first without extern(C) and print `func.mangleof`. Use that as the parameter to pragma(mangle). I can't imagine any benefit you'd get from doing that, though.
Dec 15 2020
next sibling parent reply Dave P. <dave287091 gmail.com> writes:
On Wednesday, 16 December 2020 at 04:17:13 UTC, Mike Parker wrote:
 On Tuesday, 15 December 2020 at 22:04:12 UTC, Dave P. wrote:
 [...]
Mangling does not play any role in passing and calling function pointers between D and C. It only plays a role in linking and loading. You can declare function pointers in D and C without any name whatsoever. What matters is that both sides agree on the number and type of parameters accepted by the function that's pointed to, and that they both agree on the calling convention. I don't believe extern(C) has any effect on templated functions. However, the D calling convention is defined to be identical to the C calling convention on the host system for everything except Windows x86.
Oh interesting, so I only need extern(C) for declaring symbols I’m linking to and for symbols I want to export to C. I had sort of assumed that D might have different calling conventions for different things, but that makes things a lot easier.
 So theoretically, you should be able to pass a pointer to a 
 templated free function to C as long as the types in the 
 functions parameter list match those the C function expects. I 
 don't know if I'd do that myself, though.

 [...]
There's no such thing as C name mangling. C functions are *not* mangled (though some platforms do prepend an underscore to symbol names). What extern(C) does is to turn off D name mangling.
Yeah, I was playing a bit loose with the terminology and referring to the leading underscore.
 So what you're asking for is a way to retain the D name 
 mangling on an extern C function. The way to do that is with 
 `pragma(mangle, "new_name")`. To match the original D function 
 mangling, declare the function first without extern(C) and 
 print `func.mangleof`. Use that as the parameter to 
 pragma(mangle).

 I can't imagine any benefit you'd get from doing that, though.
Agreed!
Dec 15 2020
parent Mike Parker <aldacron gmail.com> writes:
On Wednesday, 16 December 2020 at 04:45:34 UTC, Dave P. wrote:

 Oh interesting, so I only need extern(C) for declaring symbols 
 I’m linking to and
 for symbols I want to export to C. I had sort of assumed that D 
 might have
 different calling conventions for different things, but that 
 makes things a lot easier.
Not so fast! :-) extern(C) does affect the calling convention on Windows x86. There's also extern(Windows), which changes a function to the stdcall calling convention used by the Win32 API (and OpenGL implementations on Windows, and a handful of other libraries). And there's no guarantee that as D moves to new platforms that there won't be other exceptions joining x86 Windows. That's why I said I'm not sure I'd ever pass a templated function pointer to C. It isn't going to work on 32-bit Windows, or with any stdcall C function, right now, and possibly other platforms in the future. So as a default, you should always be explicit with your extern(x) linkage attributes on functions even when you aren't actually linking with C, and only break that rule when it's necessary.
Dec 15 2020
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On Wednesday, 16 December 2020 at 04:17:13 UTC, Mike Parker wrote:

 However, the D calling convention is defined to be identical to 
 the C calling convention on the host system for everything 
 except Windows x86.
That's what's specified, but that's not how DMD actually behaves. DMD passes the arguments in reverse order. That's easily observable by calling a C function with D linkage: $ cat foo.c #include <stdio.h> void foo(int a, int b) { printf("a=%d b=%d\n", a, b); } $ cat main.d module main; import std; pragma(mangle, "foo") // override D mangling to get the same name as the C function void foo(int a, int b); // D calling convention void main() { foo(1, 2); } $ clang -o foo.o foo.c -c $ dmd main.d foo.o $ ./main a=2 b=1 LDC behaves the same way as DMD and, IIRC, GDC follows how GCC passes the arguments. -- /Jacob Carlborg
Dec 15 2020
parent reply Dave P. <dave287091 gmail.com> writes:
On Wednesday, 16 December 2020 at 06:46:42 UTC, Jacob Carlborg 
wrote:
 On Wednesday, 16 December 2020 at 04:17:13 UTC, Mike Parker 
 wrote:

 However, the D calling convention is defined to be identical 
 to the C calling convention on the host system for everything 
 except Windows x86.
That's what's specified, but that's not how DMD actually behaves. DMD passes the arguments in reverse order. That's easily observable by calling a C function with D linkage:
Is this a bug in the spec or in the implementation? How do we get this fixed?
Dec 16 2020
parent Jacob Carlborg <doob me.com> writes:
On 2020-12-16 16:18, Dave P. wrote:

 Is this a bug in the spec or in the implementation?
Yeah, that's a good question. That's always problematic with D. The spec is incomplete.
 How do we get this fixed?
The simplest would be to change the spec. -- /Jacob Carlborg
Dec 16 2020
prev sibling next sibling parent Jacob Carlborg <doob me.com> writes:
On Wednesday, 16 December 2020 at 04:17:13 UTC, Mike Parker wrote:

 However, the D calling convention is defined to be identical to 
 the C calling convention on the host system for everything 
 except Windows x86.
Also keep in mind that D supports other types than C does, like D arrays and delegates. In those cases the D calling convention needs to be extended since there's no way to just relay on the C calling convention. One could think it would be possible to passes D arrays and delegates as structs, but that's not how DMD passes them. On some platforms it matches how a struct is passed, on some it doesn't. -- /Jacob Carlborg
Dec 15 2020
prev sibling parent Jacob Carlborg <doob me.com> writes:
On Wednesday, 16 December 2020 at 04:17:13 UTC, Mike Parker wrote:

 So what you're asking for is a way to retain the D name 
 mangling on an extern C function. The way to do that is with 
 `pragma(mangle, "new_name")`. To match the original D function 
 mangling, declare the function first without extern(C) and 
 print `func.mangleof`. Use that as the parameter to 
 pragma(mangle).

 I can't imagine any benefit you'd get from doing that, though.
I actually had a use case for this. An initialization function that is called by the C runtime, i.e. `pragma(crt_constructor)`. That function needs to have C calling convention. But to avoid any conflicts with other `extern(C)` functions I wanted to keep the D mangling. https://github.com/dlang/druntime/blob/master/src/core/memory.d#L212-L232 -- /Jacob Carlborg
Dec 15 2020
prev sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 12/15/20 2:04 PM, Dave P. wrote:

 I want to pass
 some templated functions as function pointers to some C code
As Mike Parker said, it works: // The same thing as a C function pointer: alias Func = long function(int); long bar(T)(int) { return 0; } Func f0 = &(bar!float); Func d1 = &(bar!double);
 (and that different versions would be mangled
 differently).
Yes: auto foo(T)() { } void main() { pragma(msg, foo!int.mangleof); pragma(msg, foo!double.mangleof); } Output: _D6deneme__T3fooTiZQhFNaNbNiNfZv _D6deneme__T3fooTdZQhFNaNbNiNfZv Ali
Dec 15 2020