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

发布于 2024-08-28 12:34:02 字数 798 浏览 2 评论 0原文

我在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

熊抱啵儿 2024-09-04 12:34:02

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

无论如何,它仍然会被破坏。 (如果不是extern "C")编译器就是这样工作的。我同意编译器可能会说“这不一定需要被破坏”,但标准对此没有任何说明。也就是说,重整在这里不起作用,因为我们不尝试链接到该函数。

事实上,在我编写的并在许多平台上运行的所有代码中,我从未使用过 extern "C" 并且它可以按正常功能正常工作。

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

extern "C" 与与 C 的接口有关,pthread 是 C 的一个库。它不仅不会破坏名称,而且确保可以使用 C 调用约定来调用它。需要保证的是调用约定,并且因为我们不能假设我们正在某个编译器、平台或体系结构上运行,所以尝试做到这一点的最佳方法是使用提供给我们的功能:extern “C”

我的问题是 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.

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.

万人眼中万个我 2024-09-04 12:34:02

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 treats extern "C" as a different calling convention.

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

庆幸我还是我 2024-09-04 12:34:02

这个问题是有效的 - 尽管函数被传递给 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.

若水般的淡然安静女子 2024-09-04 12:34:02

它用于使函数使用编译器通过 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).

葬﹪忆之殇 2024-09-04 12:34:02

可能是因为您正在连接一个普通的 C 库——pthreads。

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

箹锭⒈辈孓 2024-09-04 12:34:02

由于 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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文