C/C++语言转WebAssembly篇

Posted 第三眼的思绪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C/C++语言转WebAssembly篇相关的知识,希望对你有一定的参考价值。

C代码

#include <stdio.h>
#include <string.h>


const char *str = "hello ";
char *show(char *name)
    int m = strlen(str);
    int n = strlen(name);
    char result[m+n];
    for (int i = 0; i < m; i++) 
        result[i] = str[i];
    
    for (int j = 0; j < n; j++) 
        result[m+j] = name[j];
    
    printf("result printf===>%s\\n", result);
    char * tmp = result;
    return tmp;

emsdk安装环境变量需确认是否正常

C to wasm
emcc helloworld.c -s "EXPORTED_FUNCTIONS=['_show']" -s "EXPORTED_RUNTIME_METHODS=['cwrap','ccall']" -o hello.html
运行一:在浏览器中运行
Module.ccall("show","string",["string"],["zhangsan"]);

注:JS中加载WASM默认开启同源验证,所以需要同服务访问加载。


运行二:在微信小程序/小游戏中运行

编译出来的JS与微信生态语法存在一定不兼容,需要做一定改造适配。将hello.js代码改造如下:

// The Module object: Our interface to the outside world. We import
// and export values on it. There are various ways Module can be used:
// 1. Not defined. We create it here
// 2. A function parameter, function(Module)  ..generated code.. 
// 3. pre-run appended it, var Module = ; ..generated code..
// 4. External script tag defines var Module.
// We need to check if Module already exists (e.g. case 3 above).
// Substitution will be replaced with actual code on later stage of the build,
// this way Closure Compiler will not mangle it (e.g. case 4. above).
// Note that if you want to run closure, and also to use Module
// after the generated code, you will need to define   var Module = ;
// before the code. Then that object will be used in the code, and you
// can continue to use Module afterwards as well.
var Module = typeof Module != 'undefined' ? Module : ;

var isWechat = typeof wx == 'object';
WebAssembly = isWechat ? WXWebAssembly : WebAssembly;

// See https://caniuse.com/mdn-javascript_builtins_object_assign

// --pre-jses are emitted after the Module integration code, so that they can
// refer to Module (if they choose; they can also define Module)
// PRE_JSES

// Sometimes an existing Module object exists with properties
// meant to overwrite the default module functionality. Here
// we collect those properties and reapply _after_ we configure
// the current environment's defaults to avoid having to be so
// defensive during initialization.
var moduleOverrides = Object.assign(, Module);

var arguments_ = [];
var thisProgram = './this.program';
var quit_ = (status, toThrow) => 
  throw toThrow;
;

// Determine the runtime environment we are in. You can customize this by
// setting the ENVIRONMENT setting at compile time (see settings.js).

// Attempt to auto-detect the environment
var ENVIRONMENT_IS_WEB = typeof window == 'object' || isWechat;
var ENVIRONMENT_IS_WORKER = typeof importScripts == 'function';
// N.b. Electron.js environment is simultaneously a NODE-environment, but
// also a web environment.
var ENVIRONMENT_IS_NODE = typeof process == 'object' && typeof process.versions == 'object' && typeof process.versions.node == 'string';
var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER;

if (Module['ENVIRONMENT']) 
  throw new Error('Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -s ENVIRONMENT=web or -s ENVIRONMENT=node)');


// `/` should be present at the end if `scriptDirectory` is not empty
var scriptDirectory = '';

// Hooks that are implemented differently in different runtime environments.
var read_,
    readAsync,
    readBinary,
    setWindowTitle;

// Normally we don't log exceptions but instead let them bubble out the top
// level where the embedding environment (e.g. the browser) can handle
// them.
// However under v8 and node we sometimes exit the process direcly in which case
// its up to use us to log the exception before exiting.
// If we fix https://github.com/emscripten-core/emscripten/issues/15080
// this may no longer be needed under node.
function logExceptionOnExit(e) 
  if (e instanceof ExitStatus) return;
  let toLog = e;
  if (e && typeof e == 'object' && e.stack) 
    toLog = [e, e.stack];
  
  err('exiting due to exception: ' + toLog);


