Are extern "C" functions a separate type?
Asked Answered
M

3

18

From the C++11 draft, 7.5 (para. 1):

Two function types with different language linkages are distinct types even if they are otherwise identical.

So I can do overload based on language linkages:

extern "C" typedef void (*c_function)();
typedef void (*cpp_function)();

void call_fun(c_function f)
{
}
void call_fun(cpp_function f)
{
}

extern "C" void my_c()
{
}
void my_cpp()
{
}
int main()
{
    call_fun(my_c);
    call_fun(my_cpp);
}

But, with GCC 4.7.1 this sample code gives the error messages:

test.cpp: In function 'void call_fun(cpp_function)':
test.cpp:7:6: error: redefinition of 'void call_fun(cpp_function)'
test.cpp:4:6: error: 'void call_fun(c_function)' previously defined here

And with CLang++ :

test.cpp:7:6: error: redefinition of 'call_fun'
void call_fun(cpp_function f)
     ^
test.cpp:4:6: note: previous definition is here
void call_fun(c_function f)
     ^

Now the questions:

  • Is my understanding of the standard correct? Is this code valid?

  • Does anybody know if these are bugs in the compilers or if they are intentionally doing it that way for compatibility purposes?

Mutz answered 18/9, 2012 at 9:41 Comment(2)
Just for the record: the C++03 standard has exactly the same sentence in the same paragraph, so this is not a question of a C++11 feature not yet supported by compilers.Mussman
See https://mcmap.net/q/741590/-how-do-i-pass-a-c-callback-to-a-c-library-function. Make sure calling conventions of C and C++ match.Parsaye
C
8

It's a known bug in gcc, and they record that it's non-conforming since this bug blocks the uber-bug, "C++98 conformance issues".

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=2316

Check the creation date.

There's some discussion towards the end, about the practicalities of introducing a fix. So the answer to your last question is "both": it's a bug and the bug has intentionally been left in for compatibility.

Other compilers with the same issue might have made the error independently, but I think more likely they also know that it's wrong but want to be bug-compatible with gcc.

Contagious answered 18/9, 2012 at 10:9 Comment(4)
Triky, because I'm really using pointers to template functions converted to pointers to extern "C" functions. I don't know of any way to make a extern "C" template function, so I think I will keep my code as it is for now...Mutz
Mmm, sounds infuriating. I guess you can do it with (at most) one extern "C" wrapper function per C API that you use, provided that the C-linkage function takes a user data pointer. You smuggle your C++-linkage function pointer to the C-linkage function by storing it in the user data and call it from there. "If in doubt add more indirection", sort of thing.Contagious
@Steve_Jessop: Thanks, I had something like that in mind, but that breaks my zero-overhead objective that I pursued when used templates instead of a dispatcher object with a virtual function and a static member func... D'oh!Mutz
@rodrigo: yeah, I figured you probably wouldn't be too pleased by the idea of an extra function, let alone the extra memory allocation you might need for the user data, depending on the lifetime of everything involved...Contagious
H
10

The code is clearly valid. G++ (and a number of other compilers) are a bit lax (to put it mildly) about integrating the linkage into the type.

Holds answered 18/9, 2012 at 9:58 Comment(7)
+1 Based on quote which OP provided this seems to be correct, unless untill there is some corner of the standard which adds an exception to the said quote.Anyhow, Knowing James I think he knows better and I trust his word on that :)Disc
@Als The classical example where this makes a difference: passing a static member function to pthread_create (or CreateThread). According to the standard, this is illegal (since pthread_create requires an extern "C", and a member cannot be extern "C"), but both g++ and VC++ allow it.Holds
Thanks. The thing is that I have some code that happens to rely on this GCC behavior. It works, of course, but it makes me a bit uncomfortable because it seems to be totally non-portable. My issue is whether this laxitude is intentional, and thus I can consider it a "GCC extension".Mutz
@Mutz Given how long ago this discrepancy with the standard was noted in g++, I think it's safe to say that it's intentional, or at least that g++ doesn't intend to correct it, and that it's an "extension" (which, as far as I know, can't be turned off).Holds
@JamesKanze: I'm not sure about CreateThread requiring an extern "C" function. AFAICT it requires an extern "C++" function when it's called from C++ code.Whodunit
@MSalters: As long as the definition in Windows.h has the #ifdef __cplusplus\n extern "C" {\n #endif lines, it requires an extern "C" function when using a C++ compiler. Not that the VC++ really cares...Mutz
@Whodunit I'm not too sure about CreateThread myself; Microsoft isn't too clear (but then, Microsoft doesn't consider extern "C" part of the type). I'm supposing that the function can be called from C, but that really doesn't mean much; MS uses a number of extensions to defined the calling conventions, and CreateThread is likely defined with one of these. And since we are using implementation defined extensions, what is or is not allowed is implementation defined.Holds
C
8

It's a known bug in gcc, and they record that it's non-conforming since this bug blocks the uber-bug, "C++98 conformance issues".

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=2316

Check the creation date.

There's some discussion towards the end, about the practicalities of introducing a fix. So the answer to your last question is "both": it's a bug and the bug has intentionally been left in for compatibility.

Other compilers with the same issue might have made the error independently, but I think more likely they also know that it's wrong but want to be bug-compatible with gcc.

Contagious answered 18/9, 2012 at 10:9 Comment(4)
Triky, because I'm really using pointers to template functions converted to pointers to extern "C" functions. I don't know of any way to make a extern "C" template function, so I think I will keep my code as it is for now...Mutz
Mmm, sounds infuriating. I guess you can do it with (at most) one extern "C" wrapper function per C API that you use, provided that the C-linkage function takes a user data pointer. You smuggle your C++-linkage function pointer to the C-linkage function by storing it in the user data and call it from there. "If in doubt add more indirection", sort of thing.Contagious
@Steve_Jessop: Thanks, I had something like that in mind, but that breaks my zero-overhead objective that I pursued when used templates instead of a dispatcher object with a virtual function and a static member func... D'oh!Mutz
@rodrigo: yeah, I figured you probably wouldn't be too pleased by the idea of an extra function, let alone the extra memory allocation you might need for the user data, depending on the lifetime of everything involved...Contagious
K
1

For what it's worth, this code also fails to compile with default settings in VS2012:

(8) error C2084: function 'void call_fun(c_function)' already has a body
(4) see previous definition of 'call_fun'
(19) error C3861: 'call_fun': identifier not found
(20) error C3861: 'call_fun': identifier not found
Kathikathiawar answered 18/9, 2012 at 12:14 Comment(1)
But the online Coumeau C++ compiler accepts it without any warning!Mutz

© 2022 - 2024 — McMap. All rights reserved.