2015-07-23 4 views
0

У меня есть некоторые функции обратного вызова в C++. Я хотел бы воссоздать в javascript после компиляции с emscripten.asm.js Module.ccall/Module.cwrap callback

Кто-нибудь знает, как позвонить тем, кто использует ccall или cwrap?

Спасибо!

ответ

2

Метод, который я использовал, состоял в том, чтобы преобразовать указатель на unsigned int (технически, uint32_t), который затем передал JS. Когда я был готов выполнить обратный вызов, я передал значение обратно на C++, преобразовал его обратно в указатель функции и затем вызвал связанную функцию.

Многое из этого может быть автоматизировано; например, вы можете настроить класс обратного вызова, чтобы иметь базовый класс, для которого все указатели преобразуются до того, как вызывается виртуальная функция, которая перенаправляется на правильный производный класс (в моем случае я использую это для обработки типов параметров и возвращаемых типов).

Я рад помочь вам с кодом. Я работаю над проектом под названием «Эмпирический», который будет представлять собой библиотеку только для заголовков, в основном ориентированную на перенос научного программного обеспечения в Интернет. Он все еще находится в активном и раннем развитии, но вы можете найти некоторые полезные вещи.

https://github.com/mercere99/Empirical

emp::JSWrap() это функция, которая принимает в качестве объекта станд :: функции и возвращает 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.

Для интеграции этих двух файлов в вашей программе, вам нужно:

  • добавить на C++ кода #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 с указанным именем.
  • Из 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); 

} 

Обратите внимание, что если вы собираетесь сделать обратный вызов после того, как основные() закончились, вы должны убедиться, что функции, которые вы звонящие будут сохраняться.

Чтобы скомпилировать этот код, поместите его в файл (назовем его 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++ вы его передали.

В этом случае вам нужно знать подпись функции раньше времени, но, как я уже упоминал выше, вы можете использовать более сложный обратный вызов, который может запомнить подпись.

+0

«Техника, которую я использовал, заключалась в том, чтобы преобразовать указатель на unsigned int (технически, uint32_t), который затем передал JS». Вы делаете это на C++ перед использованием emscripten, правильно? Можете ли вы показать мне простой пример кода? Быстрый пример foo_bar. Большое вам спасибо заранее! –

+0

Хорошо - я только что изменил свой ответ выше, чтобы включить код примера внизу. –