WebAssembly Compiling C++

Why Compile C++ to WebAssembly?

Compiling C++ to WebAssembly has significant benefits:

  1. High Performance: C++ code runs close to native speed in WebAssembly.
  2. Reusability: Leverage existing C++ libraries and codebases in web applications.
  3. Cross-Platform Compatibility: Write once in C++ and run anywhere that supports WebAssembly.
  4. Security: WebAssembly’s sandboxed environment ensures safe execution.

Prerequisites for Compiling C++ to WebAssembly

Before starting, ensure you have the following:

  1. Emscripten SDK (emsdk): A toolchain for compiling C++ (and C) to WebAssembly.
  2. C++ Compiler: The em++ compiler included in the Emscripten SDK handles C++ code.

Install Emscripten SDK

Clone the Emscripten SDK repository:

git clone https://github.com/emscripten-core/emsdk.git
cd emsdk

Install and activate the latest version:

./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh

Verify the installation:

emcc --version

Step-by-Step Process for Compiling C++ to WebAssembly

Step 1: Write C++ Code

Create a simple C++ program to demonstrate the compilation process.

Example: A Program to Calculate Factorials

// factorial.cpp
#include <iostream>
using namespace std;

int factorial(int n) {
if (n == 0) return 1;
return n * factorial(n - 1);
}

int main() {
int num = 5;
cout << "Factorial of " << num << " is " << factorial(num) << endl;
return 0;
}

Step 2: Compile C++ Code to WebAssembly

Use the em++ command to compile the C++ code into WebAssembly.

Basic Compilation

em++ factorial.cpp -o factorial.html

This generates:

  • factorial.html: A webpage to load the WebAssembly module.
  • factorial.wasm: The compiled WebAssembly binary.
  • factorial.js: JavaScript glue code to interact with the Wasm module.

Step 3: Export Functions for JavaScript

To make C++ functions accessible in JavaScript, use the EXPORTED_FUNCTIONS flag.

Command:

em++ factorial.cpp -s EXPORTED_FUNCTIONS="['_factorial']" -o factorial.html

This exports the factorial function (prefixed with _ by Emscripten).

Step 4: Load and Use WebAssembly in JavaScript

Create an HTML file with JavaScript to load and use the WebAssembly module.

Example:

<!DOCTYPE html>
<html>
<head>
<title>WebAssembly Factorial</title>
<script src="factorial.js"></script>
</head>
<body>
<h1>Factorial Calculation with WebAssembly</h1>
<script>
const Module = {
onRuntimeInitialized: function () {
const result = Module._factorial(5); // Call the exported function
console.log("Factorial of 5:", result);
document.body.innerHTML += `<p>Factorial of 5 is: ${result}</p>`;
},
};
</script>
</body>
</html>

Step 5: Optimize the WebAssembly Binary

Use optimization flags to improve performance and reduce the size of the output.

Common Flags:

  • -01, -02, -03: Optimize for speed.
  • -Oz: Optimize for size.
  • -s ALLOW_MEMORY_GROWTH=1: Enable dynamic memory growth.

Example:

em++ factorial.cpp -O3 -s EXPORTED_FUNCTIONS="['_factorial']" -o factorial.wasm

Advanced Features in C++ for WebAssembly

1. Working with C++ Classes

WebAssembly supports C++ classes, enabling object-oriented programming.

Example:

// math_operations.cpp
#include <iostream>
using namespace std;

class Math {
public:
int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }
};

extern "C" {
Math mathInstance;

int add(int a, int b) {
return mathInstance.add(a, b);
}

int multiply(int a, int b) {
return mathInstance.multiply(a, b);
}
}

Command to Compile:

em++ math_operations.cpp -s EXPORTED_FUNCTIONS="['_add', '_multiply']" -o math_operations.wasm

2. Handling I/O Streams

Emscripten provides support for basic C++ I/O streams like cin and cout.

Example:

#include <iostream>
using namespace std;

int main() {
string name;
cout << "Enter your name: ";
cin >> name;
cout << "Hello, " << name << "!" << endl;
return 0;
}

3. Interfacing with JavaScript

WebAssembly modules can interact with JavaScript for more dynamic applications.

Example: Passing Arrays from JavaScript to C++

#include <iostream>
using namespace std;

extern "C" {
int sum(int* arr, int size) {
int total = 0;
for (int i = 0; i < size; i++) {
total += arr[i];
}
return total;
}
}

Compile with:

em++ array_sum.cpp -s EXPORTED_FUNCTIONS="['_sum']" -o array_sum.js

In JavaScript:

const Module = {
onRuntimeInitialized: function () {
const array = [1, 2, 3, 4, 5];
const arrayPointer = Module._malloc(array.length * 4);
Module.HEAP32.set(array, arrayPointer / 4);
const result = Module._sum(arrayPointer, array.length);
Module._free(arrayPointer);
console.log("Sum:", result);
},
};

Best Practices for Compiling C++ to WebAssembly

  1. Export Only Necessary Functions
    Keep the interface minimal to reduce code size.
  2. Use Optimization Flags
    Always optimize for performance or size, depending on your application needs.
  3. Debug Efficiently
    Use Emscripten’s debugging tools (-g) to diagnose issues.
  4. Leverage Existing Libraries
    Many C++ libraries are compatible with WebAssembly and can be used for rapid development.

Leave a Comment