无法将适当的lambda传递给emscripten_set_main_loop

时间:2019-03-29 10:18:02

标签: c++ lambda callback emscripten

我真的很努力指示emscripten_set_main_loop执行渲染新框架的方法。我根本无法提出可以成功编译的lambda,更不用说按预期运行了。

首先,我得到了MyView类,该类应该用作Javascript的接口。想法是创建一个该类型的新对象,然后调用StartRenderingLoop,这将启动呈现新帧的循环。这应该从Javascript完成,这就是为什么我在底部得到Embind定义的原因:

class MyView
{
public:
  void StartRenderingLoop();

private:
  std::unique_ptr<Renderer> _renderer;
  void Render();
};

EMSCRIPTEN_KEEPALIVE
MyView::MyView()
{
  _renderer = std::unique_ptr<Renderer>(new Renderer());
}

void MyView::Render()
{
  _renderer->Render();
}

EMSCRIPTEN_KEEPALIVE
void MyView::StartRenderingLoop()
{
  emscripten_set_main_loop(/* should invoke MyView::Render(), but how? */, -1, 1);
}

EMSCRIPTEN_BINDINGS(MyView)
{
  class_<MyView>("MyView")
    .function("startRenderingLoop", &MyView::StartRenderingLoop)
    // everything else...
    ;
}

这是我尝试过的:

1)

emscripten_set_main_loop(&MyView::Render(), -1, 1);

这甚至无法编译。 Emscripten引发以下错误:

note: candidate function not viable: no known conversion from 'void (MyView::*)()' to 'em_callback_func' (aka 'void (*)()') for 1st argument

2)

emscripten_set_main_loop([]() { _renderer->Render(); }, -1, 1);

由于无法访问this指针而无法编译,这很公平:

error: 'this' cannot be implicitly captured in this context

3)

emscripten_set_main_loop([this]() { _renderer->Render(); }, -1, 1);

捕获this会使lambda与预期值不兼容,并引发类似于尝试1)的错误。

4)

声明全局std::function对象:

static std::function<void()> renderLoopFunction;

然后在StartRenderingLoop中执行以下操作:

EMSCRIPTEN_KEEPALIVE
void MyView::StartRenderingLoop()
{
  renderLoopFunction = [=]() mutable { int test = 3; };
  emscripten_set_main_loop(renderLoopFunction, -1, 1);
}

this之类的资源相反,它再次无法编译:

note: candidate function not viable: no known conversion from 'std::function<void ()>' to 'em_callback_func' (aka 'void (*)()') for 1st argument

我正处于一个阶段,如果一个有三个自由愿望的仙女出现,我很乐意将其中一个投资于解决这个谜团,只是让编译器停止告诉我我是一个不称职的白痴。 / p>

3 个答案:

答案 0 :(得分:1)

该接口需要类型为void (*)()的普通旧c函数指针。

假设您只打算拥有一个Render对象...

class MyView
{
public:
  void Render();

private:
  std::unique_ptr<Renderer> _renderer;
};

// a global or otherwise known globally
auto view = MyView();

// &main_loop has the signature void (*)(), which is what you need
void main_loop()
{
  view.Render();
}

int main()
{
   emscripten_set_main_loop(&main_loop, -1, 1);
}

答案 1 :(得分:1)

为了使用emscripten_set_main_loop_arg,我似乎真的不得不回到使用C的角度。因为我希望尽可能地保持面向对象的代码,所以我正在寻找一种替代方法。事实证明,该函数的第二个版本可以处理参数:emscripten_set_main_loop_arg

使用emscripten_set_main_loop_arg可以将StartRenderingLoop方法保留为实例方法,只需将this传递给Render回调即可。修改后的代码如下:

void MyView::Render()
{
  _renderer->Render();
}


void RenderLoopCallback(void* arg)
{
  static_cast<MyView*>(arg)->Render();
}


EMSCRIPTEN_KEEPALIVE
void MyView::StartRenderingLoop()
{
  emscripten_set_main_loop_arg(&RenderLoopCallback, this, -1, 1);
}

答案 2 :(得分:0)

这是我一直在做的事情:

   void do_frame(void* void_fn_ptr) {
        auto* fn_ptr = reinterpret_cast<std::function<void()>*>(void_fn_ptr);
        if (fn_ptr) {
            auto& fn = *fn_ptr;
            fn();
        }
    }


    void window::loop(std::function<void()> fn) {
        #ifdef __EMSCRIPTEN__
            emscripten_set_main_loop_arg(do_frame, &fn, 0, 1);
        #else
            while (!should_close()) {
                fn();
            }
        #endif
    }

然后:

    w.loop([&]() {
        // render stuff...
    });