Как обнаружить (со стороны JS) «необнаруженное исключение», исходящее из кода C ++?
Вы должны перехватывать исключения, используя блоки try-catch
() tutorial ).
Пример:
try {
console.log( fib(14) );
}
catch ( e ) {
console.error( e );
}
Но на данный момент он распространяется как указатель, поэтому в консоли вы увидите некоторое число:
5249672
Если вы хотите получить правильное сообщение об ошибке в JS, вы должны написать привязку в своем коде C ++:
#include <emscripten/bind.h>
std::string getExceptionMessage(int eptr)
{
return reinterpret_cast<std::exception*>(eptr)->what();
}
EMSCRIPTEN_BINDINGS(getExceptionMessageBinding)
{
emscripten::function("getExceptionMessage", &getExceptionMessage);
};
Это будет показано в Код JS через объект Module
. Вы можете использовать его в JS коде так:
try {
console.log( fib(14) );
}
catch ( e ) {
console.error( Module.getExceptionMessage(e) );
}
Вывод (исключение выдается):
input out of range
Вот проблема GitHub , где это имеет обсуждалось и предлагалось .
Я скомпилировал этот код с включенными исключениями для C ++ 11 и привязок следующим образом:
~/emsdk/upstream/emscripten$ ./em++ -std=c++11 -Os -fexceptions --bind
-s WASM=1
-s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap"]'
-s DISABLE_EXCEPTION_CATCHING=0
fib.cc
Вот еще одна похожая проблема GitHub , в которой обсуждается другой подход.
Добавление функции перехвата исключений (DISABLE_EXCEPTION_CATCHING=0
), похоже, слишком сильно увеличивает размер файла.
Если вас беспокоит увеличение размера выходных файлов, вы можете полностью отключить обработку исключений и прибегнуть к проверке ошибок с недопустимыми значениями или кодами ошибок, возвращаемыми функцией (ями), например, -1, если ввод Неправильно.
Однако, вот замечание:
Размеры файлов предыдущей сборки были:
110K - a.out.js
187K - a.out.wasm
Обработка исключений и RTTI для привязок были частью этого.
Я снял код и использовал inline JS с использованием EM_ASM для выдачи JS ошибки в следующем фрагменте кода:
#include <emscripten.h>
extern "C" {
EMSCRIPTEN_KEEPALIVE
int fib(int n) {
if (n > 12) {
EM_ASM(
throw Error("out_of_range"); // JS error with EM_ASM
);
}
int a {0}, b {1};
for ( int i {0}; i < n; ++i ) {
const auto t = a + b;
a = b;
b = t;
}
return b;
}
}
Скомпилировано с отключенными исключениями:
$ ./em++ -std=c++11 -Os
-fno-exceptions
-s WASM=1
-s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap"]'
fib1.cc
Вот HTML файл (fib1.html
):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WASM Test Page</title>
</head>
<body>
<script src="a.out.js"></script>
<script>
"use strict";
Module.onRuntimeInitialized = _ => {
const fib = Module.cwrap('fib', 'number', ['number']);
try {
console.log(fib(10));
console.log(fib(14));
}
catch ( e ) {
console.error(e);
}
};
</script>
</body>
</html>
Вывод на консоль (исключение обнаружено):
89
fib1.html:21 Error: out_of_range
at Array.ASM_CONSTS (a.out.js:1)
at _emscripten_asm_const_i (a.out.js:1)
at wasm-function[1]:0x6b
at Module._fib (a.out.js:1)
at Object.Module.onRuntimeInitialized (fib1.html:18)
at doRun (a.out.js:1)
at run (a.out.js:1)
at runCaller (a.out.js:1)
at removeRunDependency (a.out.js:1)
at receiveInstance (a.out.js:1)
И размеры файлов были:
15K - a.out.js
246 - a.out.wasm (bytes)
Бросок JS Ошибка по-прежнему работает с отключенными исключениями, а размеры сгенерированных файлов намного меньше. Возможно, вы захотите изучить это больше. Возможно, создайте несколько классов, унаследованных от Error с расширенной функциональностью. Однако исключения, которые вызываются из стандартных API, таких как std::vector::at()
, не будут работать и вызывать прекращение. Таким образом, вы должны учитывать это при отключении исключений.
Как выполнить сброс / перезапуск модуля WebAssembly, сгенерированного emcc
, таким образом, чтобы избежать утечек памяти?
На данный момент нет такого API для сброса / перезапуска модуля. Сам модуль автоматически собирает мусор, когда на него больше нет ссылок. Вам не нужно заботиться об утечке памяти в этом случае. За это отвечает среда выполнения JS.
Но объект C ++, созданный кодом JS, должен быть уничтожен с использованием Module.destroy
, если он управляет ресурсами (памятью, файлом ручки, et c.). Сборщик мусора (G C) не будет вызывать деструктор, когда собирает объект, что может привести к утечке памяти / ресурсов. Вызов Module.destory
вызовет деструктор и утечек памяти не будет. Прямо сейчас у вашего вопроса нет такого объекта. Поэтому будьте внимательны, когда вы это делаете, и при необходимости вызывайте Module.destory
.
Что касается выделения / освобождения в вашем коде C ++, вы сами несете ответственность за освобождение ресурсов, которые вы выделить. Вот несколько моментов, которые могут вам в этом помочь:
Избегать Неопределенное поведение .
Следуйте правило трех / пяти / нуля религиозно.
Использование стандартных библиотек C ++ на основе RAII для автоматизированного c управления памятью, такого как std::unique_ptr
/ std::shared_ptr
вместе с std::make_unique
/ std::make_shared
.
Найдите контейнеры STL, такие как std::vector
, std::map
и др. c. для хранения и управления коллекциями, а не прибегать к написанию своих собственных. Стандартные компоненты хорошо протестированы, поэтому меньше проблем с ошибками.
Всегда обращайтесь к документации API, которые вы планируете использовать. Убедитесь, что API выделяет ресурсы, которые вам, возможно, придется освободить определенным образом после использования.
Ниже приведена информация о загрузке модуля WASM: Эффективная загрузка модулей WebAssembly