From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-ej1-x631.google.com (mail-ej1-x631.google.com [IPv6:2a00:1450:4864:20::631]) by sourceware.org (Postfix) with ESMTPS id 6AEE63858C74 for ; Fri, 27 Jan 2023 14:07:52 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 6AEE63858C74 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-ej1-x631.google.com with SMTP id hw16so13931342ejc.10 for ; Fri, 27 Jan 2023 06:07:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=SMqpSAqHhZvqjThpm4j/OgDb6T+vOz58i137uwCgwbM=; b=S4t5aY4GWTLFHkie/51zo4MXtqkcznDMmtuS2oIhZMwR8MMA4dvMTNKfAulKY9IadJ QcprOF0vKGO+gbg2Dna/plv8gCEhPxWXTWGMmTbvgRI8TfbJSTnQ9cLyeZSwUHkHldhG xBQDDC5gbJIzLYcRsGc1sXUyZSubT6Ql2StpvCUt3AtjNVcOBNtu/wEagoIO5lYSDLgS h5LfGt0UPYk2qZuKp3BHNKoVGwog+Oj+7Pw6dF3/bx2lgDmhZx7zfbJFKM+PH0OMmLP6 cOEoSdkPQH93/0naRZDTkp2XNM8R+FoROwF0eHZsliHHsztwoZ9fDjxv1pR1frbcRroN j8HQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=SMqpSAqHhZvqjThpm4j/OgDb6T+vOz58i137uwCgwbM=; b=KLIGRhkb68w0YIPxUJloPz2GJhp86v+b0ewQerlLhPVIe+RHUFduQkqIlpY2GQ5hdG l03Ps1B0DkFMY/T0UsoSI1eNlFOUkoFEL83fdqRLURg9N5kX/G0FieIzT0Nr7QgCaaVt KLG9tT8J8sKBSDTob1NBrHBL8SeiY6X0b6eM3v5ZEc7rZtFGUdOuFZgcEZfoL4lmBlgy s2XzdC6bcyGCUdkC2Tp4derh1U7CEQIk8P0cyWmhrvZMvVrBROfRuMS/nmR0HlCCxzet V7N6IU/MRzYkNy2T0AtRnilPPR+GRlqgmiTnyDqhknUtGrksdneeTbfJKQqaUnH+ad// XSUg== X-Gm-Message-State: AFqh2kqIKKvjjpn4XS4bZ6N7OhvjP/MoN60uJDp59MnvTmy0k725G/W3 4D/fw/aBxZaFKiZVrSyRFkCKL7QTY5ecARTYl4c= X-Google-Smtp-Source: AMrXdXtNpJcSiNzMNX3hSPuo07gXGwR9sKMwnLlYyJyCKHQGSt2IgfGKHDgCqJJd7GdW4lrqNGKuC1lA0Rgc9x3VaG8= X-Received: by 2002:a17:906:2dd3:b0:871:7329:ef59 with SMTP id h19-20020a1709062dd300b008717329ef59mr6081456eji.53.1674828470387; Fri, 27 Jan 2023 06:07:50 -0800 (PST) MIME-Version: 1.0 References: In-Reply-To: From: Jonathan Wakely Date: Fri, 27 Jan 2023 14:07:39 +0000 Message-ID: Subject: Re: Correct way to provide a C callback function nside C++ To: Pepe Cc: gcc-help@gcc.gnu.org Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-Spam-Status: No, score=-0.6 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,KAM_SHORT,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: On Fri, 27 Jan 2023 at 13:24, Pepe via Gcc-help wrot= e: > > Hi there, > > I=E2=80=99m in an ongoing discussion about whether or not one should use = extern =E2=80=9CC=E2=80=9D when defining a function that will be used as a = callback in a statically linked C library. For example: > > c_func.h: > // =E2=80=A6 > void reg_callback(void (*fn)()); > // =E2=80=A6 > > cpp_impl.cpp: > // =E2=80=A6 > extern =E2=80=9CC=E2=80=9D { > #include =E2=80=9Cc_func.h=E2=80=9D > } > > // my callback function with internal linkage > namespace { > > extern =E2=80=9CC=E2=80=9D { > static void my_callback_A() { > // =E2=80=A6 > } > } // extern =E2=80=9CC=E2=80=9D > > void my_callback_B() { > // =E2=80=A6 > } > > } // 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, beca= use it is not guaranteed that C and C++ use the same calling conventions. T= herefore 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=E2=80=99ve been trying to find something definitive for days now, but t= o no avail. Now I=E2=80=99m not sure what=E2=80=99s true or not. The counte= r argument is the following: The compiler should know reg_callback is a C f= unction 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 d= efinitive 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 =3D 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=3D2316 (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. > > Pepe