【问题标题】:How to create lambda-wrapper around C-style callback?如何围绕 C 风格的回调创建 lambda-wrapper?
【发布时间】:2014-10-24 07:56:36
【问题描述】:

我有这样的层:C# 主应用程序 -- C++ 库的 CLI/C++ 包装器 -- C++ 库。

由于 C#-C++ 互操作性的限制,编写了 CLI 中间层。我还用它从 C# 的角度打磨了一些粗糙的边缘——换句话说,中间层允许隐藏 C/C++ 类型(如 char*)并进行所有必要的转换。

我未能进行的一个转换是传递回调。我尝试像 Pass a c++ lambda to old c function pointer 那样做,但我不能将 lambda 作为回调传递。

C++ 回调定义:

typedef void (*CppOnEngineCloseCallback)
    (void * customParam, const wchar_t * errorMessage);

我知道customParam没有被使用,所以我为中间层(CLI/C++)定义了这样的回调:

typedef void (*CLIOnEngineCloseCallback)
        ( String ^ errorMessage);

在包装器中我创建了这样的 lambda:

// lambda signature should resemble CppOnEngineCloseCallback
auto lambda = [&] (void * customParam, const wchar_t * errorMessage) {
    cli_callback(Marshal::PtrToStringUni( IntPtr(static_cast<void*>(
        const_cast<wchar_t*>(errorMessage)))));
};

我担心cli_callback 是否会在 CLI 环境中正确保存(这是我的第一个项目)——毕竟我在时间 A 传递它,并在时间 B 使用它。

编译器更加担心,因为当我尝试使用 lambda 时:

InitEngine( // C++ function that requires callback
    &lambda);

它说它无法将lambda 转换为CppOnEngineCloseCallback

首先可行吗?怎么样?

【问题讨论】:

    标签: c# visual-c++ lambda c++-cli


    【解决方案1】:

    您正在使用auto,因此您隐藏了实际类型,并使您自己更难管理 C++ 强类型类型安全性。

    可能 lambda 函数不是正确的类型,或者至少不是您期望的类型。

    CppOnEngineCloseCallback 可能是函数指针类型(或 std::funciton 类型)。
    因此,为了帮助您自己,请尝试将 lambda 的类型更改为预期的类型,然后看看编译器会说什么。

    CppOnEngineCloseCallback lambda = [&] (void * customParam, const wchar_t * errorMessage) {
        cli_callback(Marshal::PtrToStringUni( IntPtr(static_cast<void*>(
            const_cast<wchar_t*>(errorMessage)))));
    };
    

    InitEngine() 方法需要 CppOnEngineCloseCallback,因此您必须确保它得到它。

    除此之外,您只能将带有空括号 [] 的 lambda 分配给函数指针。在您的情况下,您没有使用本地范围内的任何内容,因此您实际上并不需要 [&amp;]

    Lambda functions : operator ret(*)(params)()

    此用户定义的转换函数仅在捕获时定义 lambda 表达式列表为空。

    如果您确实想使用这种 lambda 表达式,则必须使用 std::function 而不是函数指针:

    typedef std::function<void(void *, const wchar_t *)> CppOnEngineCloseCallback;
    

    【讨论】:

    • 谢谢。 CppOnEngineCloseCallback 是一个函数指针,如我的问题(第一个 sn-p)所示。将auto 更改为CppOnEngineCloseCallback 只会将错误(消息完全相同)移动到具有lambda 定义的行。
    • @greenoldman 我已经编辑了我的答案来解释如何使用[] 而不是[&amp;]。或者改用std::function
    • 再次感谢您,但cli_callback 位于本地范围内,我需要捕获它。当我只指定它进行捕获时,我会回到原始错误。而且我不能修改 C++ 层(库之一),只能修改 CLI/C++ 和 C#。
    • @greenoldman 然后你可以定义一个适当的方法而不是 lambda 并使 cli_callback 可访问。
    • 但理论上你正在做的事情是可能的,一旦你修复了编译错误,你只需要使正确的参数可以访问,
    【解决方案2】:

    一般来说,将 lambda 转换为函数指针是不可能的。 如您所知,lambda 是编译器生成的类的对象,其行为仅类似于函数。

    对于您的实际问题:您提到没有使用customParam

    一个典型的模式是,customParam 由库的调用者提供,并在回调函数中用于标识处理回调的某些对象。在回调中,然后映射到您实际要调用的函数:

    void MyNativeCallback(void * customParam, const wchar_t * errorMessage)
    {
        static_cast<MyClass*>(customParam)->Callback(errorMessage);
    }
    

    因此,您可以将指向您的 lambda 的指针作为 customParam 传递给库,并提供一个如上所述的辅助函数作为回调。无论如何,正确获取指针类型并非易事。我更喜欢一个小的帮助类。

    【讨论】:

      猜你喜欢
      • 2015-07-04
      • 2011-07-27
      • 1970-01-01
      • 1970-01-01
      • 2020-09-09
      • 2014-10-20
      • 1970-01-01
      • 2012-05-04
      • 2019-10-29
      相关资源
      最近更新 更多