var fs;
var nodePath;
var requireNodeFS;
if (ENVIRONMENT_IS_NODE) 
  if (!(typeof process == 'object' && typeof require == 'function')) throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)');
  if (ENVIRONMENT_IS_WORKER) 
    scriptDirectory = require('path').dirname(scriptDirectory) + '/';
   else 
    scriptDirectory = __dirname + '/';
  

// include: node_shell_read.js


requireNodeFS = () => 
  // Use nodePath as the indicator for these not being initialized,
  // since in some environments a global fs may have already been
  // created.
  if (!nodePath) 
    fs = require('fs');
    nodePath = require('path');
  
;

read_ = function shell_read(filename, binary) 
  requireNodeFS();
  filename = nodePath['normalize'](filename);
  return fs.readFileSync(filename, binary ? undefined : 'utf8');
;

readBinary = (filename) => 
  var ret = read_(filename, true);
  if (!ret.buffer) 
    ret = new Uint8Array(ret);
  
  assert(ret.buffer);
  return ret;
;

readAsync = (filename, onload, onerror) => 
  requireNodeFS();
  filename = nodePath['normalize'](filename);
  fs.readFile(filename, function(err, data) 
    if (err) onerror(err);
    else onload(data.buffer);
  );
;

// end include: node_shell_read.js
  if (process['argv'].length > 1) 
    thisProgram = process['argv'][1].replace(/\\\\/g, '/');
  

  arguments_ = process['argv'].slice(2);

  if (typeof module != 'undefined') 
    module['exports'] = Module;
  

  process['on']('uncaughtException', function(ex) 
    // suppress ExitStatus exceptions from showing an error
    if (!(ex instanceof ExitStatus)) 
      throw ex;
    
  );

  // Without this older versions of node (< v15) will log unhandled rejections
  // but return 0, which is not normally the desired behaviour.  This is
  // not be needed with node v15 and about because it is now the default
  // behaviour:
  // See https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode
  process['on']('unhandledRejection', function(reason)  throw reason; );

  quit_ = (status, toThrow) => 
    if (keepRuntimeAlive()) 
      process['exitCode'] = status;
      throw toThrow;
    
    logExceptionOnExit(toThrow);
    process['exit'](status);
  ;

  Module['inspect'] = function ()  return '[Emscripten Module object]'; ;

 else
if (ENVIRONMENT_IS_SHELL) 

  if ((typeof process == 'object' && typeof require === 'function') || typeof window == 'object' || typeof importScripts == 'function') throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)');

  if (typeof read != 'undefined') 
    read_ = function shell_read(f) 
      return read(f);
    ;
  

  readBinary = function readBinary(f) 
    let data;
    if (typeof readbuffer == 'function') 
      return new Uint8Array(readbuffer(f));
    
    data = read(f, 'binary');
    assert(typeof data == 'object');
    return data;
  ;

  readAsync = function readAsync(f, onload, onerror) 
    setTimeout(() => onload(readBinary(f)), 0);
  ;

  if (typeof scriptArgs != 'undefined') 
    arguments_ = scriptArgs;
   else if (typeof arguments != 'undefined') 
    arguments_ = arguments;
  

  if (typeof quit == 'function') 
    quit_ = (status, toThrow) => 
      logExceptionOnExit(toThrow);
      quit(status);
    ;
  

  if (typeof print != 'undefined') 
    // Prefer to use print/printErr where they exist, as they usually work better.
    if (typeof console == 'undefined') console = /** @type!Console */();
    console.log = /** @type!function(this:Console, ...*): undefined */ (print);
    console.warn = console.error = /** @type!function(this:Console, ...*): undefined */ (typeof printErr != 'undefined' ? printErr : print);
  

 else

