JavaScript から C++ の関数を呼び出す
C++ の関数呼び出しについては、検索すると参考になる記事も多くここで特筆すべき内容も特にないので、ファイルのみ掲載している。
C:\wasm\project\03-call-cpp-func\.vscode\launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "localhost:8080",
"url": "http://localhost:8080/",
}
]
}
C:\wasm\project\03-call-cpp-func\CMakePresets.json
{
"version": 5,
"cmakeMinimumRequired": {
"major": 3,
"minor": 20,
"patch": 0
},
"configurePresets": [
{
"name": "wasm-config-debug",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build",
"cacheVariables": {
"CMAKE_CXX_STANDARD": "17",
"CMAKE_CXX_STANDARD_REQUIRED": "ON",
"CMAKE_CXX_EXTENSIONS": "OFF",
"CMAKE_TOOLCHAIN_FILE": "$env{EMSDK}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
"CMAKE_BUILD_TYPE": "Debug"
}
}
],
"buildPresets": [
{
"name": "wasm-build-debug",
"configurePreset": "wasm-config-debug",
"verbose": true
}
]
}
C:\wasm\project\03-call-cpp-func\CMakeLists.txt
C++ のソースコードに main() が無いので、--no-entry としている。
cmake_minimum_required(VERSION 3.20)
project(03_call_cpp_func LANGUAGES CXX)
add_executable(03_call_cpp_func func.cpp)
target_link_options(03_call_cpp_func PRIVATE
"-gsource-map"
"-sASSERTIONS=1"
"-sSAFE_HEAP=1"
"--no-entry"
"-sEXPORTED_FUNCTIONS=_malloc,_free"
"-sEXPORTED_RUNTIME_METHODS=wasmExports,ccall,stringToUTF8OnStack,UTF8ToString,HEAP32"
)
C:\wasm\project\03-call-cpp-func\index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8"/>
<script src="build/03_call_cpp_func.js"></script>
<script>
const cpp_func_example = () =>
{
let v = 0;
let s = '';
// 数値を扱う関数: ccall による呼び出し
v = Module.ccall('increment', 'number', ['number'], [v]);
console.log(`return=${v}`);
// 数値を扱う関数: wasmExports を利用した呼び出し
v = Module.wasmExports.increment(v);
console.log(`return=${v}`);
// 数値を扱う関数: 直接呼出し
v = Module._increment(v);
console.log(`return=${v}`);
// 文字列を扱う関数: ccall による呼び出し
v = Module.ccall('str_repeat', 'number', ['string', 'number'], ['HELLO1 ', 3]);
s = Module.UTF8ToString(v);
Module._free(v);
console.log(`return=${s}`);
// 文字列を扱う関数: wasmExports を利用した呼び出し
v = Module.stringToUTF8OnStack('HELLO2 ');
v = Module.wasmExports.str_repeat(v, 3);
s = Module.UTF8ToString(v);
Module._free(v);
console.log(`return=${s}`);
// 数値配列を扱う関数
const js_nums = Array.from({ length: 10 }, (_, i) => i + 1);
const intptr = Module._malloc(js_nums.length * Module.HEAP32.BYTES_PER_ELEMENT);
Module.HEAP32.set(js_nums, intptr / Module.HEAP32.BYTES_PER_ELEMENT);
v = Module.wasmExports.sum(intptr, js_nums.length);
console.log(`return=${v}`);
Module._free(intptr);
};
Module.onRuntimeInitialized = () =>
{
cpp_func_example();
};
</script>
</head>
<body>
</body>
</html>
C:\wasm\project\03-call-cpp-func\func.cpp
// UTF-8N CRLF
#include <emscripten.h>
#include <cstdio>
#include <cstring>
#include <numeric>
extern "C"
EMSCRIPTEN_KEEPALIVE
int increment(int v)
{
printf("-- increment(%d)\n", v);
return v + 1;
}
extern "C"
EMSCRIPTEN_KEEPALIVE
char* str_repeat(const char* str, int num)
{
printf("-- str_repeat(%s, %d)\n", str, num);
char* ret = (char*)malloc(strlen(str) * num + 1);
ret[0] = '\0';
for (int i=0; i<num; ++i) {
strcat(ret, str);
}
return ret;
}
extern "C"
EMSCRIPTEN_KEEPALIVE
int sum(const int* nums, int len)
{
printf("-- sum(%d..%d)\n", nums[0], nums[len - 1]);
return std::accumulate(nums, nums + len, 0);
}
// EOF
実行結果
前の例 と同じようにデバッグを実行するとコンソールには以下のような表示が行われる
-- increment(0)
return=1
-- increment(1)
return=2
-- increment(2)
return=3
-- str_repeat(HELLO1 , 3)
return=HELLO1 HELLO1 HELLO1
-- str_repeat(HELLO2 , 3)
return=HELLO2 HELLO2 HELLO2
-- sum(1..10)
return=55
次回 は静的ライブラリを作成する。