为什么需要“extern C”对于 C++ C 函数的回调?
我在 Boost 代码中找到了这样的例子。
namespace boost {
namespace {
extern "C" void *thread_proxy(void *f)
{
....
}
} // anonymous
void thread::thread_start(...)
{
...
pthread_create(something,0,&thread_proxy,something_else);
...
}
} // boost
为什么你真的需要这个extern "C"
?
很明显,thread_proxy
函数是私有内部函数,我不希望它 会被破坏为“thread_proxy”,因为我实际上根本不需要它被破坏。
事实上,在我编写的并在许多平台上运行的所有代码中,我从未使用过 extern "C" ,并且它与正常函数一样工作。
为什么要添加extern "C"
?
我的问题是 extern "C"
函数污染了全局命名空间,并且它们实际上并没有像作者期望的那样隐藏。
这不是重复的! 我不是在谈论损坏和外部链接。在此代码中很明显,外部链接是不需要的!
答: C 和 C++ 函数的调用约定不一定相同,因此您需要创建一个符合 C 调用约定的函数。参见 C++ 标准 7.5 (p4)。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
无论如何,它仍然会被破坏。 (如果不是
extern "C"
)编译器就是这样工作的。我同意编译器可能会说“这不一定需要被破坏”,但标准对此没有任何说明。也就是说,重整在这里不起作用,因为我们不尝试链接到该函数。在不同平台上编写与
extern "C"
无关。我希望所有标准 C++ 代码都能在具有标准 C++ 兼容编译器的所有平台上运行。extern "C"
与与 C 的接口有关,pthread 是 C 的一个库。它不仅不会破坏名称,而且确保可以使用 C 调用约定来调用它。需要保证的是调用约定,并且因为我们不能假设我们正在某个编译器、平台或体系结构上运行,所以尝试做到这一点的最佳方法是使用提供给我们的功能:extern “C”
。上面的代码没有任何污染。它位于未命名的命名空间中,并且无法在翻译单元外部访问。
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.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"
.There's nothing polluting about the above code. It's in an unnamed namespace, and not accessible outside the translation unit.
extern "C"
链接并不一定意味着仅抑制名称修改。事实上,可能有一个编译器将extern "C"
视为不同的调用约定。该标准将其完全开放为实现定义的语义。
extern "C"
linkage does not necessarily mean that only name mangling is suppressed. In fact, there may be a compiler which treatsextern "C"
as a different calling convention.The standard leaves this completely open as implementation-defined semantics.
这个问题是有效的 - 尽管函数被传递给 C 库,但该 C 库根本没有链接到 C++ 代码。它只给出函数的地址,因此它对函数的名称完全不感兴趣。
关键是 extern "C" 是最接近跨平台的方式,告诉编译器使函数使用该平台上的标准 C 调用约定(即参数和参数的确切方式)返回值应在堆栈上传递)。
不幸的是,它还具有在全局级别创建外部链接器符号的副作用。但可以通过使用诸如 boost_detail_thread_proxy 之类的名称来缓解这种情况。
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.它用于使函数使用编译器通过 C 调用约定理解的任何内容,同时避免编译器特定的关键字,例如
__cdecl
。这就是全部内容了。它与名称修改、名称空间或任何其他奇怪的答案完全无关(正如您在询问时已经知道的那样)。
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).
可能是因为您正在连接一个普通的 C 库——pthreads。
Probably because you are interfacing a plain C library -- pthreads.
由于 C 和 C++ 不保证具有相同的调用约定,因此您需要将回调函数声明为
extern "C"
以便将其传递给pthread_create
C 函数。上面的
thread_proxy
函数具有外部链接(即在其翻译单元之外可见),因为命名空间对extern "C"
函数没有影响——甚至是匿名命名空间。相反,要为thread_proxy
函数提供内部链接,您需要将其声明为静态:[编辑] 请注意,boost 已合并此更改。请参阅https://svn.boost.org/trac/boost/ticket/5170。
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 thepthread_create
C function.The
thread_proxy
function above has external linkage (i.e. is visible outside its translation unit) because namespaces have no impact onextern "C"
functions -- even anonymous namespaces. Instead, to give thethread_proxy
function internal linkage, you need to declare it as static:[Edit] Note that boost has incorporated this change. See https://svn.boost.org/trac/boost/ticket/5170.