18

I find such examples in Boost code.

namespace boost {
   namespace {
     extern "C" void *thread_proxy(void *f)
     {
       ....
     }

   } // anonymous
   void thread::thread_start(...)
   {
       ...
       pthread_create(something,0,&thread_proxy,something_else);
       ...
   }
} // boost

Why do you actually need this extern "C"?

It is clear that the thread_proxy function is private internal and I do not expect that it would be mangled as "thread_proxy" because I actually do not need it mangled at all.

In fact, in all my code that I had written and that runs on many platforms, I never used extern "C" and this had worked as-is with normal functions.

Why is extern "C" added?


My problem is that extern "C" functions pollute the global namespace and they are not actually hidden as the author expects.

This is not a duplicate! I'm not talking about mangling and external linkage. It is obvious in this code that external linkage is unwanted!

Answer: The calling conventions of C and C++ functions are not necessarily the same, so you need to create one with the C calling convention. See 7.5 (p4) of C++ standard.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Artyom
  • 31,019
  • 21
  • 127
  • 215
  • 1
    Possible dupe: http://stackoverflow.com/questions/2403391/how-does-an-extern-c-declaration-work – Billy ONeal Apr 07 '10 at 16:34
  • 1
    @Billy ONeal it is not dupe... As I told mangling is not required there. I know how to use extern "C" in order to solve manging issues. – Artyom Apr 07 '10 at 16:36
  • @Artyom: Name mangling is required to support namespaces -- the feature you are complaining `extern "C"` conflicts with. – Billy ONeal Apr 07 '10 at 16:38
  • @Billy I know this! But still "extern C" used even when linkage is not required! – Artyom Apr 07 '10 at 16:39
  • @Billy ONeal: External linkage is not required for C callbacks. – JoeG Apr 07 '10 at 16:45
  • @Billy how external linkage is connected with call in different translation unit? This function can be static and would work perfectly well. It is not related at all. – Artyom Apr 07 '10 at 16:46
  • @Joe Gauterin: Hmm.. for some reason I was thinking otherwise. Comment deleted. – Billy ONeal Apr 07 '10 at 17:33

6 Answers6

17

It is clear that the thread_proxy function is private internal and I do not expect that it would be mangled as "thread_proxy" because I actually do not need it mangled at all.

Regardless, it's still going to be mangled. (Had it not been extern "C") That's just how the compiler works. I agree it's conceivable a compiler could say "this doesn't necessarily need to be mangled", but the standard says nothing on it. That said, mangling doesn't come into play here, as we aren't trying to link to the function.

In fact, in all my code that I had written and that runs on many platforms, I never used extern "C" and this had worked as-is with normal functions.

Writing on different platforms has nothing to do with extern "C". I expect all standard C++ code to work on all platforms that have a standard C++ compliant compiler.

extern "C" has to do with interfacing with C, which pthread is a library of. Not only does it not mangle the name, it makes sure it's callable with the C calling convention. It's the calling convention that needs to be guaranteed, and because we can't assume we are running on a certain compiler, platform, or architecture, the best way to try and do that is with the functionality given to us: extern "C".

My problem is that extern "C" functions pollute the global namespace and they are not actually hidden as the author expects.

