从lambda到模板函数指针

时间:2017-08-31 13:50:23

标签: c++

我正在包装C库并希望允许使用回调函数指针的参数列表来确定如何在异步调用的另一端转换void*。问题是我无法弄清楚如何从lambda trampoline中访问模板化的回调。下面是带有注释的示例代码,其中发生编译器错误,但无效代码应该提供我想要的内容。

// Hypothetical asynchronous C library call
typedef void (*future_cb)(void* data);
void call_in_the_future(future_cb cb, void* data) {
  cb(data);
}

struct Mine { /* For sake of the example */ };

struct Wrapper {
  Wrapper() : ptr_(nullptr) { }

  template <typename DataType>
  void run(void (*cb)(Wrapper* wrap, DataType other), DataType ptr) {
    auto lcb = [](void* data) {
      Wrapper* wrap = static_cast<Wrapper*>(data);
      DataType other = static_cast<DataType>(wrap->ptr_);

      /***** Compiler error here *****/
      cb(wrap, other);
    };

    ptr_ = ptr;

    // Call the hypothetical asynchronous C library function.
    call_in_the_future(lcb, this);
  }

  void* ptr_;
};

// Looking to store each type and cast them for this callback.
static void wrapper_cb(Wrapper* wrap, Mine* me) { }

int main() {
  Mine* me = new Mine();
  Wrapper* wrap = new Wrapper();
  wrap->run(wrapper_cb, me);
}

以下是构建错误:

main5.cc:19:7: error: variable 'cb' cannot be implicitly captured in a lambda with no capture-default specified
      cb(wrap, other);
      ^
main5.cc:13:19: note: 'cb' declared here
  void run(void (*cb)(Wrapper* wrap, DataType other), DataType ptr) {
                  ^
main5.cc:14:16: note: lambda expression begins here
    auto lcb = [](void* data) {
               ^
main5.cc:19:7: error: variable 'cb' cannot be implicitly captured in a lambda with no capture-default specified
      cb(wrap, other);
      ^
main5.cc:37:9: note: in instantiation of function template specialization 'Wrapper::run<Mine *>' requested here
  wrap->run(wrapper_cb, me);
        ^
main5.cc:13:19: note: 'cb' declared here
  void run(void (*cb)(Wrapper* wrap, DataType other), DataType ptr) {
                  ^
main5.cc:14:16: note: lambda expression begins here
    auto lcb = [](void* data) {
               ^
2 errors generated.

编辑:如果我尝试在lambda中捕获cb,则会出现编译错误:

error: no matching function for call to 'call_in_the_future'

编辑2:为了说清楚,我理解为什么这两个构建错误正在发生。我想知道的是,如果有解决这些问题的方法,我可以致电cb

2 个答案:

答案 0 :(得分:2)

您的捕获列表为空。 cb未传递给lambda。 所以它无法使用。

auto lcb = [](void* data) {
  Wrapper* wrap = static_cast<Wrapper*>(data);
  DataType other = static_cast<DataType>(wrap->ptr_);

  /***** Compiler error here *****/
  cb(wrap, other);
};

Lambda expressions

发生下一个错误,因为只有一个不捕获任何内容的lambda可以用作标准函数调用。

C++ lambda with captures as a function pointer Passing lambda as function pointer

答案 1 :(得分:1)

您还必须将回调存储在包装器中,例如:

struct Wrapper {
    template <typename DataType>
    void run(void (*cb)(Wrapper*, DataType*), DataType* ptr) {
        auto lcb = [](void* userdata) {
            Wrapper* wrap = static_cast<Wrapper*>(userdata);
            auto* cb = reinterpret_cast<void(*)(Wrapper*, DataType*)>(wrap->cb_);
            DataType* other = static_cast<DataType*>(wrap->ptr_);

            cb(wrap, other);
        };

        cb_ = reinterpret_cast<void(*)()>(cb);
        ptr_ = ptr;

        // Call the hypothetical asynchronous C library function.
        call_in_the_future(lcb, this);
    }

    void (*cb_)() = nullptr; // Cannot use void* to erase type of function pointer
    void* ptr_ = nullptr;
};

或没有强制转换的回调:

class Wrapper {
private:
    class IFunc
    {
    public:
        virtual ~IFunc() =  default;
        virtual void Call() const = 0;
    };

    template <typename DataType>
    struct Func : IFunc
    {
        Func(void (*cb)(Wrapper*, DataType*), DataType* ptr, Wrapper* wrapper) :
            cb(cb), ptr(ptr), wrap(wrapper) {}

        void Call() const override { cb(wrap, ptr); }

        static void CallFromVoidP(void* that)
        {
            static_cast<Func*>(that)->Call();
        }

        void (*cb)(Wrapper*, DataType*);
        DataType* ptr;
        Wrapper* wrap;
    };

public:
    template <typename DataType>
    void run(void (*cb)(Wrapper*, DataType*), DataType* ptr) {
        func = std::make_unique<Func<DataType>>(cb, ptr, this);

        // Call the hypothetical asynchronous C library function.
        call_in_the_future(&Func<DataType>::CallFromVoidP, func.get());
    }

    std::unique_ptr<IFunc> func;
};