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

6

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.

Rochkind answered 7/4, 2010 at 16:31 Comment(7)
Possible dupe: #2403891Overhear
@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.Rochkind
@Artyom: Name mangling is required to support namespaces -- the feature you are complaining extern "C" conflicts with.Overhear
@Billy I know this! But still "extern C" used even when linkage is not required!Rochkind
@Billy ONeal: External linkage is not required for C callbacks.Sampling
@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.Rochkind
@Joe Gauterin: Hmm.. for some reason I was thinking otherwise. Comment deleted.Overhear
C
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.

Citole answered 7/4, 2010 at 16:37 Comment(15)
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.Rochkind
@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.Overhear
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.Anthropology
@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.Citole
@Rochkind -- x86, Borland C++ compiler. It uses fastcall by default for C++ functions.Anthropology
@atzz: I'm merely listing the effects it will have. I'm make it more clear though.Citole
Declaring the function as extern "C" which gives it 'C' linkage so it will be visible outside the translation unit.Sampling
@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.Citole
@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.Rochkind
@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.Citole
@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.Rochkind
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.Ligroin
@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.Citole
@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.2Rochkind
@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).Excursus
Z
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.

Zygosis answered 7/4, 2010 at 16:37 Comment(3)
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.Rochkind
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?Dung
@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.Sampling
M
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.

Mn answered 7/4, 2010 at 16:42 Comment(1)
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"Rochkind
S
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).

Sampling answered 7/4, 2010 at 17:7 Comment(0)
M
0

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

Mcgann answered 7/4, 2010 at 16:35 Comment(0)
E
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.

Excursus answered 8/2, 2011 at 20:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.