Why Compile C++ to WebAssembly?
Compiling C++ to WebAssembly has significant benefits:
- High Performance: C++ code runs close to native speed in WebAssembly.
- Reusability: Leverage existing C++ libraries and codebases in web applications.
- Cross-Platform Compatibility: Write once in C++ and run anywhere that supports WebAssembly.
- Security: WebAssembly’s sandboxed environment ensures safe execution.
Prerequisites for Compiling C++ to WebAssembly
Before starting, ensure you have the following:
- Emscripten SDK (emsdk): A toolchain for compiling C++ (and C) to WebAssembly.
- 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
- Export Only Necessary Functions
Keep the interface minimal to reduce code size. - Use Optimization Flags
Always optimize for performance or size, depending on your application needs. - Debug Efficiently
Use Emscripten’s debugging tools (-g) to diagnose issues. - Leverage Existing Libraries
Many C++ libraries are compatible with WebAssembly and can be used for rapid development.