5 ms·
> extern "C" also ensures that the C calling convention is used, which is relevant for callbacks. It's not just name mangling. I stand corrected. I didn't know
by spacechild1 2y ago
> extern "C" also ensures that the C calling convention is used, which is relevant for callbacks. It's not just name mangling.
I stand corrected. I didn't know that `extern "C"` enforces the C calling convention.
However, on modern platforms this doesn't really matter because, as I said, there is only a single calling convention (per platform). And I'm pretty sure that future platforms will keep it that way. Fortunately, if you try to pass a C++ callback of the wrong calling convention, you get a compiler error.
> If you're passing a callback to a C function from C++, it's wrong unless the callback is declared extern "C".
That's certainly not true because `extern "C"` is not the only way to specify the calling convention. In fact, you might need a different calling convention! As I mentioned, on x86 the Windows API uses stdcall for all API functions and callbacks, so `extern "C"` would be wrong. If you look at the Microsoft examples, you will see that they declare the callbacks as WINAPI (without `extern "C"`): https://learn.microsoft.com/en-us/windows/win32/procthread/creating-threads https://learn.microsoft.com/en-us/windows/win32/procthread/c...
So I stand by my point that in practice you don't need `extern "C"` for passing C++ callbacks to C functions. You can pass a lambda function just fine, and when it doesn't work the compiler will tell you.
- LegibleCrimson2 2y agoA couple big caveats here: * cdecl is a platform specific calling convention. There is no standard C ABI. cdecl is a wintel thing, not the standard C calling convention. On Linux, this is the System V ABI for instance. On Windows ARM, it's also not cdecl. * Specifying calling convention at all is a compiler specific extension. There is no standard way of specifying a C calling convention without `extern`. So specifying cdecl gets you the right calling convention on some platforms and ties your code to some specific compilers. The only portable way to specify C linkage in a C++ program is extern "C". You will always get the right ABI for your platform and it will work on every compiler. > So I stand by my point that in practice you don't need `extern "C"` for passing C++ callbacks to C functions. You can pass a lambda function just fine, and when it doesn't work the compiler will tell you. The compiler will very often not tell you. It will complain if the lambda can't be coerced to a function pointer (because it's a closure) or if the argument or return types are wrong. An incorrect ABI will usually be accepted and will just do the wrong thing or crash at runtime. The C++ standard says that language linkage is part of a function's type, but very few compilers actually support this. Your position works sometimes for some compilers and some platforms. I assert that it's better to use standard C++ features and just work everywhere.
- spacechild1 2y ago> * Specifying calling convention at all is a compiler specific extension. Yes, because the calling conventions themselves are platform/compiler specific. > There is no standard way of specifying a C calling convention without `extern`. Well, on modern platforms you don't need to because there is only a single calling convention that is shared between C and C++. For legacy platforms with multiple calling conventions, you need compiler specific extensions by definition. > The only portable way to specify C linkage in a C++ program is extern "C". You will always get the right ABI for your platform and it will work on every compiler. Again, on platforms with several calling conventions `extern "C"` absolutely won't give you the appropriate calling convention all the time. See again my Win32 API example. > The compiler will very often not tell you > An incorrect ABI will usually be accepted and will just do the wrong thing or crash at runtime. That's absolutely not my experience! Functions with different calling conventions have different types, so a C++ compiler must reject such code. See https://godbolt.org/z/6EnncE5v5 https://godbolt.org/z/6EnncE5v5. (Note that for the lambda case MSVC is smart enough to automatically add __stdcall whereas MinGW refuses to compile. The free function is rejected by both compilers.) Can you show me an actual example where a C++ compiler silently accepts a function with the wrong calling convention? > Your position works sometimes for some compilers and some platforms. It has always worked for me so far and I write software for many different platforms.
- LegibleCrimson2 2y agoAh, yeah, you're right. I was spacing the fact that C as well as C++ can have multiple calling convention. I blame early morning brain. As far as the wrong calling convention goes, I'm basing it on the fact that an extern "C++" function can be passed as a callback where an extern "C" is demanded. Even if they're the same calling convention, that should fail, but it doesn't. Looks like it doesn't fail at runtime, which is a small comfort, but given the different permissiveness of different compilers, it still makes me very nervous to pass a C++ function as a C callback and just hope that it works, given that it isn't guaranteed in the standard.
- spacechild1 2y ago