The WebIDL Binder provides a simple and lightweight approach to binding C++, so that compiled code can be called from JavaScript as if it were a normal JavaScript library.

The WebIDL Binder uses WebIDL to define the bindings, an interface language that was specifically designed for gluing together C++ and JavaScript. Not only is this a natural choice for the bindings, but because it is low-level it is relatively easy to optimize.

The binder supports the subset of C++ types that can be expressed in WebIDL. This subset is more than sufficient for most use cases — examples of projects that have been ported using the binder include the Box2D and Bullet physics engines.

This topic shows how bind and use C++ classes, functions and other types using IDL.

The first step is to create a WebIDL file that describes the C++ types you are going to bind. This file will duplicate some of the information in the C++ header file, in a format that is explicitly designed both for easy parsing, and for representing code items.

The mapping between the IDL definition and the C++ is fairly obvious. The main things to notice are:

The IDL class definitions include a function returning void that has the same name as the interface. This constructor allows you to create the object from JavaScript, and must be defined in IDL even if the C++ uses the default constructor (see Foo above).

The type names in WebIDL are not identical to those in C++ (for example, int maps to long above). For more information about the mappings see WebIDL types.

Note

structs are defined in the same way as the classes above — using the interface keyword.

Add --post-jsglue.js in your final emcc command. The post-js option adds the glue code at the end of the compiled output.

Create a file called something like my_glue_wrapper.cpp to #include the headers of the classes you are binding and glue.cpp. This might have the following content:

#include<...> // Where "..." represents the headers for the classes we are binding.#include<glue.cpp>

Note

The C++ glue code emitted by the bindings generator does not include the headers for the classes it binds because they are not present in the Web IDL file. The step above makes these available to the glue code. Another alternative would be to include the headers at the top of glue.cpp, but then they would be overwritten every time the IDL file is recompiled.

Add my_glue_wrapper.cpp to the final emcc command.

The final emcc command includes both the C++ and JavaScript glue code, which are built to work together:

When using the WebIDL binder, often what you are doing is creating a library. In that
case, the MODULARIZE option makes sense to use. It wraps the entire JavaScript output
in a function, which you call to create instances,

varinstance=Module();

(You can use the EXPORT_NAME option to change Module to something else.) This is
good practice for libraries, as then they don’t include unnecessary things in the
global scope (and in some cases you want to create more than one).

Modularize also provides a promise-like API, which is the recommended way to use it,

The callback is called when it is safe to run compiled code, similar
to the onRuntimeInitialized callback (i.e., it waits for all
necessary async events). It receives the instance as a parameter,
for convenience.

Once binding is complete, C++ objects can be created and used in JavaScript as though they were normal JavaScript objects. For example, continuing the above example, you can create the Foo and Bar objects and call methods on them.

While the objects are also available in the global namespace by default, there are cases where they will not be (for example, if you use the closure compiler to minify code or wrap compiled code in a function to avoid polluting the global namespace). You can of course use whatever name you like for the module by assigning it to a new variable: varMyModuleName=Module;.

JavaScript will automatically garbage collect any of the wrapped C++ objects when there are no more references. If the C++ object doesn’t require specific clean up (i.e. it doesn’t have a destructor) then no other action needs to be taken.

If a C++ object does need to be cleaned up, you must explicitly call Module.destroy(obj) to invoke its destructor — then drop all references to the object so that it can be garbage collected. For example, if Bar were to allocate memory that requires cleanup:

varb=newModule.Bar(123);b.doSomething();Module.destroy(b);// If the C++ object requires clean up

Note

The C++ constructor is called transparently when a C++ object is created in JavaScript. There is no way, however, to tell if a JavaScript object is about to be garbage collected, so the binder glue code can’t automatically call the destructor.

You will usually need to destroy the objects which you create, but this depends on the library being ported.

C++ arguments and return types can be pointers, references, or value types (allocated on the stack). The IDL file uses different decoration to represent each of these cases.

Undecorated argument and return values in the IDL are assumed to be pointers in the C++:

// C++MyClass*process(MyClass*input);

// WebIDL
MyClass process(MyClass input);

References should be decorated using [Ref]:

// C++MyClass&process(MyClass&input);

// WebIDL
[Ref] MyClass process([Ref] MyClass input);

Note

If [Ref] is omitted on a reference, the generated glue C++ will not compile (it fails when it tries to convert the reference — which it thinks is a pointer — to an object).

If the C++ returns an object (rather than a reference or a pointer) then the return type should be decorated using [Value]. This will allocate a static (singleton) instance of that class and return it. You should use it immediately, and drop any references to it after use.

C++ classes that are declared inside a namespace (or another class) must use the IDL file Prefix keyword to specify the scope. The prefix is then used whenever the class is referred to in C++ glue code.

For example, the following IDL definition ensures that Inner class is referred to as MyNameSpace::Inner

The WebIDL Binder allows C++ base classes to be sub-classed in JavaScript. In the IDL fragment below, JSImplementation="Base" means that the associated interface (ImplJS) will be a JavaScript implementation of the C++ class Base.

All the binding functions expect to receive wrapper objects (which contain a raw pointer) rather than a raw pointer. You shouldn’t normally need to deal with raw pointers (these are simply memory addresses/integers). If you do, the following functions in the compiled code can be useful:

If you do not pass the Class, it will be assumed to be the root class — this probably isn’t what you want!

getPointer(object) — Returns a raw pointer.

castObject(object,Class) — Returns a wrapping of the same pointer but to another class.

compare(object1,object2) — Compares two objects’ pointers.

Note

There is always a single wrapped object for a certain pointer to a certain class. This allows you to add data on that object and use it elsewhere using normal JavaScript syntax (object.attribute=someData etc.)

compare() should be used instead of direct pointer comparison because it is possible to have different wrapped objects with the same pointer if one class is a subclass of the other.

All the binding functions that return pointers, references, or objects will return wrapped pointers. The reason is that by always returning a wrapper, you can take the output and pass it to another binding function without that function needing to check the type of the argument.

One case where this can be confusing is when returning a NULL pointer. When using bindings, the returned pointer will be NULL (a global singleton with a wrapped pointer of 0) rather than null (the JavaScript built-in object) or 0.

The void* type is supported through a VoidPtr type that you can use in IDL files. You can also use the any type.

The difference between them is that VoidPtr behaves like a pointer type in that you get a wrapper object, while any behaves like a 32-bit integer (which is what raw pointers are in Emscripten-compiled code).