Re: Correct way to provide a C callback function nside C++

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




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




[Index of Archives]     [Linux C Programming]     [Linux Kernel]     [eCos]     [Fedora Development]     [Fedora Announce]     [Autoconf]     [The DWARVES Debugging Tools]     [Yosemite Campsites]     [Yosemite News]     [Linux GCC]

  Powered by Linux