将C ++函数传递给emscripten中的javascript函数 [英] Passing a C++ function to a javascript function in emscripten

查看:78
本文介绍了将C ++函数传递给emscripten中的javascript函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习脚本,并试图更好地理解它.据我了解,它主要用于的用例是将现有的C/C ++代码移植到Web客户端(浏览器)并从JavaScript调用C/C ++代码.

I am learning about emscripten and trying to understand it better. As far as I understand the use-case it was mostly designed for is to port existing C/C++-code to a web client (browser) and calling C/C++ code from JavaScript.

但是我想知道是否可以在网页上使用C ++和Emscripten(注意:这更多是出于好奇-我知道目前没有很多充分的理由这样做).我设法从C ++调用Javascript函数,并将类型为string,int,double等的参数传递给它们.但是我缺少的是:从C ++调用Javascript函数并将C或C ++函数作为句柄传递.举一个简单的例子:我将如何在纯C ++中编写以下Javascript代码?

But I am wondering whether it is possible to use C++ and Emscripten to web page (note: this is more out of curiosity - I know that there are not many good reasons to do that at the moment). I manage to call Javascript functions from C++ and pass arguments of types string, int, double etc to them. But what I am missing is: calling a Javascript function from C++ and passing a C or C++ function as a handle. So as a simple example: How would I write the following Javascript code ind pure C++?

var myfun = function() { /* do something meaningful here */ }
document.onload(myfun);

推荐答案

TL; DR;

我写了一个库: js-bind 可以接受任何数量的参数来做到这一点轻松地:

I wrote a library : js-bind which accepts any number of arguments to do that easily :

using namespace std::placeholders;
using emscripten::val;

// First find the HTML object to attach the event to
auto clickme_btn = val::global("document").call<val>("getElementById", string("clickme_btn"));

// Bind the event handler for click
auto onclick = [](val event){ cout << "hello world ! " << endl; };
clickme_btn.set("onclick", js::bind(onclick, _1));

此库是基于以下说明的一些宏元编程.

This library is some Macro metaprogramming based on the explanation below.

详细答案:

您有不同的可能性,例如emcallen ccall,但在我看来,更容易使用的是Embind.

You have different possibilites, like emscripten ccall, but what is easier to use in my opinion is Embind.

例如,从C ++中获取XMLHttpRequest的绑定事件处理程序.

For example take binding event handlers of an XMLHttpRequest from within C++.

要启用它,您必须使用以下命令进行编译:-bind -s NO_EXIT_RUNTIME = 1

To enable it you have to compile with : --bind -s NO_EXIT_RUNTIME=1

一个人可以通过独立功能和单例轻松实现它,如此处所示:

One could achieve it easily with freestanding functions and a singleton, as show here :

#include <iostream>

#include <emscripten.h>
#include <emscripten/bind.h>
#include <emscripten/val.h>


namespace xhr {

  inline emscripten::val& singleton() {
    using emscripten::val;
    static val instance = val::global("XMLHttpRequest").new_();
    return instance;
  }

  void on_load(emscripten::val event) { 
    std::cout << "Successful Query " << std::endl;
    std::cout << "response is : " << singleton()["responseText"].as<std::string>() << std::endl;
  }

  void on_error(emscripten::val event) {
    std::cout << "Error on query " << std::endl;
  }

  void on_progress(emscripten::val event) {
    std::cout << "Progress on query " << std::endl;

    std::cout << event["lengthComputable"].as<bool>() << ": " << event["loaded"].as<unsigned int>() / event["total"].as<unsigned int>() << std::endl;
  }


  using namespace emscripten;

  EMSCRIPTEN_BINDINGS(xhr) {
    function("on_load", &on_load);
    function("on_error", &on_error);
    function("on_progress", &on_progress);
  }

}


