C++和Rust通過wasmtime實現相互調用實例 1 wasmtime介紹 wasmtime是一個可以運行WebAssembly代碼的運行時環境。 WebAssembly是一種可移植的二進位指令集格式,其本身與平臺無關,類似於Java的class文件位元組碼。 WebAssembly本來的設計初衷 ...
C++和Rust通過wasmtime實現相互調用實例
1 wasmtime介紹
wasmtime是一個可以運行WebAssembly代碼的運行時環境。
WebAssembly是一種可移植的二進位指令集格式,其本身與平臺無關,類似於Java的class文件位元組碼。
WebAssembly本來的設計初衷是想讓瀏覽器可以運行C語言這種編譯型語言的代碼。通常我們的C語言代碼會使用gcc或clang等編譯器直接編譯鏈接成與平臺相關的二進位可執行文件,這種與平臺相關的二進位文件瀏覽器是無法直接運行的。如果想讓瀏覽器運行C語言代碼,就需要使用可將C語言編譯成WebAssembly指令的編譯器,編譯好的代碼是wasm格式。然後就可以使用各種wasm運行時來執行wasm代碼,這就類似於JVM虛擬機執行class文件。
由於指令集和運行時環境本身與web場景並不綁定,因此隨著後來的發展,WebAssembly指令集出現了可以脫離瀏覽器的獨立運行時環境,WebAssembly的用途也變得更加廣泛。
相比於瀏覽器的運行時,wasmtime是一個獨立運行時環境,它可以脫離Web環境來執行wasm代碼。它本身提供了命令行工具和API兩種方式來執行wasm代碼。本文主要介紹如何使用API方式來運行wasm代碼。
2 wasmtime安裝
2.1 wasmtime-cli安裝
wasmtime-cli包含wasmtime
命令,可以讓我們直接在shell中運行wasm
格式的代碼。我們這裡安裝wasmtime主要是為了測試方便。
-
在shell中執行如下命令
curl https://wasmtime.dev/install.sh -sSf | bash
-
wasmtime的可執行文件會被安裝在
${HOME}/.wasmtime
目錄下 -
運行以上命令後會在
${HOME}/.bashrc
或${HOME}/.bash_profile
文件中幫我們添加以下環境變數export WASMTIME_HOME="${HOME}/.wasmtime" export PATH="$WASMTIME_HOME/bin:$PATH"
-
如果希望所有用戶(包括root)可以使用
wasmtime
命令,可以將以上環境變數設置到/etc/profile.d
中,我們可以在該目錄下創建wasmtime.sh
文件,並添加一下代碼export WASMTIME_HOME=/home/<xxx>/.wasmtime # 將xxx替換成自己的home目錄 export PATH="$WASMTIME_HOME/bin:$PATH"
-
可以使用如下命令直接運行
wasm
文件wasmtime hello.wasm
2.2 wasmtime庫安裝
如果想在代碼中載入wasm
文件並運行其中的代碼,我們需要為我們使用的語言安裝wasmtime庫。註意這裡的wasmtime庫是為了讓我們從代碼中能夠載入wasm文件併在wasmtime運行時中運行。wasmtime並不是wasm編譯器,不能將C++或Rust代碼編譯成wasm文件,如果我們想將其他語言編譯成wasm代碼,需要下載各個語言自己的wasm編譯器,具體安裝方式在本文第3節。
目前wasmtime支持的語言有:
- Rust
- C
- C++
- Python
- .NET
- Go
我們這裡以Rust和C++為例介紹如何安裝wasmtime庫
Rust
在Rust中使用wasmtime庫非常簡單,我們只需要在Cargo.toml
配置文件中添加如下依賴
[dependencies]
wasmtime = "12.0.2"
C++
wasmtime的C++庫需要我們引入wasmtime-cpp這個項目,wasmtime-cpp依賴wasmtime的C API,因此需要先安裝C API。
-
可以在wasmtime的release中找到尾碼為
-c-api
的包,比如我們安裝的平臺是x86_64-linux
,那麼我們可以下載如下文件wget https://github.com/bytecodealliance/wasmtime/releases/download/v12.0.2/wasmtime-v12.0.2-x86_64-linux-c-api.tar.xz
-
解壓以上文件並將其移動到
/usr/local
目錄下tar -xvf wasmtime-v12.0.2-x86_64-linux-c-api.tar.xz sudo mv ./wasmtime-v12.0.2-x86_64-linux-c-api /usr/local/wasmtime
-
在
/etc/profile.d/wasmtime.sh
中添加環境變數export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/wasmtime/lib export LIBRARY_PATH=$LIBRARY_PATH:/usr/local/wasmtime/lib export C_INCLUDE_PATH=$C_INCLUDE_PATH:/usr/local/wasmtime/include export CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:/usr/local/wasmtime/include
-
下載wasmtime-cpp項目的include/wasmtime.hh文件,將其放到
wasmtime.h
所在的目錄下,按照我們的安裝步驟,需要放置到/usr/local/wasmtime/include
目錄下 -
如此就可以在我們的C++項目中引入wasmtime庫了
#include <wasmtime.hh>
3 wasm編譯器安裝
Rust
安裝
Rust語言的編譯器目前其實是一個LLVM的編譯前端,它將代碼編譯成LLVM IR,然後經過LLVM編譯成相應的目標平臺代碼。
因此我們並不需要替換Rust語言本身的編譯器,只需要在編譯時設置目標平臺為wasm即可。我們在安裝rust時,通常只會安裝本機平臺支持的目標,因此我們需要先安裝wasm目標。
# 列出所有可安裝的target列表
rustup target list
使用上面的命令後可以看到很多可以安裝的target列表,其中已經安裝的target後面會有(installed)
標示。註意到其中有3個wasm相關的target。
wasm32-unknown-emscripten
wasm32-unknown-unknown
wasm32-wasi
wasm32-unknown-emscripten
:這個target是為了在Emscripten工具鏈下編譯Wasm。Emscripten是一個將C/C++代碼編譯為Wasm和JavaScript的工具鏈。使用這個target,你可以在瀏覽器環境中運行編譯後的Wasm代碼。wasm32-unknown-unknown
:這個target是為了在沒有任何操作系統支持的情況下運行WebAssembly代碼而設計的。這種情況下,WebAssembly代碼將運行在一個“裸機”環境中,沒有任何操作系統提供的支持。因此,如果你需要在裸機環境中運行WebAssembly代碼,那麼使用這個target是一個不錯的選擇。wasm32-wasi
:這個target是為了在WebAssembly System Interface (WASI)上運行WebAssembly代碼而設計的。WASI是一個標準介面,它提供了一些操作系統級別的功能,如文件系統和網路訪問等。因此,如果你需要在WebAssembly中訪問這些操作系統級別的功能,那麼使用這個target是一個不錯的選擇。
由於我們不需要在Web環境中運行Rust代碼,因此我們選擇安裝wasm32-unknown-unknown
和wasm32-wasi
兩個目標。運行以下兩條指令,將這兩個目標平臺加入到當前使用的Rust工具鏈中。
rustup target add wasm32-unknown-unknown
rustup target add wasm32-wasi
使用
當我們需要將一個Rust項目編譯成wasm時,可以選擇執行如下的兩種編譯命令
# 在項目根目錄執行
cargo build --target wasm32-unknown-unknown # 將在target/wasm32-unknown-unknown目錄中生成build中間結果和wasm文件
# 或者執行
cargo build --target wasm32-wasi # 將在target/wasm32-wasi目錄中生成build中間結果和wasm文件
C++
安裝
目前,要將C++項目編譯成WebAssembly,最常用的工具鏈是emscripten。emscripten支持將C,C++或任何使用了LLVM的語言編譯成瀏覽器,Node.js或wasm運行時可以運行的代碼。
Emscripten is a complete compiler toolchain to WebAssembly, using LLVM, with a special focus on speed, size, and the Web platform.
WebAssembly目前支持兩種標準API:
- Web APIs
- WASI APIs
Emscripten對JavaScript API做了重構,將其包裝在與WASI介面一樣的API中,然後Emscripten在編譯代碼時,將儘可能的使用WASI APIs,以此來避免不必要的API差異。因此Emscripten編譯出來的wasm文件大部分時候可以同時運行在Web和非Web環境中。
使用如下命令下載emsdk
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
使用如下命令安裝最新的工具
git pull
./emsdk install latest
./emsdk activate latest
如果臨時將emsdk的工具目錄加入環境變數,可以運行
source ./emsdk_env.sh
或者可以在/etc/profile.d
目錄中創建emsdk.sh
文件,並加入如下環境變數的配置,需要將<emsdk_installed_dir>
替換為emsdk
所在的目錄。
export PATH=$PATH:<emsdk_installed_dir>/emsdk:<emsdk_installed_dir>/emsdk/node/16.20.0_64bit/bin:<emsdk_installed_dir>/emsdk/upstream/emscripten
export EMSDK=<emsdk_installed_dir>/emsdk
export EMSDK_NODE=<emsdk_installed_dir>/emsdk/node/16.20.0_64bit/bin/node
使用如下命令測試是否安裝成功,如果輸出下麵的信息,說明我們已經可以正常使用emscripten的工具鏈。
> emcc -v
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.45 (ef3e4e3b044de98e1811546e0bc605c65d3412f4)
clang version 18.0.0 (https://github.com/llvm/llvm-project d1e685df45dc5944b43d2547d0138cd4a3ee4efe)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: <emsdk_installed_dir>/emsdk/upstream/bin
使用
由於我們不使用Web運行時,下麵將只介紹將C或C++代碼編譯成獨立wasm二進位文件的使用方法。
- 簡單使用
emcc -O3 hello.cpp -o hello.wasm
當我們將輸出目標的尾碼名指定為wasm
時,編譯器會自動幫我們設置如下連接選項,上面的命令與下麵的命令時等價的
emcc -O3 hello.cpp -o hello.wasm -s STANDALONE_WASM
這樣編譯出來的結果不會包含js
文件,只會包含一個可被wasmtime
運行的wasm
文件。
- 結合cmake使用
更常用的方式通常是將整個C++項目編譯成wasm,因此我們需要將工具鏈與cmake結合來構建整個項目。
假設我們有一個cmake項目有如下項目結構
hello_project
|-hello.cpp
|-CMakeLists.txt
其中hello.cpp
中有如下代碼
#include <stdio.h>
int main() {
printf("hello, world!\n");
return 0;
}
CMakeLists.txt
應該按照下麵的方式進行改寫
cmake_minimum_required(VERSION 3.26)
project(hello_project)
add_definitions(-std=c++17)
set(CMAKE_CXX_STANDARD 17)
if (DEFINED EMSCRIPTEN)
add_executable(hello hello.cpp)
set(CMAKE_EXECUTABLE_SUFFIX ".wasm")
set_target_properties(foo PROPERTIES COMPILE_FLAGS "-Os")
set_target_properties(foo PROPERTIES LINK_FLAGS "-Os -s WASM=1 -s STANDALONE_WASM")
else()
add_executable(hello hello.cpp)
endif ()
以上CMakeLists.txt
表示,當我們使用emscripten
工具鏈進行編譯時,將輸出.wasm
文件,且添加對應的編譯和連接選項。當我們使用其他工具鏈編譯時,將直接輸出對應平臺的可執行文件。
按照上面的方式寫好CMakeLists.txt
後,需要使用以下命令來執行編譯的過程
# 在項目根目錄下
mkdir build
cd build
# 執行emcmake命令會幫我們自動配置cmake中指定的工具鏈為emscripten的工具鏈,這樣就確保了使用的編譯工具為emcc或em++,同時使用的標準庫更改為emscripten提供的標準庫
emcmake cmake ..
# 再執行make進行編譯,編譯後可以發現build目錄中生成了hello.wasm文件
make
使用wasmtime-cli運行hello.wasm
文件
> wasmtime hello.wasm
hello, world!
4 小試牛刀
實驗場景
需要測試Rust代碼被編譯成wasm,C++代碼被編譯成wasm,在wasmtime中正確運行。其中C++代碼可以調用Rust代碼中的函數,然後外部可以調用C++代碼中的函數。
- Rust項目:包含一個
add
函數,做兩個整數的加法並返回結果,可以被外部調用。需要編譯成wasm。 - C++項目:包含一個
foo
函數,調用Rust中的add
函數並返回結果。需要編譯成wasm。 - wasmtime項目:需要載入前面兩個項目生成的wasm文件,並運行
foo
函數,看是否能獲取正確的結果。
Rust項目編譯成wasm
創建一個項目叫做demo-rust-wasmtime
cargo new demo-rust-wasmtime --lib
創建好的項目結構如下
demo-rust-wasmtime
├── Cargo.lock
├── Cargo.toml
└── src
└── lib.rs
首先需要在Cargo.toml
中配置生成的庫為cdylib
,這表示按照C語言的FFI來生成動態庫,要想不同語言之間能夠互相調用對方的函數,通常需要將不同的語言按照相同的FFI來進行編譯,確保函數調用的方式是相同的。這裡同時我們將Rust項目的名稱修改為calc
。
[package]
name = "calc"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib"]
[dependencies]
在lib.rs
中實現我們需要的add
函數
#[no_mangle]
pub extern "C" fn add(left: i32, right: i32) -> i32 {
left + right
}
這裡有兩個地方需要註意:
#[no_mangle]
會通知Rust編譯器,其後面的函數編譯時名字不要進行混淆,確保使用add
這個名稱進行鏈接時能找到正確的函數。extern "C"
表示編譯器需要確保函數在編譯時使用與C語言相同的調用約定(ABI),從而使得函數可以與C語言代碼無縫地進行交互,當然如果我們將不同的語言都遵照C語言的ABI進行編譯,那麼它們之間就可以互相調用。
C語言的調用約定規定了函數參數的傳遞方式、返回值的處理方式以及堆棧的清理方式。
這樣就定義好了Rust項目中可以讓外部使用的add
方法。
我們使用如下命令對項目進行編譯
cargo build --target wasm32-unknown-unknown
# 或
cargo build --target wasm32-wasi
這裡兩種target都可以使用,因為我們的項目中並沒有使用任何系統的API,所以通常使用第一種target即可。
編譯後可以在target/wasm-xxx/debug/
目錄下看到生成的calc.wasm
文件。
可以使用wasmtime-cli實驗一下是否能夠調用add
方法:
> wasmtime calc.wasm --invoke add 101 202
warning: using `--invoke` with a function that takes arguments is experimental and may break in the future
warning: using `--invoke` with a function that returns values is experimental and may break in the future
303
可以看到已經正確輸出了結果,說明這個Rust項目已經被正確編譯成了wasm。
C++項目編譯成wasm
創建一個項目叫做demo-cpp-wasmtime
,使用cmake作為構建工具,其目錄結構如下
demo-cpp-wasmtime
├── CMakeLists.txt
├── toolbox.cpp
└── toolbox.h
正如第3節講到的,我們需要使用emscripten工具鏈代替gcc工具鏈來將這個C++項目編譯成wasm。
cmake配置
因此我們需要按照如下方式配置CMakeLists.txt
文件
cmake_minimum_required(VERSION 3.26)
project(demo_cpp_wasmtime)
add_definitions(-std=c++17)
set(CMAKE_CXX_STANDARD 17)
if (DEFINED EMSCRIPTEN)
add_executable(toolbox toolbox.cpp toolbox.h)
set(CMAKE_EXECUTABLE_SUFFIX ".wasm")
set_target_properties(toolbox PROPERTIES COMPILE_FLAGS "-Os -s SIDE_MODULE=1")
set_target_properties(toolbox PROPERTIES LINK_FLAGS "-Os -s WASM=1 -s SIDE_MODULE=1 -s STANDALONE_WASM --no-entry")
else()
add_library(toolbox toolbox.cpp)
endif ()
這裡有幾點需要註意的
-
在使用emscripten時,我們使用
add_executable
指定編譯目標為可執行文件,這是因為wasm本身是可執行的二進位代碼,在沒有特殊配置時,編譯後的wasm代碼中會生成一個_start
函數,這個函數就是運行時執行wasm代碼的入口。這裡如果我們將add_executable
替換成add_library
,則使用emscripten編譯後只會生成libtoolbox.a
庫文件,而不會生成wasm代碼。 -
針對emscripten編譯工具鏈,我們配置了編譯參數和鏈接參數
-
-Os
表示開啟編譯優化 -
-s SIDE_MODULE=1
表示將toolbox
編譯成module
,這樣生成的wasm就類似動態鏈接庫,可以讓wasmtime在運行時動態鏈接這份wasm代碼。emscripten支持將代碼編譯成兩種不同的
module
- Main modules:系統庫會被鏈接進去
- Side modules:系統庫不會被鏈接進去
通常一個完整的項目只能有一個
Main module
,這個Main module
可以鏈接多個Side module
這裡的編譯選項
SIDE_MODULE
可以被設置為1
或者2
,設置成2
則編譯器會優化掉大量未被使用的代碼或未被標記為EMSCRIPTEN_KEEPALIVE
的代碼,設置成1
則會保留所有代碼。 -
-s WASM=1
表示只輸出wasm
文件,設置為0
表示只輸出js
代碼,設置成2
表示兩種代碼都輸出 -
-s STANDALONE_WASM
表示編譯的wasm是不依賴web環境而運行的 -
--no-entry
編譯生成的wasm代碼通常需要有一個入口函數,也就是C++中需要有main
函數,然而我們這裡toolbox.cpp
中將只有一個foo
函數,因此我們需要使用這個鏈接參數來表示我們不需要入口函數。
-
代碼實現
toolbox.h
頭文件如下
#pragma once
extern "C" {
int foo(int right);
}
類似Rust,這裡我們聲明瞭一個函數foo
,並使用extern "C"
表示這個foo函數需要按照C語言ABI進行編譯。
接下來是toolbox.cpp
的實現
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#else
#define EMSCRIPTEN_KEEPALIVE
#define EM_IMPORT(NAME)
#endif
extern "C" {
EM_IMPORT(add) int add(int a, int b);
}
extern "C" {
EMSCRIPTEN_KEEPALIVE int foo(int right) {
return add(1, right);
}
}
下麵解釋一下代碼中的幾個巨集的作用:
-
#ifdef __EMSCRIPTEN__
:當我們使用emscripten工具鏈編譯這個項目時,__EMSCRIPTEN__
會被自動定義 -
EMSCRIPTEN_KEEPALIVE
和EM_IMPORT(NAME)
:這是頭文件
emscripten.h
中定義的巨集,查看源碼可以發現#define EMSCRIPTEN_KEEPALIVE __attribute__((used)) #ifdef __wasm__ #define EM_IMPORT(NAME) __attribute__((import_module("env"), import_name(#NAME))) #else #define EM_IMPORT(NAME) #endif
__attribute__((used))
的作用是告訴編譯器,即使該變數或函數沒有被直接使用,也不要將其優化掉。這在一些特殊的情況下很有用,例如當你想要確保某個變數或函數在編譯後的可執行文件中存在,即使它在代碼中沒有被顯式調用或使用。這樣就確保了我們的foo
函數不會被編譯器優化掉__attribute__((import_module("env"), import_name(#NAME)))
是用於WebAssembly的特殊屬性,用於指定導入函數所屬的模塊和導入函數的名稱。在WebAssembly中,可以從外部導入函數,這些函數通常由宿主環境(如瀏覽器或wasmtime)提供。當你使用__attribute__((import_module("env"), import_name(#NAME)))
屬性時,它告訴WebAssembly運行時,該函數屬於名為"env"的模塊,並且其導入名稱為#NAME
。
使用EM_IMPORT(add)
巨集告訴編譯器,這裡聲明的add方法其具體實現來自於其他模塊,具體就是來自於env
模塊中的add
函數。因此這裡聲明的add
方法其實可以起任意的名字,只要簽名與env
模塊中的add
方法相同即可。
編譯
使用如下命令進行編譯
# 在項目根目錄下
mkdir build
cd build
emcmake cmake ..
make
編譯後在build
目錄下會生成toolbox.wasm
二進位文件。
我們可以使用wasm2wat
命令將編譯好的wasm
二進位文件轉換成可讀的wat
文件來看一下生成的代碼的結構
如果沒有安裝
wasm2wat
命令可以使用一下命令來安裝sudo apt install wabt
執行wasm2wat toolbox.wasm -o toolbox.wat
命令後,可以打開toolbox.wat
文件查看其結構如下
(module
(type (;0;) (func (param i32 i32) (result i32)))
(type (;1;) (func))
(type (;2;) (func (param i32) (result i32)))
(import "env" "add" (func (;0;) (type 0)))
(func (;1;) (type 1))
(func (;2;) (type 2) (param i32) (result i32)
i32.const 1
local.get 0
call 0)
(export "__wasm_call_ctors" (func 1))
(export "__wasm_apply_data_relocs" (func 1))
(export "foo" (func 2)))
可以看出,代碼中import "env" "add"
表示add
函數來自env module
的add
函數。同時export "foo"
表示toolbox.wasm
對外暴露了foo
函數。
wasmtime項目
wasmtime項目可以使用wasmtime支持的各種語言實現,這裡我們以C++為例,看看如何將前面兩個項目生成的.wasm
文件調用起來。
創建一個項目叫做demo-run
,使用cmake進行項目構建,其目錄結構如下
demo-run
├── CMakeLists.txt
└── main.cpp
cmake配置
wasmtime項目可以使用gcc工具鏈進行編譯,因此它的CMakeLists.txt
可以正常進行配置
cmake_minimum_required(VERSION 3.26)
project(demo_run)
set(CMAKE_CXX_STANDARD 17)
add_executable(demo_run main.cpp)
target_link_libraries(demo_run PUBLIC wasmtime)
因為我們需要在代碼中使用wasmtime
的庫,因此這裡需要使用target_link_libraries(demo_run PUBLIC wasmtime)
將wasmtime
鏈接進來。這也就要求必須先按照第2節中的安裝方式配置好wasmtime
的環境變數。
代碼實現
具體wasmtime提供的每個API的用法在這裡不多做贅述,具體可以參考wasmtime官方文檔和官方提供的examples
#include <iostream>
#include <wasmtime.hh>
#include <fstream>
using namespace wasmtime;
std::vector<unsigned char> readFile(const char *name) {
std::ifstream watFile(name, std::ios::binary);
std::vector<unsigned char> arr;
char byte;
while (watFile.get(byte)) {
arr.push_back(byte);
}
return arr;
}
int main() {
std::cout << "Compiling module" << std::endl;
Engine engine;
// 載入calc.wasm成為module
auto calcByteArr = readFile("calc.wasm");
Span<uint8_t> calcSpan(calcByteArr.data(), calcByteArr.size());
auto calcModule = Module::compile(engine, calcSpan).unwrap();
// 載入toolbox.wasm成為module
auto toolboxByteArr = readFile("toolbox.wasm");
Span<uint8_t> toolboxSpan(toolboxByteArr.data(), toolboxByteArr.size());
auto toolboxModule = Module::compile(engine, toolboxSpan).unwrap();
std::cout << "Initializing..." << std::endl;
Store store(engine);
store.context().set_wasi(WasiConfig()).unwrap();
std::cout << "Linking..." << std::endl;
Linker linker(engine);
linker.define_wasi().unwrap();
// 鏈接器初始化calc module,實例化成具體的Instance
auto calcInst = linker.instantiate(store, calcModule).unwrap();
// 將上一步的calcInst中的所有export的對象定義到env module名下
linker.define_instance(store, "env", calcInst).unwrap();
// 鏈接器初始化toolbox module,實例化成具體的Instance
auto toolboxInst = linker.instantiate(store, toolboxModule).unwrap();
// 獲取toolboxInst中的foo方法
auto func = std::get<Func>(toolboxInst.get(store, "foo").value());
// 調用foo方法,傳入參數7,
auto fooRes = func.call(store, {7}).unwrap();
// 列印結果 FooResult: 8
std::cout << "FooResult: " << fooRes[0].i32() << std::endl;
return 0;
}
就像註釋中寫的那樣,我們將calc.wasm
中export
的方法add
添加到了名稱為env
的module
下,這樣上一步中C++編譯成的.wasm
代碼就可以鏈接到這個add
方法。
編譯與運行
mkdir build
cd build
cmake ..
make
執行編譯後會生成可執行文件demo_run
,由於代碼還要依賴兩個.wasm
文件,因此我們這裡手動將前面兩個項目生成的.wasm
文件拷貝到demo_run
可執行文件的同級目錄下。
運行生成的demo_run
可執行文件後可得如下輸出
> ./demo_run
Compiling module
Initializing...
Linking...
FooResult: 8
以上就實現了C++和Rust通過wasmtime實現相互調用的過程。