3

Consider this example:

int foo(void);
extern "C" int foo(void);

int main()
{
    return foo();
}

It errors out with:

$ g++ -c main.cpp
main.cpp:2:16: error: conflicting declaration of ‘int foo()’ with ‘C’ linkage
    2 | extern "C" int foo(void);
      |                ^~~
main.cpp:1:5: note: previous declaration with ‘C++’ linkage
    1 | int foo(void);
      |     ^~~

Which is perfectly fine.

But lets swap first 2 lines:

extern "C" int foo(void);
int foo(void);

int main()
{
    return foo();
}

Now it compiles without any error, but the C linkage is chosen, even though C++ linkage is found the last.

Questions:

  1. Why case 2 compiles and case 1 fails? I would expect them to behave the same way.
  2. For the case 2 which compiles, why C linkage is chosen and is it possible to change that?

Background: I have a C++ program where the function name occasionally clashed with the one in a C standard library. I would expect an error in such case, but it compiled fine and the wrong linkage was chosen. I need to either find a way of making it to fail consistently (to fix all such cases), or to force the selection of a C++ linkage for all conflicting functions.

stsp
  • 308
  • 1
  • 6
  • 6
    *Background: I have a C++ program where the function name occasionally clashed with the one in a C standard library.* How many `using namespace std;` lines are in that code? I strongly suspect this is apropos here: [**Why is "using namespace std;" considered bad practice?**](https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice) – Andrew Henle Nov 27 '22 at 16:41
  • 1
    Names from the C standard library are reserved in the global namespace scope anyway. Only ever use them inside your own namespace. (Or for that matter, just never use the global namespace when writing C++, with the exception of `operator new`/`operator delete`.) – user17732522 Nov 27 '22 at 16:51
  • @AndrewHenle Unfortunately even including only C++ standard library headers may import C standard library declarations into the global namespace scope. So the problem is likely to persist without `using namespace std;` and without using any of the `.h` C standard library headers. – user17732522 Nov 27 '22 at 16:55
  • @user17732522 Agreed - the real problem here is "the function name occasionally clashed with the one in a C standard library" That's goes well beyond ["code smell"](https://en.wikipedia.org/wiki/Code_smell) and IMO dives directly into the fetid sewer of "rank code stench" - the "occasionally" implies "sometimes you call `strstr()` from `libc`, sometimes you call `strstr()` from `libMyLib`, and you never know which one you're really getting" to me, and that is a recipe some really weird Heisenbugs. – Andrew Henle Nov 27 '22 at 17:01
  • The project is initially a freestanding environment, so the clash was never a problem. But all of sudden you include some header from stdlib, and everything stops working. – stsp Nov 27 '22 at 17:15
  • @stsp It seems I misremembered the standard. The C standard library names are not generally reserved. They are reserved only for C linkage and only their concrete signatures are reserved for C++ linkage. So overloading C with C++ linkage is allowed. I would still follow my previous advice. – user17732522 Nov 27 '22 at 18:13

1 Answers1

7
extern "C" int foo(void);
int foo(void);

Here foo is declared with extern "C". Re-declaration does not specify a different calling convention, so no problem.

But,

int foo(void);
extern "C" int foo(void);

Here first line does not explicitly specify calling convention, so default C++ is implicitly chosen. Then second line does explicitly specify a different calling convention, creating a conflict.

So question 2... It is not possible to change calling convention after it is set by first declaration.


How to solve the problem... Use a different name for your C++ function. Or put it in a namespace. Or maybe isolate conflicting C++ name in a separate .cpp file, and export a different function (or just function pointer) for calling it elsewhere (see last paragraph below).

Another solution is to be careful about #include order, so the C++ function is always declared first. There is no "nice" solution to this, other than being pedantic about include order.

The only proper, valid solution is not have any symbols, which conflict with anything in C++ standard library. That is not allowed by C++ standard. Rename your own functions, or put them in your own namespace.


An important detail is, that the linker symbol for these functions is different. C++ does symbol name mangling to enable namespaces, methods and overloading. So it is possible for both functions to exist and be callable in a linked/running program. Problem is only at source code level.

hyde
  • 60,639
  • 21
  • 115
  • 176
  • @user17732522 Added a bolded paragraph about that. – hyde Nov 27 '22 at 17:14
  • 1
    The project is initially a freestanding environment, so the conflicts were never a problem. But once you include some C header, working code silently breaks. Including C++ header before Cish, or using extern "C++" could work but doesn't, because the C function has eg "int" argument and C++ one has "short" argument. So they still do not conflict. :( It would be good to disallow any overloading in the presence of a func with C linkage, but I don't suppose its possible. – stsp Nov 27 '22 at 17:31
  • 1
    @hyde Sorry about my earlier comment. I turns out I misremembered the standard. It seems that the names are reserved only for functions with C linkage and only specific signatures are reserved for both linkage specifications. Also it seems overloading C linkage with C++ linkage is now allowed per https://cplusplus.github.io/CWG/issues/1708.html, but I am a bit confused about the current wording in https://eel.is/c++draft/dcl.link#7.sentence-1 myself. – user17732522 Nov 27 '22 at 18:10
  • @stsp After rereading the standard I realized that it is indeed allowed to overload with different language linkage like that. But if your problem really is that e.g. overload resolution picks the wrong overload, then this is not an issue with linkage in the first place, but instead just a name conflict in the same way that you could have when using any library (whether C or C++). And again the solution is simply to work in your own namespace. (And this applies to the library as well as the user code, but unfortunately the C library can't respect that since C doesn't have namespaces.) – user17732522 Nov 27 '22 at 18:24
  • I ended up with manual renamings, thanks. Still I am quite disappointed that there is no way to at least make the conflicts obvious, because I can't even be sure I fixed all instances. – stsp Nov 27 '22 at 20:31