【问题标题】:Passing JS function to Emscripten-generated code将 JS 函数传递给 Emscripten 生成的代码
【发布时间】:2012-09-03 17:51:21
【问题描述】:

我有一段 C++ 代码通过 Emscripten 转换为 JavaScript。我希望转换后的 C++ 代码回调调用它的 JavaScript 代码。比如:

JavaScript:

function callback(message) {
    alert(message);
}

ccall("my_c_function", ..., callback);

C++:

void my_c_function(whatever_type_t *callback) {
    callback("Hello World!");
}

这有可能吗?

【问题讨论】:

    标签: javascript emscripten


    【解决方案1】:

    我认为接受的答案有点过时了。

    请参考this bullet point in the "Interacting with code" emscripten tutorial

    例如 C:

    void invoke_function_pointer(void(*f)(void)) {
      (*f)();
    }
    

    JS:

    var pointer = Runtime.addFunction(function() { 
      console.log('I was called from C world!'); 
    });
    Module.ccall('invoke_function_pointer', 'number', ['number'], [pointer]);
    Runtime.removeFunction(pointer);
    

    这样 C 代码就不需要知道它被转译为 JS 并且所需的任何桥都可以完全由 JS 控制。

    (代码被侵入到消息编辑器中;可能包含错误)

    【讨论】:

    • 重要的是同时有效的函数指针的数量是固定的,由emcc ... -s RESERVED_FUNCTION_POINTERS=20 ...指定
    【解决方案2】:

    通过embind 有一种新方法可以满足您的要求。

    考虑以下一段 C++ 代码。

    #include <emscripten/bind.h>
    using namespace emscripten;
    
    void cbTest(emscripten::val cb)
    {
        cb();
    }
    
    EMSCRIPTEN_BINDINGS(my_module) {
        function("cbTest", &cbTest);
    }
    

    cbTest C++ 函数接受emscripten::val。这可以是任何类型的对象。对我们来说,这是一个函数对象。 这就是你从 JS 调用它的方式

    var cbFunc = function() {
        console.log("Hi, this is a cb");
    }
    
    Module.cbTest(cbFunc);
    

    P.S 这个api还在建设中。

    【讨论】:

      【解决方案3】:

      在 Emscripten 中经常做的事情是将强类型映射到简单类型。

      JS:

      function callback(message) {
          alert(message);
      }
      
      var func_map = {
          0: callback
      };
      
      // C/C++ functions get a _ prefix added
      function _invoke_callback(callback_id, text_ptr) {
          func_map[callback_id](Pointer_stringify(text_ptr));
      }
      
      ccall("my_c_function", ..., 0);
      

      C++:

      // In C/C++ you only need to declare the func signature and
      // make sure C is used to prevent name mangling
      extern "C" void invoke_callback(int callback_id, const char* text);
      
      void my_c_function(int callback_id) {
          invoke_callback( callback_id, "Hello World!" );
      }
      

      当然,您可以添加一些胶水代码,这样就可以无缝衔接。

      【讨论】:

      【解决方案4】:

      我需要写一些与问题中描述的内容非常相似的东西。我的代码最终看起来像这样:

      C:

      void call(void (*back)(char*)){
          back("Hello!");
      }
      

      JS:

      function back(text){
          alert(Pointer_stringify(text));
      }
      var pointer = Runtime.addFunction(back);
      var call = Module.cwrap('call', 'void', ['pointer']);
      call(pointer);
      Runtime.removeFunction(pointer);
      

      请注意,返回到回调的指针必须使用 Pointer_stringify 取消引用。

      你可以在 GitHub 上找到这样的example code

      【讨论】:

      • 该链接几乎没有提供其他信息。
      • 使用 UTF8ToString 而不是 Pointer_stringify,因为后者已被删除。
      【解决方案5】:

      以下是我从几篇帖子和查看 Emscripten 捆绑代码中收集到的信息:

      在 C++ 中:

      #include <iostream>
      #include <functional>
      
      extern "C" {
        void registerCallback(void(*back)(const char*));
        void triggerCallback(char* message); // for invoking it from JS, just for this example
      }
      
      // global
      std::function<void(const char*)> gCallback;
      
      void registerCallback(void(*back)(const char*)){
          gCallback = back;
      }
      
      void triggerCallback(char* message){
        if (gCallback) {
          gCallback(message);
        } else {
          std::cerr << "Cannot pass '"<< message <<"' to undefined callback\n";
        }
      }
      

      其他帖子中缺少的重要一点是使用 RESERVED_FUNCTION_POINTERS=... 标志编译 C++,例如:

      em++ -std=c++11 -s RESERVED_FUNCTION_POINTERS=20 source.cpp -s EXPORTED_FUNCTIONS="['_registerCallback','_triggerCallback']" -o try.html
      

      将try.html加载到浏览器后,可以在其控制台中执行以下JS代码:

      // Register a callback function
      function callback(text){ alert("In JS: "+Pointer_stringify(text)); }
      var cb = Runtime.addFunction(callback);
      _registerCallback(cb);
      
      // Invoke it with some "C string"
      var jsStr = "XOXOXO";
      var cStr = allocate(intArrayFromString(jsStr), 'i8', ALLOC_NORMAL)
      _triggerCallback(cStr);
      
      // Free Emscripten heap and release the function pointer
      _free(cStr);
      Runtime.removeFunction(cb);
      

      您应该会看到一条带有“In JS: XOXOXO”的警报。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-12-23
        • 2018-04-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-22
        • 2017-05-07
        • 1970-01-01
        相关资源
        最近更新 更多