digitalmars.D.learn - extern(C) and name mangling
- Dave P. (9/9) Dec 15 2020 I can’t find this in the spec, but from experimentation it seems
- Mike Parker (24/35) Dec 15 2020 Mangling does not play any role in passing and calling function
- Dave P. (11/39) Dec 15 2020 Oh interesting, so I only need extern(C) for declaring symbols
- Mike Parker (16/22) Dec 15 2020 Not so fast! :-)
- Jacob Carlborg (28/31) Dec 15 2020 That's what's specified, but that's not how DMD actually behaves.
- Dave P. (4/12) Dec 16 2020 Is this a bug in the spec or in the implementation? How do we get
- Jacob Carlborg (6/8) Dec 16 2020 Yeah, that's a good question. That's always problematic with D. The spec...
- Jacob Carlborg (10/13) Dec 15 2020 Also keep in mind that D supports other types than C does, like D
- Jacob Carlborg (9/16) Dec 15 2020 I actually had a use case for this. An initialization function
- =?UTF-8?Q?Ali_=c3=87ehreli?= (20/24) Dec 15 2020 As Mike Parker said, it works:
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
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
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: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.[...]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.Yeah, I was playing a bit loose with the terminology and referring to the leading underscore.[...]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.Agreed!
Dec 15 2020
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
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
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:Is this a bug in the spec or in the implementation? How do we get this fixed?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:
Dec 16 2020
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
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
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
On 12/15/20 2:04 PM, Dave P. wrote:I want to pass some templated functions as function pointers to some C codeAs 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