LoginSignup
2
0

More than 1 year has passed since last update.

wasi-sdk + wasmer-python で C/C++ アプリを WASM でポータブルに動かすメモ

Last updated at Posted at 2022-06-05

背景

C/C++ コードを Python からポータブルな形で呼び出したい...
WASI + wasmer-python でやってみます.

malloc/new しなかったり, C++ STL 使わないような場合は Emscripten 利用も検討ください.

wasmer-python と emcc で C/C++ コードを wasm にして python でポータブルに動かすメモ(制約多い)
https://qiita.com/syoyo/items/6f0b74014e55ecf0a02b

制約

  • main を用意し, main 経由で処理しないといけない
  • C++例外や thread は使えない
  • Python 界とは直接はやりとりできず, 画像データを処理したいときなどは, ファイル経由でアクセスしないとだめっぽい(WASI の Rust runtime では stdin/stdout 上書きできるようだが, wasmer-python ではできない)

Emscripten に対しての利点

  • malloc/new とか, C++ STL(+ libc)関連がある程度使えるので, より汎用な C/C++ コードを動かすことができる

方法

まず, wasi-sdk で C/C++ コードを WASI 形式(?) の WASM にします.

#include <cstdio>
#include <cstdlib>

int main(int argc, char **argv)
{
  printf("argc %d\n", argc);
  for (size_t i = 0; i < argc; i++) {
    printf("argv[%d] = %s\n", i, argv[i]);
  }
  return 0;
}

以下のような感じでコンパイルしておきます.

/home/syoyo/work/wasi-sdk/wasi-sdk-16.0/bin/clang++ --sysroot=/home/syoyo/work/wasi-sdk/wasi-sdk-16.0/share/wasi-sysroot -fno-exceptions -o binding.wasm binding.cc

wasmer-python で実行

そのままだと wasi_snapshot_preview1fd_close とかのシンボルが見つからないエラーになります.
WASI 用のランタイムが用意されていますので, それを import するようにします.

手順としては, StateBuilder で main 実行の情報(プログラム名, 引数, 環境変数, アクセスできるフォルダ)を設定します.

StateBuilder の情報と, ランタイムは WASI のバージョンに依存するので, WASI のバージョンから import_object を作り, _start を invoke します!

from wasmer import engine, wasi, ImportObject, Function, Store, Memory, MemoryType, Module, Instance
from wasmer_compiler_cranelift import Compiler

store = Store(engine.Universal(Compiler))

wasm_bytes = open('binding.wasm', 'rb').read()

module = Module(store, wasm_bytes)
print(module)

wasi_version = wasi.get_version(module, strict=True)
print("WASI version", wasi_version)

wasi_env = \
    wasi.StateBuilder('wasi_test_program'). \
        argument('--myargs'). \
        environment('BORA', 'true'). \
        environment('DORA', 'false'). \
        map_directory('the_host_current_dir', '.'). \
        finalize()

import_object = wasi_env.generate_import_object(store, wasi_version)

instance = Instance(module, import_object)

# _start に引数を与えても反映されないので注意
result = instance.exports._start()
argc 2
argv[0] = wasi_test_program
argv[1] = --myargs

データのやりとり

stdin/stdout 使えないので, 小規模なデータであれば環境変数 or 引数で指定, 画像データなどの場合は一度ファイルに落としてやりとり, となるでしょう.
(PIPE とか使えるんじゃろか?)

複数インスタンス起動したい場合はファイル名を変えたり, map_directory で調整でしょうか.

main 返り値

wasmer-python では, 0 以外の場合は例外を出します :frowning2:

RuntimeError: WASI exited with code: 3

返り値取得したいときは以下のような感じになるでしょうか.

try:
    instance.exports._start()
except RuntimeError as e:
    print("err_code", str(e).split()[-1])

よりきちんとエラーメッセージをパースするなら parse https://pypi.org/project/parse/ を使うとよいでしょう.

おまけ. wasmtime で動かす

コマンドラインから動かせました :smile:
Python から subprocess で起動とかでもいいかも.

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0