簡體   English   中英

將C ++函數傳遞給emscripten中的javascript函數

[英]Passing a C++ function to a javascript function in emscripten

我正在學習emscripten並試圖更好地理解它。 據我所知,用例主要是將現有的C / C ++ - 代碼移植到Web客戶端(瀏覽器)並從JavaScript調用C / C ++代碼。

但我想知道是否有可能將C ++和Emscripten用於網頁(注意:這更多是出於好奇 - 我知道目前沒有太多合理的理由)。 我設法從C ++調用Javascript函數,並將類型為string,int,double等的參數傳遞給它們。 但我缺少的是:從C ++調用Javascript函數並將C或C ++函數作為句柄傳遞。 所以作為一個簡單的例子:我如何編寫以下Javascript代碼ind純C ++?

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

TL; DR;

我寫了一個庫: js-bind ,它接受任意數量的參數來輕松完成:

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));

該庫是基於以下解釋的一些宏元編程。

詳細解答:

你有不同的可能性,比如emscripten ccall,但在我看來更容易使用的是Embind。

例如,從C ++中獲取XMLHttpRequest的綁定事件處理程序。

要啟用它,您必須編譯: - --bind -s NO_EXIT_RUNTIME=1

Emscripten:綁定獨立功能

人們可以通過獨立功能和單例輕松實現它,如下所示:

#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為例來實現:

#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;
}

我不確定emscripten,但總而言之,我明白你需要知道的是如何將C ++函數作為另一個C ++函數的句柄傳遞。 我希望我可以幫忙。

JavaScript,PHP和其他更靈活的語言允許傳遞函數對象。 在C和C ++中,它稍有不同,您必須將函數指針作為參數傳遞給其他函數。 在C中,這個名稱是Callback,而不是句柄。

例如:

/* This function takes a single callback as a parameter. */
//here we say that the parameter, that we name numberSource, is a function that receives no parameters itself (void), and return an int
void printNumber(int (*numberSource)(void)) {
    printf("%d", numberSource());
}

/* A possible callback */
int oneExampleFunction(void) {
    return 100;
}

/* Another possible callback. */
int otherExampleFunction(void) {
    return 200;
}

/* This is how we would call printNumber with three different callbacks. */

//with "&" we are referencing the memory address of the function, 
//since thats what printNumber is expecting
printNumber(&oneExampleFunction);
printNumber(&otherExampleFunction);
printNumber(&rand); //where are using rand(), a system function, that works as well. 

通常的做法是為參數創建自定義類型,因此您不需要像int (*numberSource)(void)那樣使用丑陋的東西。 它將是這樣的:

//Function pointer called CallbackType that takes a float
//and returns an int
typedef int (*NameYouWantForTheType)(void);  

所以printNumber函數是這樣的:

void printNumber(NameYouWantForTheType numberSource ) {
    printf("%d", numberSource());
}

所以,在你的情況下,如果你想翻譯這個JS代碼

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

到C,你有一個名為“document”的C對象接收一個執行其他動作的函數,你的C代碼將是:

void myfun (void) {
  /* do something meaningful here */
}

document.onload(&myfun);

這是我在C代碼中修改Emscripten時使用了很久的東西:

void myfun(void(*f)(void)) { (*f)() }

然后這將是JavaScript:

var theparty = Runtime.addFunction(function() { print("Will there be confetti?") });
Module.ccall("myfun", "number", ["number"], [theparty]);
Runtime.removeFunction(theparty); // output => Will there be confetti?

我總是刪除執行后不再需要的函數來保存內存。 這是使代碼位協同工作的簡單而無縫的方法。 顯然,您可以將其修改為除了打印信息之外的任何其他操作。 :P

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM