On 27 Jan 2023, at 15:07, Jonathan Wakely wrote: > On Fri, 27 Jan 2023 at 13:24, Pepe via Gcc-help <gcc-help@xxxxxxxxxxx> wrote: >> >> Hi there, >> >> I’m in an ongoing discussion about whether or not one should use extern “C” when defining a function that will be used as a callback in a statically linked C library. For example: >> >> c_func.h: >> // … >> void reg_callback(void (*fn)()); >> // … >> >> cpp_impl.cpp: >> // … >> extern “C” { >> #include “c_func.h” >> } >> >> // my callback function with internal linkage >> namespace { >> >> extern “C” { >> static void my_callback_A() { >> // … >> } >> } // extern “C” >> >> void my_callback_B() { >> // … >> } >> >> } // namespace >> >> void do_something() { >> reg_callback(my_callback_A); >> reg_callback(my_callback_B); >> } >> >> Both callbacks have internal linkage. Both work fine, and something like my_callback_B is found in lots of code bases. >> >> In my opinion, using callback B is implementation defined behaviour, because it is not guaranteed that C and C++ use the same calling conventions. Therefore a function must adhere to the C calling conventions to be used as a callback in a C library, which would be callback A. >> >> I’ve been trying to find something definitive for days now, but to no avail. Now I’m not sure what’s true or not. The counter argument is the following: The compiler should know reg_callback is a C function and make sure that a given argument would either be valid or cause a compiler error. That sounds reasonable, so I would love to know how to do it properly for future reference. Given we use gcc I was hoping to get a definitive answer in this mailing list. Thanks a lot! > > You are (mostly) correct. The C++ standard says that extern "C" > functions and extern "C++" functions have different types, and so this > should not even compile: > > extern "C" { > using callback = void(*)(); > void f(callback); > } > > void g() { }; > void h() { f(g); } > > There should be a compilation error when trying to pass g (which is an > extern "C++" function) to f (which accepts a pointer to an extern "C" > function). > > GCC (and most other compilers) do not actually conform to that > requirement in the standard, and the types are identical. Which means > there is no compilation error, and the code works fine. > > I think it's safe to assume that *either* the code compiles and works > as expected, or fails to compile. And in practice it compiles and > works with all widely used compilers. You will not find a C++ > implementation where the types are not compatible, but the code > compiles anyway and then misbehaves at runtime. > > The relevant GCC bug about this nonconformance is > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=2316 (and will probably > never be fixed, because it would break far too much code). > > The relevant quote from the C++ standard is in [dcl.link]: > "The default language linkage of all function types, functions, and > variables is C ++ language linkage. Two function types with different > language linkages are distinct types even if they are otherwise > identical." > > Being distinct types means that there should be not implicit > conversion from &g in the example above to the type callback. An > explicit conversion (e.g. using reinterpret_cast) would be allowed, > but then it would be undefined behaviour to actually call the function > g() through a pointer to a different function type. In practice, that > isn't a problem because they're not distinct types with GCC, so the > code works. > > > > > > Thank you very much, that perfectly answered my question :) >> >> Pepe