Reputation: 333
I have an existing project written on C++ that I would like to compile for webassembly using emscripten. The code calls boost library:
#include <boost/program_options.hpp>
#include <iostream>
#include <string>
#include <exception>
#include <algorithm>
#include <iterator>
#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem/exception.hpp>
#include <boost/filesystem/convenience.hpp>
#include <boost/thread/thread.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/assign.hpp>
I have compiled the necessary parts of boost library using emscripten as static libraries and converted them from bc to a-files using emar. Now I'm trying to compile the project feeding the compiler with the precompiled libraries: (part of Makefile)
C_OPTIONS= -O3 -DNDEBUG -g \
/home/hiisi/workspace/boost_libs/program_options/build/emscripten-1.38.38/release/link-static/threading-multi/libs/cmdline.bc.a \
/home/hiisi/workspace/boost_libs/program_options/build/emscripten-1.38.38/release/link-static/threading-multi/libs/config_file.bc.a \
/home/hiisi/workspace/boost_libs/program_options/build/emscripten-1.38.38/release/link-static/threading-multi/libs/convert.bc.a \
/home/hiisi/workspace/boost_libs/program_options/build/emscripten-1.38.38/release/link-static/threading-multi/libs/libboost_program_options.bc.a \
However make still complains on the very first occurrence of boost in the code:
main.cpp:1:10: fatal error: 'boost/program_options.hpp' file not found
#include <boost/program_options.hpp>
^~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
shared:ERROR: compiler frontend failed to generate LLVM bitcode, halting
The question may sound little bit naive, but how do I correctly do this? The project compiles perfectly fine with g++, but not em++
Upvotes: 1
Views: 2017
Reputation: 51
https://emscripten.org/docs/compiling/Building-Projects.html#using-libraries https://emscripten.org/docs/compiling/Building-Projects.html#emscripten-ports
DEMO: C++ with boost in WASM and also show passing arrays between js/cpp
using example.cpp
as example:
example.cpp:
// example.cpp
#include <iostream>
#include <emscripten/emscripten.h>
#include <boost/multiprecision/cpp_int.hpp>
#include <vector>
namespace mp = boost::multiprecision;
extern "C" {
EMSCRIPTEN_KEEPALIVE
int daysInWeek() {
return 7;
}
EMSCRIPTEN_KEEPALIVE
int mypow(int a,int b) {
mp::cpp_int x = mp::pow(mp::cpp_int(a), b);
// std::cout << x << "\n";
return (int) x;
}
EMSCRIPTEN_KEEPALIVE
int* createIntArray(int size) {
std::vector<int> vec(size);
// Populate the integer vec with some values
for (int i = 0; i < size; ++i) {
vec[i] = i + 99; // Fill the array with values 1, 2, 3, ..., size
}
int* array = new int[vec.size()]; // Allocate memory for the array
std::copy(vec.begin(), vec.end(), array); // Copy vector elements to the array
return array;
}
EMSCRIPTEN_KEEPALIVE
int powArr(int* a, int size) {
mp::cpp_int x = mp::pow(mp::cpp_int(a[0]), a[1]);
std::cout<<"Passed in array:[";
for (size_t i = 0; i < size; i++)
{
std::cout<< a[i];
if(i<size-1) std::cout<<",";
}
std::cout<<"]\n";
return (int) x;
}
}
Generate example.js
and example.wasm
emcc example.cpp -o example.js -sMODULARIZE -sEXPORTED_RUNTIME_METHODS=ccall,cwrap -sUSE_BOOST_HEADERS=1 -s "EXPORTED_FUNCTIONS=['_free','_malloc']"
if you don't want to use ccall
or cwrap
in js:
emcc example.cpp -o example.js -sMODULARIZE -sUSE_BOOST_HEADERS=1 -s "EXPORTED_FUNCTIONS=['_free','_malloc']"
Test the wasm by running:
node testBoost.js
testBoost.js:
// testBoost.js
var factory = require('./example.js');
factory().then((wasmInstance) => {
// simple function without paramenter
console.log('daysInWeek:',wasmInstance._daysInWeek()); // values can be returned, etc.
const res = wasmInstance._mypow(2,4); // direct calling works
console.log('result 1: calling mypow,',res);
// # using ccall
// const res2 = wasmInstance.ccall("mypow",'number',['number','number'], [3,2]); // using ccall etc. also work
// console.log('result 2',res2);
// # using cwrap
// const mypower = wasmInstance.cwrap('mypow', // name of C function
// 'number', // return type
// ['number', 'number']); // ar
// const res3 = mypower(5,2);
// console.log('result 3',res3);
// testing getting array from cpp
const cppOutputArrPointer = wasmInstance._createIntArray(5);
const js_output_array = new Uint32Array(wasmInstance.HEAP32.buffer, cppOutputArrPointer, 5);
console.log('returned i32 array from cpp:',js_output_array);
// testing passing array to cpp
const TYPES = {
i8: { array: Int8Array, heap: "HEAP8" },
i16: { array: Int16Array, heap: "HEAP16" },
i32: { array: Int32Array, heap: "HEAP32" },
f32: { array: Float32Array, heap: "HEAPF32" },
f64: { array: Float64Array, heap: "HEAPF64" },
u8: { array: Uint8Array, heap: "HEAPU8" },
u16: { array: Uint16Array, heap: "HEAPU16" },
u32: { array: Uint32Array, heap: "HEAPU32" }
};
const jsInputArr = [3,4];
const type = TYPES.i32;
const typedArray = type.array.from(jsInputArr);
// Allocate memory for the integer array
const heapPointer = wasmInstance._malloc(typedArray.length * typedArray.BYTES_PER_ELEMENT);
wasmInstance[type.heap].set(typedArray, heapPointer >> 2);
// Call the WebAssembly function with the integer array
const resArr = wasmInstance._powArr(heapPointer, jsInputArr.length);
console.log("result of using powArr function", resArr);
// Free the allocated memory
wasmInstance._free(heapPointer);
});
I have put together a repo regarding various usage of cpp wasm: https://github.com/cyavictor88/wasm-cpp
Upvotes: 0
Reputation: 333
All I had to do is to make sure that boost lib presents in emscripten include directory. In my case that was emsdk/fastcomp/emscripten/system/include/
I made a symlink to system' boost library there and everything worked like a charm.
Upvotes: 4
Reputation: 41780
You have to add the include directories to use boost.
That would be an argument that look like this:
... -I/home/hiisi/workspace/boost_libs/include ...
Upvotes: 1