React+TypeScriptで、TsxからWebAssembly呼び出し

2024/01/13に公開

Abstract

てっきり、JavaScriptからしかWebAssembly呼び出しできんのかと思っとったら、何のことない。App.tsxから呼べるやんか。前回のめんどくさいの何やったとっていうことで、App.tsxからWebAssemnbly呼び出してみた。

結論

今回の成果物はココ↓
https://github.com/aaaa1597/ReactTs-WebAsm003

前提

ざっくり手順

  1. cppファイル生成 -> ビルド
  2. ビルドでできたwasmファイルをpublic配下にコピー
  3. App.tsxで呼び出す。
  4. 実行

手順詳細

  1. cppファイル生成 -> ビルド
rm React-Ts-Template ReactTs-WebAsm003
cd ReactTs-WebAsm003/src     # プロジェクトフォルダに移動
mkdir asm-cpp                # jsモジュール置き場を作成
type nul > asm-cpp/hello.cpp # hello.cppを自分で生成
hello.cppの中身
#include <stdio.h>
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#else
#define EMSCRIPTEN_KEEPALIVE
#endif

extern "C" {
int EMSCRIPTEN_KEEPALIVE add(int a, int b){
  return a + b;
}

int EMSCRIPTEN_KEEPALIVE sub(int a, int b){
  return a - b;
}
} /* extern "C" */

で、ビルド

コマントプロンプト
<emsdkをインストールしたフォルダ>emsdk\emsdk_env.bat  # <- コマントプロンプト起動の度に毎回実行。
emcc asem-cpp/hello.cpp -o asem-cpp/hello.js -sWASM=1 -sEXIT_RUNTIME=1 -sEXPORTED_RUNTIME_METHODS=ccall,cwrap

成功したら、フォルダ構成は以下の様になる。

$ プロジェクトフォルダ
~略~
└─src
    │  ~略~
    └─asm-cpp
	    hello.cpp
	    hello.js      # <- 使わない。捨ててよし。
	    hello.wasm    # <- ビルドで生成される
  1. ビルドでできたhello.wasmをpublic配下にコピー
コマントプロンプト
$ プロジェクトフォルダ
~略~
├─public
│  └─wasm
│     hello.wasm     # <- ここにコピー
│  ~略~
│   └─asm-cpp
│      hello.wasm    # <- これを、
  1. App.tsxで呼び出す。
helloif.js(wasmを呼び出すためのインターフェース)
-import React from 'react';
+import React, { useEffect, useState } from 'react';
import './App.css';

function App() {
+  const [count, setCount] = useState(0)

+ const getFromAsm = () => {
+   WebAssembly.instantiateStreaming(fetch("wasm/hello.wasm")).then(
+     (obj) => {
+       console.log('----------001')
+	const ret1 = (obj.instance.exports._Z3addii as CallableFunction)(11, 22);
+	console.log('retretret=', ret1);
+	setCount(ret1)
+	const ret2 = (obj.instance.exports._Z3subii as CallableFunction)(11, 22);
+	console.log('retretret=', ret2);
+	console.log('----------002')
+	return ret1
+      }
+    );
+  }

  return (
    <div className="App">
      hello world!!
+     <button onClick={() => getFromAsm()}> Click {count} times</button>
    </div>
  );
}
export default App;
  1. 実行

    出来た!!

前回より、こっちの方がスッキリ。全然、分かりやすくなった。

Discussion