There's nothing polluting about the above code. It's in an unnamed namespace, and not accessible outside the translation unit.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • 1
    Can you give a name of any platform/compiler where C plain function calling convention is not compatible on ABI level with C++ one? Because at least on Alpha, Itanium, ARM, X86, x86_64 it is. – Artyom Apr 07 '10 at 16:42
  • 1
    @Artyom: Not on x86. It's entirely possible that your compiler uses `__cdecl` on all extern "C" functions, but `__stdcall` otherwise. `__stdcall` produces more efficient (smaller) code but does not allow creation of varadic functions because the callee cleans the stack. Since `__cdecl` is the standard in most of the C world it makes sense to require that calling convention to external code. – Billy ONeal Apr 07 '10 at 16:45
  • 1
    Mangling does not enter the picture here. Mangling is only significant when *linking* against a function by name; here its address is passed directly as a parameter to other function. – atzz Apr 07 '10 at 16:46
  • 1
    @Artyom: Who cares? If you want to argue about your specific platform, go somewhere else to do it. In *standard C++* (hint: no platform attached), you *need* to use `extern "C"` to *ensure* the behavior. That's it. Languages are an abstract concept, they aren't tied to which platforms you decide to list. That said, there are compilers/platforms today (x86, for example) that don' always use C calling conventions. – GManNickG Apr 07 '10 at 16:46
  • 3
    @Artyom -- x86, Borland C++ compiler. It uses fastcall by default for C++ functions. – atzz Apr 07 '10 at 16:47
  • @atzz: I'm merely listing the effects it will have. I'm make it more clear though. – GManNickG Apr 07 '10 at 16:51
  • Declaring the function as `extern "C"` which gives it 'C' linkage so it will be visible outside the translation unit. – JoeG Apr 07 '10 at 17:00
  • @Joe: That's incorrect. It just means "don't mangle this" and "call it like it is a C function". The unnamed namespace is like making the function static. It's *not* visible outside the translation unit. Try it yourself. – GManNickG Apr 07 '10 at 17:15
  • @GMan "It's not visible outside the translation unit. Try it yourself" -- it is visible, otherwise I would not find it in symbols when I run `nm -D libboost_thread.so`. So it is visible. – Artyom Apr 07 '10 at 19:34
  • @Artyom: *"Outside the translation unit."* It is *not* globally available, and not visible outside the translation unit. Of course it exists somewhere, or you wouldn't be able to use it. – GManNickG Apr 07 '10 at 19:53
  • @Gman Make simple test. Create file with `namespace { extern "C" void test_c(){} void test_cpp(){} } static void test_static(){}`. Compile it as `g++ -shared -fPIC test.cpp -o libtest.so` And list all symbols with `nm -C -D libtest.so` -- you will find only one -- `test_c`... So anonymous namespace does not help. – Artyom Apr 07 '10 at 20:10
  • In C++ mangling is always an issue. It is very difficult for the compiler to know that there isn't going to be another function with the same name but different signature that gets linked in later. Plus if it didn't tell you that there was an error now but then it did when you added another function with the same name it would be confusing. C++ can break on old code because of the addition of new code but it is best to avoid it. Oh, and when you do an extern C version of a function it has to treat it different and lock down the name a little more than with C++ functions. – nategoose Apr 07 '10 at 20:15
  • @Artyom: I get no symbols. Which g++ version? Really, according to the standard the function should not be visible from outside the unit. `extern "C"` has no effect on linkage. – GManNickG Apr 07 '10 at 20:30
  • @GMan, are you testing this on ELF platform (Linux) or on Windows with DLLs? Because with DLLs it is different. In any case gcc-4.3.2 – Artyom Apr 07 '10 at 21:03
  • 2
    @GMan: The symbol is also visible for me outside the translation unit, using g++ 4.4 on Linux (Ubuntu). I believe the correct way to mark the symbol as internal linkage is to declare it as static inside the extern "C", since extern "C" functions ignore any namespaces they are in (including unnamed ones). – Luke Moore Feb 10 '11 at 16:22
6

extern "C" linkage does not necessarily mean that only name mangling is suppressed. In fact, there may be a compiler which treats extern "C" as a different calling convention.

The standard leaves this completely open as implementation-defined semantics.

greyfade
  • 24,948
  • 7
  • 64
  • 80
  • Can you give a name of any platform where C plain function calling convention is not compatible on ABI level with C++ one? Because at least on Alpha, Itanium, ARM, X86, x86_64 with I have never had any issues with different conventions. – Artyom Apr 07 '10 at 16:44
  • 4
    What does that have to do with anything? Do you want to write code which only works because you're closely acquainted with the calling conventions of every single C++ implementation that has ever been written, and apply that knowledge? Or do you want to write code that works on any implementation of the C++ standard, because you do what the standard says you must do for the result you want? In future, are you going to write to the standard because someone has pointed out an implementation that does require extern "C", or because in fact you are not familiar with every compiler? – Steve Jessop Apr 07 '10 at 17:08
  • @Artyom: C++ calling conventions are a function of C++ compilers not of processors, and most compilers allow the convention used to compile C++ to be configured. Boost is designed to be portable, so it probably shouldn't break if a user changes a compiler option. – JoeG Apr 07 '10 at 17:14
5

The question is valid - although the function is being passed to a C library, that C library is not linking to the C++ code at all. It is only given the address of the function, so it has no interest at all in the name of the function.

The point is that extern "C" is the closest thing there is to a cross-platform way of telling the compiler to make the function use the standard C calling convention on that platform (i.e. exactly how parameters and return values should be passed on the stack).

It is unfortunate that it also has the side-effect of creating an extern linker symbol at the global level. But this could be mitigated by using a name like boost_detail_thread_proxy instead.

Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
  • I know that one of the solutions is to add "boost_` prefix to the variable and that's it. I just wondered why just not to remove `extern "C"` – Artyom Apr 08 '10 at 04:15
1

It's being used to make the function use whatever the compiler understands by the C calling convention while avoiding compiler specific keywords such as __cdecl.


That's all there is to it. It's got absolutely nothing to do with name mangling, namespaces or any of the other weird answers here (as you already knew when you asked).

JoeG
  • 12,994
  • 1
  • 38
  • 63
0

Probably because you are interfacing a plain C library -- pthreads.

Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725
0

Since C and C++ are not guaranteed to have the same calling convention, you need to declare the callback function as extern "C" in order to pass it into the pthread_create C function.

The thread_proxy function above has external linkage (i.e. is visible outside its translation unit) because namespaces have no impact on extern "C" functions -- even anonymous namespaces. Instead, to give the thread_proxy function internal linkage, you need to declare it as static:

namespace boost {
    namespace {
        extern "C" {
            static void *thread_proxy(void *f)
            {
                ....
            }
        } // extern "C"
    } // anonymous
    ...
} // boost

[Edit] Note that boost has incorporated this change. See https://svn.boost.org/trac/boost/ticket/5170.

Luke Moore
  • 685
  • 5
  • 7