// Note that this includes Node.js workers when relevant (pthreads is enabled).
// Node.js workers are detected as a combination of ENVIRONMENT_IS_WORKER and
// ENVIRONMENT_IS_NODE.
if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) 
  if (ENVIRONMENT_IS_WORKER)  // Check worker, not web, since window could be polyfilled
    scriptDirectory = self.location.href;
   else if (typeof document != 'undefined' && document.currentScript)  // web
    scriptDirectory = document.currentScript.src;
  
  // blob urls look like blob:http://site.com/etc/etc and we cannot infer anything from them.
  // otherwise, slice off the final part of the url to find the script directory.
  // if scriptDirectory does not contain a slash, lastIndexOf will return -1,
  // and scriptDirectory will correctly be replaced with an empty string.
  // If scriptDirectory contains a query (starting with ?) or a fragment (starting with #),
  // they are removed because they could contain a slash.
  if (scriptDirectory.indexOf('blob:') !== 0) 
    scriptDirectory = scriptDirectory.substr(0, scriptDirectory.replace(/[?#].*/, "").lastIndexOf('/')+1);
   else 
    scriptDirectory = '';
  

  if (!(typeof window == 'object' || typeof importScripts == 'function' || isWechat)) throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)');

  // Differentiate the Web Worker from the Node Worker case, as reading must
  // be done differently.
  
// include: web_or_worker_shell_read.js


  read_ = (url) => 
      var xhr = new XMLHttpRequest();
      xhr.open('GET', url, false);
      xhr.send(null);
      return xhr.responseText;
  

  if (ENVIRONMENT_IS_WORKER) 
    readBinary = (url) => 
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url, false);
        xhr.responseType = 'arraybuffer';
        xhr.send(null);
        return new Uint8Array(/** @type!ArrayBuffer */(xhr.response));
    ;
  

  readAsync = (url, onload, onerror) => 
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.responseType = 'arraybuffer';
    xhr.onload = () => 
      if (xhr.status == 200 || (xhr.status == 0 && xhr.response))  // file URLs can return 0
        onload(xhr.response);
        return;
      
      onerror();
    ;
    xhr.onerror = onerror;
    xhr.send(null);
  

// end include: web_or_worker_shell_read.js
  

  setWindowTitle = (title) => document.title = title;
 else

  throw new Error('environment detection error');


var out = Module['print'] || console.log.bind(console);
var err = Module['printErr'] || console.warn.bind(console);

// Merge back in the overrides
Object.assign(Module, moduleOverrides);
// Free the object hierarchy contained in the overrides, this lets the GC
// reclaim data used e.g. in memoryInitializerRequest, which is a large typed array.
moduleOverrides = null;
checkIncomingModuleAPI();

// Emit code to handle expected values on the Module object. This applies Module.x
// to the proper local x. This has two benefits: first, we only emit it if it is
// expected to arrive, and second, by using a local everywhere else that can be
// minified.

if (Module['arguments']) arguments_ = Module['arguments'];legacyModuleProp('arguments', 'arguments_');

if (Module['thisProgram']) thisProgram = Module['thisProgram'];legacyModuleProp('thisProgram', 'thisProgram');

if (Module['quit']) quit_ = Module['quit'];legacyModuleProp('quit', 'quit_');

// perform assertions in shell.js after we set up out() and err(), as otherwise if an assertion fails it cannot print the message
// Assertions on removed incoming Module JS APIs.
assert(typeof Module['memoryInitializerPrefixURL'] == 'undefined', 'Module.memoryInitializerPrefixURL option was removed, use Module.locateFile instead');
assert(typeof Module['pthreadMainPrefixURL'] == 'undefined', 'Module.pthreadMainPrefixURL option was removed, use Module.locateFile instead');
assert(typeof Module['cdInitializerPrefixURL'] == 'undefined', 'Module.cdInitializerPrefixURL option was removed, use Module.locateFile instead');
assert(typeof Module['filePackagePrefixURL'] == 'undefined', 'Module.filePackagePrefixURL option was removed, use Module.locateFile instead');
assert(typeof Module['read'] == 'undefined', 'Module.read option was removed (modify read_ in JS)');
assert(typeof Module['readAsync'] == &#

以上是关于C/C++语言转WebAssembly篇的主要内容,如果未能解决你的问题,请参考以下文章

20分钟上手WebAssembly

Web API 学习笔记 - Webassembly

Web API 学习笔记 - Webassembly

《WebAssembly 权威指南》WebAssembly 入门

C语言转WebAssembly

ImageMagik——开发篇(转)