First draft WebIDL docs. UPdate to interacting with code to fairly represent embind vs webidl
This commit is contained in:
Родитель
19b13896f2
Коммит
b220bde7ca
|
@ -11,6 +11,10 @@ Emscripten provides numerous methods to connect and interact between JavaScript
|
|||
|
||||
- :ref:`Using ccall or cwrap <interacting-with-code-ccall-cwrap>`.
|
||||
- :ref:`Using direct function calls <interacting-with-code-direct-function-calls>` (faster but more complicated).
|
||||
|
||||
- Call compiled **C++** classes from JavaScript using bindings created with:
|
||||
|
||||
- :ref:`Embind or WebIDL-Binder<interacting-with-code-binding-cpp>`
|
||||
|
||||
- Call JavaScript functions from **C/C++**:
|
||||
|
||||
|
@ -18,20 +22,14 @@ Emscripten provides numerous methods to connect and interact between JavaScript
|
|||
- :ref:`Using EM_ASM() <interacting-with-code-call-javascript-from-native>` (faster).
|
||||
- :ref:`Using a C API implemented in JavaScript <implement-c-in-javascript>`.
|
||||
- :ref:`As function pointers from C <interacting-with-code-call-function-pointers-from-c>`.
|
||||
|
||||
- Call compiled **C++** classes from JavaScript using bindings created with:
|
||||
|
||||
- :ref:`embind`.
|
||||
- :ref:`WebIDL-Binder`. *Embind* also supports calling JavaScript from the C++.
|
||||
- :ref:`Using the Embind val class <embind-val-guide>`.
|
||||
|
||||
|
||||
- :ref:`Access compiled code memory from JavaScript <interacting-with-code-access-memory>`.
|
||||
- :ref:`Affect execution behaviour <interacting-with-code-execution-behaviour>`.
|
||||
- :ref:`Access environment variables <interacting-with-code-environment-variables>`.
|
||||
|
||||
The JavaScript methods for calling compiled C functions are efficient, but cannot be used with name-mangled C++ functions. Both :ref:`embind` or :ref:`WebIDL-Binder` create bindings between C++ and JavaScript: *Embind* allows binding in both directions, while the more-efficient *WebIDL Binder* only supports calling compiled code from JavaScript.
|
||||
|
||||
This article explains each of the methods listed above, or provides links to more detailed information.
|
||||
This article explains each of the methods listed above, and provides links to more detailed information.
|
||||
|
||||
.. note:: For information on how compiled code interacts with the browser environment, see :ref:`emscripten-runtime-environment`. For file system related manners, see the :ref:`file-system-overview`.
|
||||
|
||||
|
@ -273,18 +271,20 @@ For example, to set an environment variable ``MY_FILE_ROOT`` to be ``"/usr/lib/t
|
|||
|
||||
Module.preRun.push(function() {ENV.MY_FILE_ROOT = "/usr/lib/test"})
|
||||
|
||||
.. _interacting-with-code-binding-cpp:
|
||||
|
||||
WebIDL Binder
|
||||
=============
|
||||
Binding C++ and JavaScript — WebIDL Binder and Embind
|
||||
======================================================
|
||||
|
||||
The :ref:`WebIDL-Binder` is a tool to make C++ classes usable from JavaScript, as JavaScript classes.
|
||||
The JavaScript methods for calling compiled C functions are efficient, but cannot be used with name-mangled C++ functions.
|
||||
|
||||
The WebIDL binder is a fairly simple and lightweight approach to binding C++ to call from JavaScript. It was used to port Bullet Physics to the Web in the `ammo.js <https://github.com/kripken/ammo.js/>`_ project.
|
||||
:ref:`WebIDL-Binder` and :ref:`embind` create bindings between C++ and JavaScript, allowing C++ code entities to be used in a natural manner from JavaScript. *Embind* additionally supports calling JavaScript code from C++.
|
||||
|
||||
Both tools can map sophisticated C++ constructs, and allow C++ to be used from JavaScript in much the same way. However they operate at different levels, and use very different approaches for defining the binding:
|
||||
|
||||
- *Embind* declares bindings within the C/C++ file
|
||||
- :ref:`WebIDL-Binder` declares the binding in a separate file. This is run through the binder tool to create "glue" code that is then compiled with the project.
|
||||
|
||||
.. note:: There is no strong evidence that one tool is "better" than the other in terms of performance (no comparative benchmarks exist), and both have been used successfully in a number of projects. The selection of one tool over the other should therefore be based on which is the most natural fit for each developer, and their project build system.
|
||||
|
||||
|
||||
|
||||
Embind
|
||||
======
|
||||
|
||||
:ref:`embind` is a tool to communicate between JavaScript and C++, in a C++-like manner. It is not as lightweight or optimizable as Emscripten JavaScript libraries or the :ref:`WebIDL-Binder`, but it does allow communication in both directions between JavaScript and the C code.
|
||||
|
||||
|
|
|
@ -4,93 +4,104 @@
|
|||
WebIDL Binder (under-construction)
|
||||
===================================
|
||||
|
||||
The *WebIDL Binder* provides a simple and lightweight approach to binding C++ so that it can be called from JavaScript (when compiled) as if it were a normal JavaScript library.
|
||||
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 <http://heycam.github.io/webidl/>`_ to define the bindings, an interface language which 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.
|
||||
|
||||
This topic shows how to define an IDL file for a C++ class (and other C++ items), generate the C++ and JavaScript wrapper/glue code, and then compile this as part of your Emscripten project.
|
||||
This topic shows how bind and use C++ classes, functions and other types using IDL.
|
||||
|
||||
For a complete working example, see **test_webidl** in the `test suite <https://github.com/kripken/emscripten/blob/master/tests/test_core.py>`_ (sources `here <https://github.com/kripken/emscripten/tree/master/tests/webidl>`_). The test suite code is guaranteed to work and covers more material than this article. Another good example is `ammo.js <https://github.com/kripken/ammo.js/tree/master>`_, which uses the *WebIDL Binder* to port the Bullet Physics engine to the web.
|
||||
.. note:: An alternative to the *WebIDL Binder* is to use :ref:`Embind`. For more information see :ref:`interacting-with-code-binding-cpp`.
|
||||
|
||||
.. note:: An alternative to the *WebIDL Binder* is to use :ref:`Embind`. This tool supports mapping sophisticated C++11 constructs between C++ and JavaScript in both directions.
|
||||
|
||||
Both *Embind* and the *WebIDL Binder* require explicit declarations of what to bind together. However the *WebIDL Binder* is (arguably) easier to use, and does so at a lower-level which is simpler to optimize.
|
||||
|
||||
The *WebIDL Binder* does not currently support binding in the other direction (calling Web APIs from C++).
|
||||
|
||||
A quick example
|
||||
===============
|
||||
|
||||
Wrapping a C++ class
|
||||
====================
|
||||
Binding using the *WebIDL Binder* is a three-stage process: create a WebIDL file that describes the C++ interface, use the binder to generate C++ and JavaScript "glue" code, and finally compile this glue code with the Emscripten project.
|
||||
|
||||
The first step is to create a WebIDL file that describes the C++ class you are going to wrap. This file duplicates some of the information in the C++ header file, in a format that is explicitly designed both for easy parsing, and to represent code items in a way that is convenient for connecting to JavaScript.
|
||||
Defining the WebIDL file
|
||||
------------------------
|
||||
|
||||
The first step is to create a *WebIDL file* that describes the C++ types you are going to bind. This file duplicates some of the information in the C++ header file, in a format that is explicitly designed both for easy parsing, and to represent code items in a way that is convenient for connecting to JavaScript.
|
||||
|
||||
For example, consider the following C++ classes:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
class Foo {
|
||||
public:
|
||||
int getVal();
|
||||
void setVal(int v);
|
||||
};
|
||||
class Foo {
|
||||
public:
|
||||
int getVal();
|
||||
void setVal(int v);
|
||||
};
|
||||
|
||||
class Bar {
|
||||
public:
|
||||
Bar(long val);
|
||||
void doSomething();
|
||||
};
|
||||
class Bar {
|
||||
public:
|
||||
Bar(long val);
|
||||
void doSomething();
|
||||
};
|
||||
|
||||
The following IDL file can be used to describe them:
|
||||
|
||||
.. code-block:: idl
|
||||
|
||||
interface Foo {
|
||||
void Foo();
|
||||
long getVal();
|
||||
void setVal(long v);
|
||||
};
|
||||
interface Foo {
|
||||
void Foo();
|
||||
long getVal();
|
||||
void setVal(long v);
|
||||
};
|
||||
|
||||
interface Bar {
|
||||
void Bar(long val);
|
||||
void doSomething();
|
||||
};
|
||||
interface Bar {
|
||||
void Bar(long val);
|
||||
void doSomething();
|
||||
};
|
||||
|
||||
The definitions include a constructor that allows you to construct them from JavaScript — this is just a function returning void that has the same name as the interface. Otherwise the definitions are fairly straightforward.
|
||||
The mapping between the IDL definition and the C++ is fairly obvious. The main things to notice are:
|
||||
|
||||
.. note:: The type names in WebIDL are not identical to those in C++. For example IDL ``long`` is a 32-bit integer, ``short`` is a 16-bit integer, etc. There are also types like ``DOMString`` which represent a JavaScript string.
|
||||
- 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 :ref:`webidl-binder-type-name`.
|
||||
|
||||
|
||||
Generating the bindings glue code
|
||||
=================================
|
||||
---------------------------------
|
||||
|
||||
To generate bindings code, run
|
||||
The *bindings generator* (`tools/webidl_binder.py <https://github.com/kripken/emscripten/blob/master/tools/webidl_binder.py>`_) takes a Web IDL file name and an output file name as inputs, and creates C++ and JavaScript glue code files.
|
||||
|
||||
::
|
||||
For example, to create the glue code files **glue.cpp** and **glue.js** for the IDL file **my_classes.idl**, you would use the following command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python tools/webidl_binder.py my_classes.idl glue
|
||||
|
||||
where ``my_classes.idl`` is the name of that IDL file, and glue is the name of the output file you want. The bindings generator will emit two files: ``glue.cpp`` and ``glue.js``.
|
||||
|
||||
To use those files, you should
|
||||
|
||||
1. Add ``--post-js glue.js`` in your final emcc command, so that it is
|
||||
included (at the end of your normal output).
|
||||
2. Add a cpp file to that emcc command, which contains something like
|
||||
Compiling the project (using the bindings glue code)
|
||||
----------------------------------------------------
|
||||
|
||||
::
|
||||
To use the glue code files (``glue.cpp`` and ``glue.js``) in a project:
|
||||
|
||||
#include<..all the stuff the glue code needs, basically headers for the classes you are binding>
|
||||
#. Add ``--post-js glue.js`` in your final *emcc* command. The :ref:`post-js <emcc-post-js>` option adds the glue code at the end of the compiled output.
|
||||
#. Update **glue.cpp** with the headers for the classes you are binding (simply ``#include <>`` them at the top of the file).
|
||||
|
||||
.. note:: The C++ glue code emitted by the *bindings generator* needs to be updated with headers for the classes it binds (these are not available to the generator because they are not present in the Web IDL file).
|
||||
|
||||
#. Add **glue.cpp** to the final *emcc* command.
|
||||
|
||||
|
||||
The final *emcc* command includes both the C++ and JavaScript glue code, which are built to work together:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
./emcc my_classes.cpp glue.cpp --post-js glue.js -o output.js
|
||||
|
||||
The output now contains everything needed to use the C++ classes through JavaScript.
|
||||
|
||||
#include<glue.cpp>
|
||||
|
||||
That way your emcc command will include both the C++ glue and the JavaScript glue, which are built to work together. The output should contain everything you need, with the classes now usable through JavaScript.
|
||||
|
||||
Using C++ classes in JavaScript
|
||||
=======================================
|
||||
================================
|
||||
|
||||
Continuing the above example, you can write things like
|
||||
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.
|
||||
|
||||
::
|
||||
.. code-block:: javascript
|
||||
|
||||
var f = new Module.Foo();
|
||||
f.setVal(200);
|
||||
|
@ -99,138 +110,292 @@ Continuing the above example, you can write things like
|
|||
var b = new Module.Bar(123);
|
||||
b.doSomething();
|
||||
|
||||
and so forth.
|
||||
.. important:: Always access objects through the :ref:`module` object, as shown above.
|
||||
|
||||
Extended properties
|
||||
==========================
|
||||
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 :term:`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: ``var MyModuleName = Module;``.
|
||||
|
||||
By default normal-looking IDL can bind a lot of regular C++ to JavaScript. However, you may need to use IDL extended properties to handle various things.
|
||||
|
||||
Ref and Value
|
||||
-------------
|
||||
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 need be taken.
|
||||
|
||||
For example, the IDL binder assumes when it sees
|
||||
If a C++ object does need to be cleaned up, you must explicitly call :js:func:`Module.destroy(obj) <Module.destroy>` to invoke its destructor. Then drop all references to the object so that it can be garbage collected.
|
||||
|
||||
::
|
||||
.. note:: The C++ constructor is called transparently when a C++ object is created in JavaScript. However there is no way to tell if a JavaScript object is about to be garbage collected, so the binder glue code can't automatically call the destructor.
|
||||
|
||||
MyClass process(MyClass input);
|
||||
|
||||
in an interface, then both input and output values are pointers, ``MyClass*`` in C++. If, instead, they are references ``MyClass&`` then you need to write
|
||||
|
||||
::
|
||||
|
||||
[Ref] MyClass process([Ref] MyClass input);
|
||||
|
||||
If you don't do that, the generated glue C++ will not compile due to an error on failure to convert a pointer to an object.
|
||||
|
||||
If the C++ returns a new object and not a reference ``MyClass process(MyClass* input)``, then you should do
|
||||
|
||||
::
|
||||
|
||||
[Value] MyClass process([Ref] MyClass input);
|
||||
|
||||
This will allocate a static instance of that class and return it. Note that that means there is a single such object, you should use it and immediately forget about it.
|
||||
|
||||
NoDelete
|
||||
-------------
|
||||
|
||||
If a class cannot be deleted (because the destructor is private), you can do
|
||||
|
||||
::
|
||||
|
||||
[NoDelete]
|
||||
interface Foo {
|
||||
..
|
||||
};
|
||||
|
||||
Const
|
||||
-------------
|
||||
|
||||
You may need ``const`` to appear in the glue C++ code, so that it matches your main C++ code. You can do that by adding ``[Const]`` to an attribute or argument.
|
||||
|
||||
Prefix
|
||||
-------------
|
||||
|
||||
If you have a C++ class that is inside a namespace or another class, you can still wrap it, by giving a prefix,
|
||||
|
||||
::
|
||||
|
||||
[Prefix="Space::"]
|
||||
interface Inner {
|
||||
..
|
||||
};
|
||||
|
||||
Everywhere that ``Inner`` appears in the C++ glue code, it will show up as ``Space::Inner``, and compilation can succeed.
|
||||
|
||||
Operators
|
||||
-------------
|
||||
|
||||
You can bind operators using
|
||||
|
||||
::
|
||||
|
||||
[Operator="+="] TYPE1 add(TYPE2 x);
|
||||
|
||||
You can call it anything you want (``add`` is just an example). Note that support is limited to ``+=,*=`` etc. for now (with ``=`` in them).
|
||||
|
||||
JSImplementation: Subclasses in JavaScript
|
||||
Pointers, References, Value types (Ref and Value)
|
||||
====================================================
|
||||
|
||||
Imagine you have a class that has a virtual method called from C++, and you want to subclass it and implement it in JavaScript. To do so, you can use the JSImplementation option, for example in this IDL:
|
||||
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++:
|
||||
|
||||
[JSImplementation="Base"]
|
||||
interface ImplJS {
|
||||
void ImplJS();
|
||||
void virtualFunc();
|
||||
void virtualFunc2();
|
||||
};
|
||||
.. code-block:: cpp
|
||||
|
||||
// C++
|
||||
MyClass* process(MyClass* input);
|
||||
|
||||
.. code-block:: idl
|
||||
|
||||
// WebIDL
|
||||
MyClass process(MyClass input);
|
||||
|
||||
``Base`` is the C++ class, and ImplJS does not exist in your C++ code. ``JSImplementation="Base"`` means "this class will be a JavaScript implementation of Base". After running the bindings generator and compiling, you can do this:
|
||||
References should be decorated using ``[Ref]``:
|
||||
|
||||
::
|
||||
.. code-block:: cpp
|
||||
|
||||
// C++
|
||||
MyClass& process(MyClass& input);
|
||||
|
||||
.. code-block:: idl
|
||||
|
||||
// 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 a new object (rather than a reference or a pointer) then the return type should be decorated using ``[Value]``. This will allocate a static instance of that class and return it.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
// C++
|
||||
MyClass process(MyClass& input);
|
||||
|
||||
.. code-block:: idl
|
||||
|
||||
// WebIDL
|
||||
[Value] MyClass process([Ref] MyClass input);
|
||||
|
||||
.. note:: There is a single instance of the returned object. You should use it and immediately forget about it.
|
||||
|
||||
Const
|
||||
=====
|
||||
|
||||
C++ attributes, arguments or return types that use ``const`` can be specified in IDL using ``[Const]``.
|
||||
|
||||
For example, the following code fragments show the C++ and IDL for a function that returns a constant pointer object.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
//C++
|
||||
const myObject* getAsConst();
|
||||
|
||||
.. code-block:: idl
|
||||
|
||||
// WebIDL
|
||||
[Const] myObject getAsConst();
|
||||
|
||||
|
||||
Un-deletable classes (NoDelete)
|
||||
===============================
|
||||
|
||||
If a class cannot be deleted (because the destructor is private), specify ``[NoDelete]`` in the IDL file.
|
||||
|
||||
.. code-block:: idl
|
||||
|
||||
[NoDelete]
|
||||
interface Foo {
|
||||
...
|
||||
};
|
||||
|
||||
|
||||
|
||||
Defining inner classes and classes inside namespaces (Prefix)
|
||||
=============================================================
|
||||
|
||||
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``
|
||||
|
||||
.. code-block:: idl
|
||||
|
||||
[Prefix="MyNameSpace::"]
|
||||
interface Inner {
|
||||
..
|
||||
};
|
||||
|
||||
|
||||
Operators
|
||||
=========
|
||||
|
||||
You can bind to C++ operators using ``[Operator=]``:
|
||||
|
||||
.. code-block:: idl
|
||||
|
||||
[Operator="+="] TYPE1 add(TYPE2 x);
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
- The operator name can be anything (``add`` is just an example).
|
||||
- Support is currently limited to operators that contain "=": ``+=``, ``*=``, ``-=`` etc.
|
||||
|
||||
|
||||
enums
|
||||
=====
|
||||
|
||||
Enums are declared very similarly in C++ and IDL:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
// C++
|
||||
enum AnEnum {
|
||||
enum_value1,
|
||||
enum_value2
|
||||
};
|
||||
|
||||
// WebIDL
|
||||
enum AnEnum {
|
||||
"enum_value1",
|
||||
"enum_value2"
|
||||
};
|
||||
|
||||
The syntax is slightly more complicated for enums declared inside a namespace:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
// C++
|
||||
namespace EnumNamespace {
|
||||
enum EnumInNamespace {
|
||||
e_namespace_val = 78
|
||||
};
|
||||
};
|
||||
|
||||
// WebIDL
|
||||
enum EnumNamespace_EnumInNamespace {
|
||||
"EnumNamespace::e_namespace_val"
|
||||
};
|
||||
|
||||
When the enum is defined inside a class, the IDL definitions for the enum and class interface are separate:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
// C++
|
||||
class EnumClass {
|
||||
public:
|
||||
enum EnumWithinClass {
|
||||
e_val = 34
|
||||
};
|
||||
EnumWithinClass GetEnum() { return e_val; }
|
||||
|
||||
EnumNamespace::EnumInNamespace GetEnumFromNameSpace() { return EnumNamespace::e_namespace_val; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WebIDL
|
||||
enum EnumClass_EnumWithinClass {
|
||||
"EnumClass::e_val"
|
||||
};
|
||||
|
||||
interface EnumClass {
|
||||
void EnumClass();
|
||||
|
||||
EnumClass_EnumWithinClass GetEnum();
|
||||
|
||||
EnumNamespace_EnumInNamespace GetEnumFromNameSpace();
|
||||
};
|
||||
|
||||
|
||||
|
||||
Sub-classing C++ base classes in JavaScript (JSImplementation)
|
||||
===============================================================
|
||||
|
||||
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``.
|
||||
|
||||
.. code-block:: idl
|
||||
|
||||
[JSImplementation="Base"]
|
||||
interface ImplJS {
|
||||
void ImplJS();
|
||||
void virtualFunc();
|
||||
void virtualFunc2();
|
||||
};
|
||||
|
||||
After running the bindings generator and compiling, you can implement the interface in JavaScript as shown:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
var c = new ImplJS();
|
||||
c.virtualFunc = function() { .. };
|
||||
|
||||
When C++ code has a pointer to a ``Base`` instance and calls ``virtualFunc``, that call will reach the JavaScript code written here.
|
||||
When C++ code has a pointer to a ``Base`` instance and calls ``virtualFunc()``, that call will reach the JavaScript code defined above.
|
||||
|
||||
Note that you *must* implement all the methods you mentioned in the IDL of the JSImplementation class (ImplJS). If not, then an error will be shown (the technical reason is that C++ implements the virtual method, in a way that calls into JavaScript. If there is nothing in JavaScript to be called, it goes up through the prototype chain and calls that same C++ function once more).
|
||||
.. note::
|
||||
|
||||
Other Issues
|
||||
=============
|
||||
You *must* implement all the methods you mentioned in the IDL of the ``JSImplementation`` class (``ImplJS``) or an error will be shown.
|
||||
|
||||
Object cleanup
|
||||
--------------------------
|
||||
The technical reason is that C++ implements the virtual method, in a way that calls into JavaScript. If there is nothing in JavaScript to be called, it goes up through the prototype chain and calls that *same* C++ function again.
|
||||
|
||||
If you create a JavaScript object wrapping a C++ object, we call the C++ constructor for you transparently. However, if nothing else holds on to the JavaScript object, it will be GC'd, but we have no way to know that (due to how JavaScript works) - so we can't call the destructor for you.
|
||||
|
||||
Therefore, if a C++ object needs to be cleaned up, you should call ``Module.destroy(obj)`` on it. That calls the destructor directly. You should then drop all references to the JavaScript object as well.
|
||||
|
||||
Namespace
|
||||
-------------
|
||||
|
||||
You should normally access objects on the Module object, ``Module.MyClass`` etc. While they are also in the global namespace, if you use closure compiler or wrap the project in a function (to avoid polluting the global namespace), then they won't be. You can of course do ``var MyModuleName = Module;`` etc. to get whatever name you want (ammo uses ``Ammo.`` for example).
|
||||
|
||||
Pointers and comparisons
|
||||
--------------------------
|
||||
=========================
|
||||
|
||||
All the bindings functions expect to receive wrapper objects, that contain the raw pointer inside them, and not a raw pointer (which is just a memory address - an integer). You should normally not need to deal with raw pointers, but if you do, the following functions can help:
|
||||
All the bindings functions expect to receive wrapper objects (which contain a raw pointer) rather than a raw pointer. You should normally not 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:
|
||||
|
||||
- ``wrapPointer(ptr, Class)`` - Given a raw pointer (an integer), returns a wrapped object. Note that if you do not pass Class, it will be assumed to be the root class - this is likely not 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
|
||||
- ``wrapPointer(ptr, Class)`` — Given a raw pointer (an integer), returns a wrapped object.
|
||||
|
||||
Note that there is always a *single* wrapped object for a certain pointer for a certain class. This allows you to add data on that object and use it elsewhere, by using normal JavaScript syntax (``object.attribute = someData`` etc.). Note that this almost means that ``compare()`` is not needed - since you can compare two objects of the same class, and if they have the same pointer they must be the same object - but not quite: The tricky case is where one is a subclass of the other, in which case the wrapped objects are different while the pointer is the same. So, the correct way to compare two objects is to call ``compare()``.
|
||||
.. note:: If you do not pass the ``Class``, it will be assumed to be the root class — this is likely not 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 for a certain class. This allows you to add data on that object and use it elsewhere using normal JavaScript syntax (``object.attribute = someData`` etc.)
|
||||
|
||||
This *almost* means that ``compare()`` is not needed — since two objects of the same class with the same pointer must be the same object. The problem is that if one is a subclass of the other the wrapped objects are different even though the pointer is the same.
|
||||
|
||||
|
||||
NULL
|
||||
-------------
|
||||
====
|
||||
|
||||
All the bindings functions that return pointers/references/objects will return wrapped pointers. The only potentially confusing case is when they are returning a null pointer. In that case, you will get NULL (a global singleton with a wrapped pointer of 0) instead of null (the JavaScript builtin object) or 0. The reason is that by always returning a wrapper, you can always take the output and pass it back to another binding function, without that function needing to check the type of the argument.
|
||||
All the bindings functions that return pointers, references, or objects will return wrapped pointers. The reason is that by always returning a wrapper, you can always 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 builtin object) or 0.
|
||||
|
||||
|
||||
.. _webidl-binder-voidstar:
|
||||
|
||||
void*
|
||||
-------------
|
||||
=====
|
||||
|
||||
The ``void*`` type is supported through a ``VoidPtr`` type which 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).
|
||||
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).
|
||||
|
||||
|
||||
.. _webidl-binder-type-name:
|
||||
|
||||
WebIDL types
|
||||
============
|
||||
|
||||
The type names in WebIDL are not identical to those in C++. This section shows the mapping for some of the the different types.
|
||||
|
||||
.. csv-table::
|
||||
:header: "C++", "IDL"
|
||||
:widths: 50, 50
|
||||
|
||||
"``bool``", "``boolean``"
|
||||
"``float``", "``float``"
|
||||
"``double``", "``double``"
|
||||
"``char``", "``byte``"
|
||||
"``char*``", "``DOMString`` (represents a JavaScript string)"
|
||||
"``unsigned char``", "``octet``"
|
||||
"``unsigned short int``", "``unsigned short``"
|
||||
"``unsigned short``", "``unsigned short``"
|
||||
"``unsigned long``", "``unsigned long``"
|
||||
"``int``", "``long``"
|
||||
"``void``", "``void``"
|
||||
"``void*``", "``any`` or ``VoidPtr`` (see :ref:`webidl-binder-voidstar`)"
|
||||
|
||||
.. note:: The WebIDL types are fully documented in `this W3C specification <http://www.w3.org/TR/WebIDL/>`_.
|
||||
|
||||
|
||||
.. _webidl-binder-test-code:
|
||||
|
||||
Test and example code
|
||||
=====================
|
||||
|
||||
For a complete working example, see `test_webidl <https://github.com/kripken/emscripten/tree/master/tests/webidl>`_ in the `test suite <https://github.com/kripken/emscripten/blob/master/tests/test_core.py>`_. The test suite code is guaranteed to work and covers more material than this article.
|
||||
|
||||
Another good example is `ammo.js <https://github.com/kripken/ammo.js/tree/master>`_, which uses the *WebIDL Binder* to port the Bullet Physics engine to the web.
|
Загрузка…
Ссылка в новой задаче