У меня есть некоторые функции обратного вызова в C ++, я хотел бы воссоздать в javascript после компиляции с emscripten.
Кто-нибудь знает, как вызвать тех, кто использует ccall или cwrap?
Спасибо!
Техника, которую я использовал, заключалась в преобразовании указателя в unsigned int (технически, uint32_t), который я затем передал в JS. Когда я был готов сделать обратный вызов, я передал значение обратно в C ++, преобразовал его обратно в указатель функции и затем вызвал связанную функцию.
Многое из этого может быть автоматизировано; Например, вы можете настроить свой класс обратного вызова на базовый класс, в который будут преобразованы все указатели перед вызовом виртуальной функции, которая перенаправляет на правильный производный класс (в моем случае я использую это для обработки параметров и типов возврата).
Я рад помочь вам с некоторым кодом. Я работаю над проектом под названием Empirical, который будет библиотекой только для заголовков, в основном предназначенной для переноса научного программного обеспечения в Интернет. Он все еще находится в стадии активной и ранней разработки, но вы можете найти некоторые полезные части.
https://github.com/mercere99/Empirical
emp::JSWrap()
это функция, которая принимает объект std :: function и возвращает uint32_t
, Вы можете найти его определение здесь: https://github.com/mercere99/Empirical/blob/master/emtools/JSWrap.h Он должен правильно обрабатывать основные параметры и возвращаемые типы и std::string
, но я работаю, чтобы расширить это. Если вы извлекаете только некоторые файлы из проекта, обратите внимание, что JSWrap включает в себя несколько других, но не слишком много.
Другой важный файл, о котором вам нужно беспокоиться — это library_emp.js (https://github.com/mercere99/Empirical/blob/master/emtools/library_emp.js), который определяет emp.Callback()
, который может быть использован на стороне JS.
Чтобы интегрировать оба этих файла в вашу программу, вам необходимо:
#include "JSWrap.h"
а также #include "init.h"
(возможно, с дополнительной информацией о пути)--js-library ../../emtools/library_emp.js
emp::Initialize();
на стороне C ++.uint32_t fun_id = emp::JSWrap(FunctionToBeWrapped);
Он вернет значение идентификатора функции, которое вы можете передать в JS. Кроме того, вы можете позвонить uint32_t fun_id = emp::JSWrap(FunctionToBeWrapped, "JS_Function_Name");
и он создаст функцию JS для вас с указанным именем.emp.Callback(id, parameters)
или вы можете использовать предоставленное имя, если вы использовали один emp.JS_Function_Name(parameters...)
и он перезвонит оригинальной функции. Любое возвращаемое значение будет передано обратно.Позвольте мне знать, если это помогает! В верхней части JSWrap.h также есть некоторая документация и тестовые файлы на https://github.com/mercere99/Empirical/tree/master/UTests/emtools (включая Makefile, файл кода с именем JSWrap.cc и файл HTML с именем JSWrap.html).
редактироватьНиже приведен пример кода, который отправляет указатель на объект функции в JS, а затем вызывает его обратно из JS.
#include <emscripten.h>
#include <functional>
// A couple of possible callbacks, all with the same signature.
double Times2(double val) { return val * 2; }
double Plus7(double val) { return val + 7; }
// A function callback from JS that takes a callback id and an arg and does the callback.
extern "C" {
double Callback_dd(uint32_t cb_id, double val) {
auto * fun_ptr = reinterpret_cast<std::function<double(double)>*>(cb_id);
return (*fun_ptr)(val);
}
}
int main() {
// Pick the function you want to run
auto fun = std::function<double(double)>(Times2);
// auto fun = std::function<double(double)>(Plus7);
// Convert a function pointer to a uint32_t.
// Note double casting to first convert it to a number and then reduce it to 32-bits.
// Using reintepret_cast would be better, but looked confusing with the double cast.
uint32_t cb_id = (uint32_t) (long long) &fun;
// The following code passed the callback ID to JavaScript. The JS code then uses the
// ID to call back the original function.
EM_ASM_ARGS({
Callback_dd = Module.cwrap('Callback_dd', 'number', ['number']);
var x = 12.5;
alert('Result: fun(' + x + ') = ' + Callback_dd($0, x));
}, cb_id);
}
Обратите внимание, что если вы собираетесь выполнить обратный вызов после того, как main () завершится, вам нужно убедиться, что вызываемые вами функции будут сохраняться.
Чтобы скомпилировать этот код, поместите его в файл (назовем его callback_test.cc) и затем из командной строки запустите:
em++ -s EXPORTED_FUNCTIONS="['_Callback_dd', '_main']" -std=c++11 callback_test.cc -o callback_test.html
Теперь вы должны иметь возможность открыть callback_test.html в вашем веб-браузере, и он будет вызывать из JS любой указатель функции C ++, который вы ему передали.
В этом случае вам нужно знать сигнатуру функции заранее, но, как я уже упоминал выше, вы можете использовать более сложный обратный вызов, который может запомнить сигнатуру.