Why do you need "extern C" for C++ callbacks to C functions?

5.2k Views Asked by At

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.

6

There are 6 best solutions below

15
On BEST ANSWER

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.

0
On

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.

0
On

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

3
On

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.

1
On

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.

0
On

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).