【问题标题】:How to use a C++ member function as the callback function for a C framework如何使用 C++ 成员函数作为 C 框架的回调函数
【发布时间】:2011-07-19 15:20:18
【问题描述】:

有一个支持回调函数类型的 C 库(我无法更改)

void (*callback)(void *appContext, int eventid)

我想设置一个 C++ 函数作为回调。

具体我有以下问题?

  1. 需要在"extern C"块下声明回调函数吗?

  2. 成员函数是否必须是静态的才能成为回调函数?是否可以使用非静态成员函数?如果是,如何?什么时候推荐使用非静态成员函数?

  3. 函数是否为模板函数是否重要?

  4. 非类 C 风格的函数比类成员函数有什么优势吗?

我正在旧的 VC++ 编译器上尝试这些变体,它不支持最新的 C++ 标准。但是代码需要独立于平台,并且应该适用于大多数 C++ 编译器。我想知道回调的推荐做法是什么?

【问题讨论】:

  • 您使用的是什么版本的 C/C++? (即 MSVC、GCC)
  • 我现在正在使用 MSVC。但是代码需要独立于平台,因为它也将被移植到 linux 平台。

标签: c++ c callback


【解决方案1】:

回调函数需要在extern "C"下声明吗?

没有。仅当您从 C 中直接调用 C++ 函数而不使用函数指针时才需要 extern "C"。如果使用函数指针,则不需要 extern "C"。

我可以使用非静态成员函数作为回调吗?

没有。 A类的非静态成员函数有一个隐含的第一个参数对应于这个指针。

我可以使用静态成员函数作为回调吗?

是的,只要签名与回调的签名匹配。

函数是否为模板函数有关系吗?

不,只要实例化模板的签名与回调匹配,模板函数就可以用作回调。

【讨论】:

    【解决方案2】:

    假设appContext 是一个不透明的指针,您将其传递给进行回调的函数,您可以像这样获取特定对象的成员函数的回调:

    class myclass {
    
      void do_something() {
         // call function making the callback using _event_handler
         // as the callback function and the "this" pointer as appContext
      }
    
      // make sure the raw callback uses the correct calling convention (cdecl, stdcall, etc.)
      static void _handle_event(void* appContext, int eventid) {
        // forward the event to the actual object
        static_cast<myclass *>(appContext)->handle_event(eventid);
      }
    
      void handle_event(int eventid) {
         // do object-specific event handling
      }
    
    };
    

    几个答案提到 extern "C" 作为要求。这是完全不正确的。 仅当您直接从 C++ 调用 C 函数时,extern "C" 才是必需的。它用于告诉 C++ 编译器“在为此函数生成符号名称时不要应用名称修饰”。您正在将 C++ 函数指针传递给 C 函数。只要调用约定匹配,它就可以正常工作。从不涉及函数的名称。

    【讨论】:

      【解决方案3】:

      如果你的成员函数是静态的,这应该可以工作。

      【讨论】:

      • 我需要在extern C块下声明函数吗?
      • 我认为如果您的成员函数是静态的,它将起作用。试试看。
      • 只要成员是静态的,调用约定是一样的,参数列表匹配就行。
      【解决方案4】:

      这并不像在extern "C" 块下声明回调函数那么简单。您需要弄清楚 C 库对其函数使用的调用约定。

      我将使用的术语是特定于 Microsoft 的,但同样的规则也应该适用于其他平台。

      默认情况下,Visual C++ 编译器生成 C 函数 __stdcall 和 C++ 函数 __cdecl。您不能混合和匹配这些调用约定,因为它们中的每一个都对谁清理堆栈做出了不同的假设。 Here's更详细的解释。

      匹配调用约定后,最简单的方法是将 C++ 回调函数声明为命名空间范围的独立函数;或者如果它需要是一个成员函数,那么一个静态成员函数。在这两种情况下,您都应该能够使用std::bind 将指针绑定到类的实例。您甚至可以使用 std::bind 绑定非静态成员函数,但我想不起来语法。

      【讨论】:

      • extern "C" 与 stdcall/cdecl 问题完全正交。无论 C 的默认约定是什么,extern "C" 都会根据标准自动使用它。
      • @n.m.:谢谢,我不知道。但是,OP 仍然需要弄清楚调用约定以使用成员函数作为回调,因为不能指定 extern "C"
      • 这就是为什么不能将成员函数直接用作 C 回调的原因。
      • 这不是真的,如果它是静态成员函数,你很容易做到。
      • 这不是标准的,可能是不可移植的。
      【解决方案5】:
      1. 确保它在全局范围内

      2. 使用extern "C"

      3. 如果需要,请使用__cdeclvoid (_cdecl *callback)

      【讨论】:

      • (1) 和 (2) 不是必需的,也不需要。您通过指针而不是名称传递函数。 (3) 与调用约定有关,这确实很重要。它必须与对方的期望相匹配,但可以是“__stdcall”以及“__cdecl”或您平台上的任何其他调用约定。
      【解决方案6】:

      严格来说,你不能。必须将 C 回调指定为 extern "C",这对于成员函数是不可能的。只能使用独立功能。您可以从那里将调用转发到任何 C++ 函数,包括静态或非静态成员函数。

      根据平台的不同,您有时可以跳过extern "C" 而侥幸逃脱,但我不会测试我的运气。

      【讨论】:

        猜你喜欢
        • 2010-11-03
        • 1970-01-01
        • 2017-05-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-15
        • 1970-01-01
        • 2013-04-09
        相关资源
        最近更新 更多