From: Michael Pape <list.gcc-help@pe82.de>
To: gcc-help@gcc.gnu.org
Subject: Re: Correct way to provide a C callback function nside C++
Date: Fri, 27 Jan 2023 15:29:07 +0100 [thread overview]
Message-ID: <D8B7DAF4-CB21-4F3A-BFA0-7A07C503DBD5@pe82.de> (raw)
In-Reply-To: <CAH6eHdQqJ3-Ac3XQTP6+Rdt3PxXj944hYPwJ-TCZhQTKxKyV=Q@mail.gmail.com>
On 27 Jan 2023, at 15:07, Jonathan Wakely wrote:
> On Fri, 27 Jan 2023 at 13:24, Pepe via Gcc-help <gcc-help@gcc.gnu.org> 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
next prev parent reply other threads:[~2023-01-27 14:29 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-01-27 13:23 Pepe
2023-01-27 13:48 ` Tom Kacvinsky
2023-01-27 13:51 ` Jonathan Wakely
2023-01-27 14:07 ` Jonathan Wakely
2023-01-27 14:29 ` Michael Pape [this message]
2023-01-27 14:30 ` Xi Ruoyao
2023-01-27 15:32 ` Jonathan Wakely
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=D8B7DAF4-CB21-4F3A-BFA0-7A07C503DBD5@pe82.de \
--to=list.gcc-help@pe82.de \
--cc=gcc-help@gcc.gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).