int main(int argc, char** argv) {

  using emscripten::val;

  xhr::singleton().call<val>("open", std::string("GET"), std::string("http://127.0.0.1:8080/CMakeCache.txt"), true);

  // Here I set the callback to &on_load function registered via the EMSCRIPTEN_BINDINGS macro.
  xhr::singleton().set("onload",val::module_property("on_load"));
  xhr::singleton().set("onprogress",val::module_property("on_progress"));  
  xhr::singleton().set("onerror",val::module_property("on_error"));  

  xhr::singleton().call<val>("send");


  return 0;
}

Emscripten:绑定成员函数

通常在C ++中,我们习惯于std :: bind回调.以xhr为例,这也可以以一种更简洁的方式实现:

Emscripten : bind member functions

Typically in C++ we are used to std::bind callbacks. This can also be achieved, taking the example of xhr in a cleaner way :

#include <iostream>
#include <functional>
#include <memory>

#include <emscripten.h>
#include <emscripten/bind.h>
#include <emscripten/val.h>

class MiniXhr : public std::enable_shared_from_this<MiniXhr> {
  using val = emscripten::val;
  using url_t = std::string;

  public:

    void set_url(const url_t& url) { url_ = url; }

    void GET();

    /**
     *
     * The member function to be called from javascript.
     */
    void on_readystate(val event) {
      std::cout << "ready " << std::endl;
      std::cout << "xxhr::on_readystate: " 
          << xhr["readyState"].as<size_t>() << " - " << url_ << " :: "
          << xhr["status"].as<size_t>() << ": " 
          << xhr["statusText"].as<std::string>() << std::endl;
    }

  private:
    url_t url_;
    val xhr = val::global("XMLHttpRequest").new_();
};


using emscripten::class_;
EMSCRIPTEN_BINDINGS(MiniXhr) {

  /**
   * Binding for the class.
   */
  class_<MiniXhr>("MiniXhr")
    .smart_ptr<std::shared_ptr<MiniXhr>>("shared_ptr<MiniXhr>")
    .function("on_readystate", &MiniXhr::on_readystate)
    ;

  /**
   * More generic binding to bind a functor with one argument (event handler get the event)
   * Here std::function call operator from C++ is bound to function opcall() in JS.
   */
  class_<std::function<void(emscripten::val)>>("VoidValFunctor")
    .constructor<>()
    .function("opcall", &std::function<void(emscripten::val)>::operator());


}

/**
 *
 * Finally the interesting part : binding the member function on_readystate to the readystatechange event of XMLHttpRequest.
 *
 */

 void MiniXhr::GET() { 

  /**
   * Here this lambda could be put as function in a library, to do an JS(std::bind), 
   * it should just be overloaded for different argument count. (Im on it).
   */
  auto jsbind = [](val& target, const char* property, auto bind_expression ) {

    // Create an std::function from the bind expression
    std::function<void(emscripten::val)> functor = bind_expression;

    // We ensure the correct object will always be bound to the this of the function
    auto functor_adapter = val(functor)["opcall"].call<val>("bind", val(functor)); 

    // Finally we simply set the eventhandler
    target.set(property, functor_adapter);
  };

  // Here we could bind as many member function as we want.

//    jsbind(xhr, "onload", std::bind(&MiniXhr::on_load, shared_from_this(), std::placeholders::_1));
//    jsbind(xhr, "onerror", std::bind(&MiniXhr::on_error, shared_from_this(), std::placeholders::_1));
//    jsbind(xhr, "onprogress", std::bind(&MiniXhr::on_progress, shared_from_this(), std::placeholders::_1));
  jsbind(xhr, "onreadystatechange", std::bind(&MiniXhr::on_readystate, shared_from_this(), std::placeholders::_1));

  // Note that we bind with shared_from_this(), as the scope where the class was instantiated may be dead
  // and only later our callback will come back.

 xhr.call<val>("open", std::string("GET"), url_, true);
 xhr.call<val>("send");
}


int main(int argc, char** argv) {


  auto x = std::make_shared<MiniXhr>();
  x->set_url("notfound.json");
  x->GET();

  return 0;
}

这篇关于将C ++函数传递给emscripten中的javascript函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