Larry Kosher
Larry Kosher

Reputation: 333

emscripten and boost library: how to compile existing project for webassembly?

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

Answers (3)

cyavictor88
cyavictor88

Reputation: 51

Use Emscripten Ports to simplify the compiling process:

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

Larry Kosher
Larry Kosher

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

Guillaume Racicot
Guillaume Racicot

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

Related Questions