13

After reading about extern and static, I am confused to come across the code which has the line below:

extern "C" static void* foo(int* a){
    return foo1(a);
}

Why does this not generate any error?

nineties
  • 423
  • 1
  • 7
  • 17
  • Curious: which compiler on which platform? Note that the body of the function can't compile (`return foo1(int* a);` is invalid C++). When compiled with G++ 6.1.0 on Mac OS X 10.11.6, from source file `bust-cpp.cpp`, using command line: `g++ -O3 -g -I./inc -std=c++11 -Wall -Wextra -Werror -c bust-cpp.cpp` I get messages: `bust-cpp.cpp:1:12: error: invalid use of ‘static’ in linkage specification` pointing at `extern "C" static void* foo(int* a){`. – Jonathan Leffler Jul 19 '16 at 00:00
  • probably this is in a file which never gets compiled – Ben Voigt Jul 19 '16 at 00:00
  • gcc-5.1 won't compile [that](http://ideone.com/15OaqR) – PcAF Jul 19 '16 at 00:02
  • g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-4) on x86. It does compile... It's a c++ test code for a fairly large project, which was written in C – nineties Jul 19 '16 at 00:03
  • 1
    Well, if you will (more likely, 'are forced to') use archaic compilers :) … That is very curious. You should report to the powers that be that it won't compile after upgrades. It isn't clear why you'd want C-style linkage on functions that cannot be accessed from outside the C++ source file that defines it. The code looks willfully obscure and pretty much completely pointless. I suppose that if a pointer to the function is returned somewhere and the C linkage is crucial, it might serve a purpose on a machine where there are differences between C and C++ linkage. A bit out-of-the-ordinary! – Jonathan Leffler Jul 19 '16 at 00:05
  • Is `foo1` defined as a macro for the Preprocessor somewhere? If you have a define preceding these lines of code such as `#define foo1(x) NULL` then it would compile just fine. Not sure why that would be though. What is the definition of `foo1`? – Richard Chambers Jul 19 '16 at 00:05
  • The people who wrote this use intel c++ compiler, but I don't have access to that... So I guess the conclusion is that this shouldn't be allowed? – nineties Jul 19 '16 at 00:06
  • foo1 is a function defined in a "C" library, which was compiled with gcc – nineties Jul 19 '16 at 00:07
  • This should have been written as `extern "C" { static void* foo(int* a) { return foo1(a); } }`. Then there's no error, and a pointer to `foo` can be passed to something needing a C callback without `foo()` ending up in the external namespace. Intel and MSVC accept the form in the question as an oversight or as an extension - perhaps undocumented. But I think there is a legitimate reason for the (presumed) desired behavior. – Michael Burr Jul 19 '16 at 01:17
  • @MichaelBurr Have you ever seen an externally linkable function that is internally linkable only? Think about how `extern` and `static` clash... Maybe I'm missing something, but it does seem like an error to me... – autistic Jul 19 '16 at 03:40
  • 2
    @Seb: the `extern "C"` functionality isn't only to make functions (or objects) externally linkable. As I mention in a comment to Dan Haydar's answer, it may also affect the calling convention. In fact, the standard shows and explains an example of a `static` function declared inside an `extern "C"` 'block' in the section about external linkage. – Michael Burr Jul 19 '16 at 05:42

1 Answers1

9

The following also compiles and does the same thing as your line:

extern "C" {
  static void* foo(int* a){
    return foo1(a);
  }
}

The static means that foo() will only be available in file scope, and it overrides the extern "C" when it comes to linkage. Normally, extern "C" effects the name of the function used by the linker if/when it is exported, so that the function could be called from other object files when the entire program is linked. Usually, this is used when either you want to link to an object file built from C source where foo() was defined, or where you want to define foo() so that C code can call it. However, the static causes foo to simply not be exported at all, so it basically doesn't even get a name (it has internal, not external linkage).

extern "C" also has a secondary effect. It also becomes part of the type of foo. That is, C++ code will see foo as having type extern "C" void(int*). Basically, this controls calling convention. A C++ compiler may e.g. arrange arguments differently in registers/on the stack than a C compiler might. Making foo be a C function means that it will use the C conventions and not the C++ conventions. This makes it safe to e.g. pass a function pointer to foo to a C function that expects a pointer to a C function. For example, the standard library has

extern "C" typedef int C_CMP(void const*, void const*);
extern "C++" typedef int CXX_CMP(void const*, void const*);
void std::qsort(void *, std::size_t, std::size_t, C_CMP);
void std::qsort(void *, std::size_t, std::size_t, CXX_CMP);

With the extern "C", &foo is passed to the first overload, but without it/with extern "C++", it is passed to the second. It would not be safe to declare foo without extern "C" and then try to pass it into a C function that expects a pointer to a C function. It would probably work, but it might also break horribly. Add the extern "C" and it becomes correct—you're safe.

HTNW
  • 27,182
  • 1
  • 32
  • 60
D Hydar
  • 502
  • 2
  • 8
  • 7
    I'm pretty sure the reason for the extern/static function is to pass a pointer to something that wants a C callback. Strictly speaking, it's not valid to pass a pointer to a function with C++ linkage to something that needs a C callable function, even if it works in practice. Usually a plain old `extern "C"` function is used, but why clutter the global namespace for something you only want a pointer to? – Michael Burr Jul 19 '16 at 00:55
  • 4
    In fact, the C++11 standard uses `extern "C" { static void f4(); }` as an example in 7.5 "Linkage specifications". The accompanying comment says, "the name of the function f4 has internal linkage (not C language linkage) and the function's type has C language linkage". – Michael Burr Jul 19 '16 at 01:03
  • Just doing the "static" should be enough to keep foo() out of the global namespace. The bit about not being able to pass a "C" linkage function *as a pointer* to C++ code and vice versa seems odd. The linkage is about *naming* while pointer to function is about physical addresses. They seem to be orthogonal ideas. Then again, my days as a language lawyer are far in the past, so.... – D Hydar Jul 19 '16 at 01:03
  • 2
    The C++ standard makes a comment in a note: "a particular language linkage may be associated with a particular form of representing names of objects and functions with external linkage, **or with a particular calling convention**, etc.". However, the standard does explicitly forbid having a storage specifier (like `static`) in the same statement as the `extern "C"`. But the nested form is permitted. There's no doubt that it's kind of an odd corner of the language. – Michael Burr Jul 19 '16 at 01:10
  • `return foo1(int* a);` This compiles? Really? – Lightness Races in Orbit Feb 09 '19 at 15:48