Article: Creating Javascript bindings for C/C++ libraries with Emscripten
How to bind C/C++ libraries with Emscripten and use them from Javascript.
Posted: 24 December 2014
In addition to allowing native code to run in the browser, Emscripten can also be used to facilitate interoperability between native code and Javascript. For raw C APIs, native functions can be called directly from Javascript with very little glue code. For C++ functions and classes, however, developers must explicitly specify bindings, using a declarative format.
Although it’s certainly possible to specify bindings for a library’s entire API, this can become cumbersome and time-consuming for larger, more complex libraries. Typically, I find it simpler to create a facade for the library, and provide bindings for the facade only. This provides all of the usual benefits of the facade pattern and, when using Embind, permits the creation of a more Javascript-flavoured interface than might otherwise be possible with the native API alone (key-value objects for configuration, Javascript function callbacks, etc.)
The sections below describe the details of using the two declarative binding systems supported by Emscripten: Embind and WebIDL.
Contents
Installing Emscripten
Install Emscripten using the instructions from the official site.
After installation is complete, ensure the directories containing the Emscripten executables are correctly added to the system PATH. If the supplied scripts to modify the PATH don’t work correctly, you may have to add the directories manually. (The directories are listed in the output of the ./emsdk activate
command.)
Embind
Embind is the more flexible of the two binding implementations supported by Emscripten, and involves fewer source files and compilation steps. The ability to use the emscripten::val
type for both return values and function/method arguments facilitates the creation of Javascript-friendly interfaces that interact directly with Javascript objects using only C++, without the need to implement an additional Javascript wrapper. For more information, see my article on creating Javascript-friendly interfaces using Embind.
Creating the bindings
Embind bindings are specified within C++ source code using the EMSCRIPTEN_BINDINGS()
macro block, which is described in detail in the Embind documentation. Bindings can be specified for functions and classes, and C++ code can interoperate with Javascript code by using the emscripten::val
class. Primitive types and strings (std::string
and std::wstring
) will be automatically converted between their C++ and Javascript equivalents.
Create a source file containing the interface which will be used to abstract access to the library, and specify the Embind bindings for your interface:
Compilation process
Compile the library itself to an LLVM bitcode shared library:
emconfigure ./configure --disable-static --enable-shared CXXFLAGS=-std=c++11 --prefix=/path/to/install/dir
emmake make
emmake make install
Compile the wrapper code to LLVM bitcode:
emcc -c -std=c++11 -I/path/to/install/dir/include MyWrapper.cpp -o MyWrapper.bc
Convert the bitcode for both the library and the wrapper to JavaScript (note that --bind
is specified here during conversion, and not during compilation of the wrapper code):
emcc --bind /path/to/install/dir/lib/libMyLibrary.so MyWrapper.bc -o MyLibrary.js
The generated Javascript file can then be included in projects like any other Javascript source file, and the bound code can be accessed through the globally-accessible Emscripten Module
object.
Notes
- Embind requires C++11 mode, which is why the examples above include the
-std=c++11
flag when compiling both the library and the wrapper code. - Shared libraries are used in the examples because static libraries are not guaranteed to work correctly when compiling to Javascript later.
- All source code should be left in the same location on the filesystem during the entire compilation process. This allows Emscripten to correctly generate source mappings during the final compilation step.
WebIDL
WebIDL is a W3 specification for describing interfaces to be implemented by web browsers. The interfaces that can be expressed using WebIDL are slightly more restrictive than those possible using Embind, and the WebIDL Binder does not support the Embind emscripten::val
class. This means the creation of a Javascript-friendly interface requires the implementation of a Javascript wrapper. However, unlike Embind, WebIDL is a widely-supported standard, with broader uses than Emscripten alone.
Creating the bindings
WebIDL bindings are specified in a separate file to the C++ source code for the classes being bound. The bindings file is used to generate Javascript and C++ “glue” code, which are then linked together.
Create an IDL file containing the WebIDL binding declarations:
Alongside the IDL file, create a source file containing the implementation of the bound classes:
Compilation process
First, compile the library we are wrapping to an LLVM bitcode shared library:
emconfigure ./configure --disable-static --enable-shared CXXFLAGS=-std=c++11 --prefix=/path/to/install/dir
emmake make
emmake make install
Next, use the WebIDL Binder script to process the IDL file and generate the Javascript and C++ “glue” code:
python /path/to/emscripten/tools/webidl_binder.py MyWrapper.idl glue
This will generate the files glue.js
and glue.cpp
in the current directory.
Compile the wrapper C++ code to LLVM bitcode (because we included glue.cpp
in our wrapper C++ file, it will be compiled here too):
emcc -c -I/path/to/install/dir/include MyWrapper.cpp -o MyWrapper.bc
Finally, link everything together and convert it to Javascript, using the --post-js
argument to include glue.js
:
emcc /path/to/install/dir/lib/libMyLibrary.so MyWrapper.bc --post-js glue.js -o MyLibrary.js
The generated Javascript file can then be included in projects like any other Javascript source file, and the bound code can be accessed through the globally-accessible Emscripten Module
object.