[知者行之始|行者知之成]C/C++程序在瀏覽器端運行DemoCode/*WebAssembly從入門到放棄*/

  • 2020 年 2 月 25 日
  • 筆記

編譯器下載和配置參考[WebAssembly從入門到放棄] Emscripten1.39.4工具鏈的安裝與簡單使用。本文介紹將C程序編譯後在瀏覽器端運行的例子。

1. 搭建服務器

服務器使用Express框架,安裝參考[物聯網loT]樹莓派實現局域網內LED亮滅,代碼如下:

// Express  const express = require('express')  const app = express()    // public文件夾作為服務器根目錄  app.use( express.static('public', {    setHeaders: (res, path, stat) => {      // Serve .wasm files with correct mime type      if (path.endsWith('.wasm')) {        res.set('Content-Type', 'application/wasm')      }    }  }) )    // 開啟服務器  app.listen( 8888, () => console.log('Server running on port 8888!') )

2. C程序及其編譯

2.1 程序demo.c

其中getPoints函數返回了100個坐標點,要給js使用。

#include <time.h>  #include <stdio.h>  #include <stdlib.h>  #include <emscripten.h>    // 點集數量  #define NUM_POINTS 100    // 坐標點的數據結構  struct Point {    int x;  // x 坐標值    int y;  // y 坐標值  };    // 坐標點存儲  struct Point points[NUM_POINTS];    // 隨機整數生成函數  int getRandInt(max) {    return (rand() % max);  }    // 主函數  int main() {    // 初始化偽隨機數    srand(time(NULL));      // 創建點集實例    for( int i = 0; i < NUM_POINTS; i++ ) {       // 設置坐標值      points[i].x = getRandInt(666);      points[i].y = getRandInt(666);    }      // 調用JS函數並傳遞參數    EM_ASM({ jsDemoFun($0, $1); }, NUM_POINTS*2, 2 );  }    // 返回點集數據結構給js  struct Point * getPoints( int canvasWidth, int canvasHeight ) {     return points;  }

2.2 編譯

命令行如下:

emcc demo.c -s WASM=1 -s EXPORTED_FUNCTIONS="['_main','_getPoints']" -O3 -o demo.js

EXPORTED_FUNCTIONS指定要導數的函數列表,比如getPoints函數是要導出給js調用的,需要指明;-O3表示三級優化,如不優化,編譯後文件體積會很大。

3 與js交互示例

getPoints返回的是整型數組的指針,即元素首地址,也就是在內存(buffer)中的地址,使用Int32Array讀取內存buffer中長度為dataLength的數據,以整型數據類型讀取。

<!DOCTYPE html>  <html>    <head>      <meta charset="utf-8">      <title>WebAssembly Demo</title>    </head>    <body>      <script src="demo.js"></script>      <script>        const jsDemoFun = ( dataLength, pointStructSize ) => {          // 調用C語言里的程序_getPoints          let points = new Int32Array( Module.buffer, _getPoints( 666, 888), dataLength )            // 遍歷內存中的數據,逐個讀取          for( let i = 0; i < points.length; i+=pointStructSize ) {            // 從內存中截取數據            let point = points.slice( i, i+pointStructSize )              console.log(point);          }        }  </script>    </body>  </html>

4測試

啟動服務器,打開瀏覽器,地址欄輸入127.0.0.1:8888。