public inbox for gcc-help@gcc.gnu.org
 help / color / mirror / Atom feed
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

  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).