为什么C ++回调C函数需要“extern C”?

时间:2010-04-07 16:31:49

标签: c++ c callback extern-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)。

6 个答案:

答案 0 :(得分:16)

  

很明显,thread_proxy函数是私有内部的,我不认为它会被破坏为“thread_proxy”,因为我实际上根本不需要它。

无论如何,它仍然会受到损害。 (如果不是extern "C")这就是编译器的工作原理。我同意可以想象一个编译器可以说“这不一定需要被修复”,但标准上没有说明任何内容。也就是说,在这里没有发挥作用,因为我们没有尝试链接到该功能。

  

事实上,在我编写的所有代码中,我在许多平台上运行,但我从未使用过extern "C",这与普通函数一样。

在不同平台上书写与extern "C"无关。我希望所有标准C ++代码都能在所有具有标准C ++兼容编译器的平台上运行。

extern "C"与C接口有关,pthread是C的库。它不仅不会破坏名称,还确保它可以使用C调用约定进行调用。这是需要保证的调用约定,因为我们不能假设我们在某个编译器,平台或体系结构上运行,尝试这样做的最好方法是使用给我们的功能:{{1} }。

  

我的问题是extern "C"函数污染了全局命名空间,并且它们实际上并没有像作者所期望的那样被隐藏。

上述代码没有任何污染。它位于未命名的命名空间中,无法在翻译单元外访问。

答案 1 :(得分:6)

extern "C"链接并不一定意味着仅抑制名称修改。实际上,可能有一个编译器将extern "C"视为不同的调用约定。

标准将此完全打开为实现定义的语义。

答案 2 :(得分:5)

问题是有效的 - 虽然函数被传递给C库,但C库根本没有链接到C ++代码。它仅被赋予函数的地址,因此它对函数的名称根本没有兴趣。

关键是extern "C"是最接近跨平台的方式告诉编译器使函数在该平台上使用标准C调用约定(即参数和返回值应该如何应该是最接近的)被传递到堆栈上。)

不幸的是,它还具有在全局级别创建外部链接器符号的副作用。但是,可以通过使用boost_detail_thread_proxy之类的名称来减轻这种情况。

答案 3 :(得分:1)

它用于使函数使用编译器通过C调用约定理解的任何内容,同时避免编译器特定的关键字,如__cdecl


这就是它的全部。它与名称修改,命名空间或任何其他奇怪的答案完全无关(正如你在问的时候已经知道的那样)。

答案 4 :(得分:0)

可能是因为你正在连接普通的C库 - pthreads。

答案 5 :(得分:0)

由于不保证C和C ++具有相同的调用约定,因此您需要将回调函数声明为extern "C",以便将其传递给pthread_create C函数。

上面的thread_proxy函数具有外部链接(即在其翻译单元之外可见),因为名称空间对extern "C"函数没有影响 - 甚至是匿名名称空间。相反,要赋予thread_proxy函数内部链接,您需要将其声明为静态:

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

[编辑]请注意,boost已包含此更改。请参阅https://svn.boost.org/trac/boost/ticket/5170