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?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • 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. – Gorpik Sep 18 '12 at 10:14
  • See http://stackoverflow.com/a/10643935/1463922. Make sure calling conventions of C and C++ match. – PiotrNycz Sep 18 '12 at 10:14

3 Answers3

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.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • +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 :) – Alok Save Sep 18 '12 at 10:02
  • 1
    @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. – James Kanze Sep 18 '12 at 10:12
  • 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". – rodrigo Sep 18 '12 at 10:39
  • @rodrigo 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). – James Kanze Sep 18 '12 at 11:07
  • @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. – MSalters Sep 18 '12 at 12:35
  • @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... – rodrigo Sep 18 '12 at 13:00
  • 1
    @MSalters 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. – James Kanze Sep 18 '12 at 13:10
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.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • 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... – rodrigo Sep 18 '12 at 13:02
  • 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. – Steve Jessop Sep 18 '12 at 14:14
  • @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!_ – rodrigo Sep 18 '12 at 14:24
  • @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... – Steve Jessop Sep 18 '12 at 14:25
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
mark
  • 5,269
  • 2
  • 21
  • 34
  • But the online [Coumeau C++ compiler](http://www.comeaucomputing.com/tryitout/) accepts it without any warning! – rodrigo Sep 18 '12 at 12:40