FP issues now ready for review. Interacting with code now ready for review. Minor changes to the others.

This commit is contained in:
hamishwillee 2014-08-29 16:34:54 +10:00
Родитель 5ac7bfe2f9
Коммит 5330b5a02e
8 изменённых файлов: 360 добавлений и 397 удалений

Просмотреть файл

@ -1,3 +1,5 @@
.. _preamble-js:
===========
preamble.js
===========

Просмотреть файл

@ -191,6 +191,7 @@ For example if ``allReady()`` is a JavaScript function you want called when ever
}
.. _faq-dead-code-elimination:
Functions in my C/C++ source code vanish when I compile to JavaScript, and/or I get ``No functions to process..``?
==================================================================================================================

Просмотреть файл

@ -57,6 +57,9 @@ After the application runs, as mentioned above you will typically want to define
- :c:func:`emscripten_async_call` lets you call a function after some specific interval (basically a wrapper around ``setTimout/requestAnimationFrame``).
- :c:func:`emscripten_async_wget` asynchronously loads a file from the network. Synchronous XHRs cannot load binary data, so an asynchronous function is necessary. It will call a callback that you give it when the file arrives. You can use this to fetch the next level in your game, for example (note that it will do the same operation we do on preloaded files, setting them up as image or audio elements as necessary for execution later).
.. todo:: HamishW :ref:`Module` has some functions which affect exectution. Remember to link it here. Some stuff on this in "interacting with code".
Notes
=====

Просмотреть файл

@ -1,26 +1,60 @@
.. _Interacting-with-code:
===================================
Interacting with code (wiki-import)
===================================
========================================
Interacting with code (ready-for-review)
========================================
.. note:: This article was migrated from the wiki (Fri, 25 Jul 2014 04:21) and is now the "master copy" (the version in the wiki will be deleted). It may not be a perfect rendering of the original but we hope to fix that soon!
(If you are looking for how compiled code interacts with the browser environment, see :ref:`Emscripten-Browser-Environment`.)
Emscripten provides numerous methods to connect and interact between JavaScript and compiled C or C++:
There are various ways to connect and interact between JS and compiled C++ in JS. An overview appears in the second half of these slides:
http://kripken.github.io/mloc\_emscripten\_talk/qcon.html
- Call compiled **C** functions from normal JavaScript:
Calling Compiled Functions From Normal JavaScript
=================================================
- Using :js:func:`ccall` or :js:func:`cwrap`.
- Directly from JavaScript (faster but more complicated).
- Call JavaScript functions from **C/C++**:
It's easy to call compiled code from normal JavaScript. For example, run this command in the Emscripten home directory:
- Using :c:func:`emscripten_run_script`.
- Using :c:type:`EM_ASM` (faster).
- Implement a C API in JavaScript.
- As 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++.
- Access compiled code memory from JavaScript.
- Affect execution behaviour.
- Access 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 glue 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, or provides links to more detailed information.
.. note:: For information on how compiled code interacts with the browser environment, see :ref:`Emscripten-Browser-Environment`. For file system related manners, see the :ref:`Filesystem-Guide`.
Calling compiled C functions from JavaScript
============================================
The easiest way to call compiled C functions from JavaScript is to use :js:func:`ccall` or :js:func:`cwrap`.
:js:func:`ccall` calls a compiled C function with specified parameters and returns the result, while :js:func:`cwrap` "wraps" a compiled C function and returns a JavaScript function you can call normally. :js:func:`cwrap` is therefore more useful if you plan to call a compiled function a number of times.
Consider the **tests/hello_function.cpp** file shown below. The ``int_sqrt()`` function to be compiled is wrapped in ``extern "C"`` to prevent C++ name mangling.
.. include:: ../../../../../tests/hello_function.cpp
:literal:
To compile this code run the following command in the Emscripten home directory:
::
./emcc tests/hello_function.cpp -o function.html -s EXPORTED_FUNCTIONS="['_int_sqrt']"
Open the page in a web browser. Nothing will happen because you compiled a function, and there is no ``main`` so nothing will be run by default. But, open a JavaScript environment (Control-Shift-K on Firefox, Control-Shift-J on Chrome), then type (as three separate commands, pressing Enter after each one):
After compiling, you can call this function with :js:func:`cwrap` using the following JavaScript:
::
@ -28,57 +62,89 @@ Open the page in a web browser. Nothing will happen because you compiled a funct
int_sqrt(12)
int_sqrt(28)
You should get the results ``3, 5``, which are the correct output: The compiled function does a square root operation, but acts on integers. Open ``tests/hello_function.cpp`` to see the source code that you just called here.
The first parameter is the name of the function to be wrapped, the second is the return type of the function, then an array of parameter types (which may be omitted if there are no parameters). The types are native JavaScript types, "number" (for a C integer, float, or general pointer) or "string" (for a C ``char*`` that represents a string).
:js:func:`cwrap` which was used in this example will wrap a compiled C function, returning a JavaScript function you can call normally. :js:func:`cwrap` gets as its first parameter the name of the function to be wrapped, then the return type of the function, then an array of parameter types (the array can be omitted if there are no parameters). The types are native JavaScript types, "number" (for a C integer, float, or general pointer) or "string" (for a C ``char*`` that represents a string).
You can run this yourself by first opening the generated page **function.html** in a web browser (nothing will happen on page load because there is no ``main()``). Open a JavaScript environment (**Control-Shift-K** on Firefox, **Control-Shift-J** on Chrome), and enter the above commands as three separate commands, pressing **Enter** after each one. You should get the results ``3`` and ``5``, which are the correct output as the compiled function works on integers.
There is also :js:func:`ccall`, which is like :js:func:`cwrap` but receives another parameter with the parameters to pass to the function, and calls the function. :js:func:`cwrap` is useful to wrap a function once and call it several times, while :js:func:`ccall` is useful for a single call to a function.
:js:func:`ccall` is similar, but receives another parameter with the parameters to pass to the function:
Some things to keep in mind with :js:func:`ccall` and :js:func:`cwrap`:
.. code-block:: javascript
- In the example above, we compiled a C function. That's easier than C++ because C++ functions are name-mangled.
- By default Emscripten does dead code elimination to minimize code size. See the :ref:`FAQ` entry on "Functions in my C/C++ source code vanish when I compile to JavaScript..?"
- In ``-O2`` and above, closure compiler is run, which minifies function names - which means you won't be able to find your compiled
functions. To prevent that, run emcc with ``-s EXPORTED_FUNCTIONS='["_main","_other_function"]'``. Exported functions will retain their names even through closure compiler.
**Note:** you need ``_`` at the beginning of the functions when exporting (but not with ccall), and note also that you need to use ``Module.ccall`` and not :js:func:`ccall` by itself, since closure will minify the function name, leaving only the Module object where we export names.
// Call C from JavaScript
var result = Module.ccall('int_sqrt', // name of C function
'number', // return type
['number'], // argument types
[28]); // arguments
Accessing Memory
================
// result is 5
You can access memory using :js:func:`getValue(ptr, type) <getValue>` and :js:func:`setValue(ptr, value, type) <setValue>`. The first argument is a pointer, a number representing a memory address. ``type`` must be an LLVM IR type, one of ``i8,i16,i32,i64,float,double`` or a pointer type like ``i8*`` (or just ``*``). Note that the types here are not as in :js:func:`ccall` and :js:func:`cwrap` - this is a lower-level operation, and we do need to care what specific integer etc. type is being used.
.. note::
You can also access memory 'directly' by manipulating the arrays that represent memory. This is not recommended unless you are sure you know what you are doing, and need the additional speed over :js:func:`getValue`/:js:func:`setValue`. A case where you might need this is if you want to import a large amount of data from JS to be processed by compiled code, then you might do something like this:
This example illustrates a few other points, which you should remember when using :js:func:`ccall` or :js:func:`cwrap`:
::
- These methods can be used with compiled **C** functions — name-mangled C++ functions won't work.
- We highly recommended that you *export* functions that are to be called from JavaScript:
- Exporting is done at compile time. For example: ``-s EXPORTED_FUNCTIONS='["_main","_other_function"]'`` exports ``main()`` and ``other_function()``. You need ``_`` at the beginning of the function names in the ``EXPORTED_FUNCTIONS`` list.
- Emscripten does :ref:`dead code elimination <faq-dead-code-elimination>` to minimize code size — exporting ensures the functions you need aren't removed.
- At higher optimisation levels (``-O2`` and above), the :term:`closure compiler` runs and minifies (changes) function names. Exporting functions allows you to continue to access them using the original name through the global ``Module`` object.
- Use ``Module.ccall`` and not ``ccall`` by itself. The former will work at all optimisation levels (even if the :term:`Closure Compiler` minifies the function names).
var buf = Module._malloc(myTypedArray.length*myTypedArray.BYTES_PER_ELEMENT);
Module.HEAPU8.set(myTypedArray, buf);
Module.ccall('my_function', 'number', ['number'], [buf]);
Module._free(buf);
Call compiled C/C++ code "directly" from JavaScript
===================================================
That allocates a buffer, copies in some data, then calls a C function to process the data, and finally frees the buffer. Here ``my_function`` is a C function that receives a single integer parameter (could be a pointer as well, as they are just 32-bit integers for us), and returns an integer as well, something like ``int my_function(char *buf)``.
Functions in the original source become JavaScript functions, so you can call them directly if you do type translations yourself — this will be faster than using :js:func:`ccall` or :js:func:`cwrap`, but a little more complicated.
Calling JavaScript From C/C++
To call the method directly, you will need to use the full name as it appears in the generated code. This will be the same as the original C function, but with a leading ``_``.
.. note:: If you use :js:func:`ccall` or :js:func:`cwrap`, you do not need to prefix function calls with ``_`` — just use the C name.
The types of the parameters you pass to functions need to make sense. Integers and floating point values can be passed as is. Pointers are simply integers in the generated code.
Strings in JavaScript must be converted to pointers for compiled code — the relevant function is :js:func:`Pointer_stringify` which given a pointer returns a JavaScript string. Converting a JavaScript string ``someString`` to a pointer can be accomplished using :js:func:`allocate(intArrayFromString(someString), 'i8', ALLOC_STACK) <allocate>`.
.. note:: The conversion to a pointer allocates memory (that's the call to :js:func:`allocate` there), and in this case we allocate it on the stack (if you are called from a compiled function, it will rewind the stack for you; otherwise, you should do ``Runtime.stackSave()`` before and ``Runtime.stackRestore(..that value..)`` afterwards).
There are other convenience functions for converting strings and encodings in :ref:`preamble-js`.
.. todo:: **HamishW** Might be better to show the allocate above using _malloc, as allocate is an advanced API. We also need to better explain the note about stackRestore etc, or remove it - as it doesn't mean a lot to me.
Calling JavaScript from C/C++
=============================
The most direct way is to just use :c:func:`emscripten_run_script`, which basically runs some JS code from C/C++ using eval. So ``emscripten_run_script("alert('hi')");`` will show an alert with 'hi' (note: this calls ``alert`` which is present in browsers, but not in node or other JS shells. You can call ``Module.print`` to print to stdout). This is not very fast though. A faster alternative is to write "inline JavaScript", basically the same as inline assembly would be used, for example
Emscripten provides two main approaches for calling JavaScript from C/C++: running the script using :c:func:`emscripten_run_script` or writing "inline JavaScript".
::
The most direct, but slightly slower, way is to use :c:func:`emscripten_run_script`. This effectively runs the specified JavaScript code from C/C++ using ``eval()``. For example, to call the browser's ``alert()`` function with the text 'hi', you would call the following JavaScript:
#include <emscripten.h>
int main() {
EM_ASM(
alert('hello world!');
throw 'all done';
);
return 0;
}
.. code-block:: javascript
If you compile that C file, Emscripten will execute those two lines of JavaScript as if they appeared directly there in the generated code, so that when the compiled program is run you will see an alert and then an exception thrown.
emscripten_run_script("alert('hi')");
You can also send values from C into JS inside :c:macro:`EM_ASM`, as well as receive values back. See ``emscripten.h`` for details. One example is
.. note:: The function ``alert`` is present in browsers, but not in *node* or other JavaScript shells. An more generic alternative is to call :js:func:`Module.print`.
::
A faster way to call JavaScript from C is to write "inline JavaScript", using :c:func:`EM_ASM` (and related macros). These are used in a similar manner to inline assembly code. The "alert" example above might be written using inline JavaScript as:
.. code-block:: c++
#include <emscripten.h>
int main() {
EM_ASM(
alert('hello world!');
throw 'all done';
);
return 0;
}
When compiled and run, Emscripten will execute the two lines of JavaScript as if they appeared directly in the generated code. The result would be an alert, followed by an exception.
You can also send values from C into JavaScript inside :c:macro:`EM_ASM_`, as well as receive values back (see the :c:macro:`linked macro <EM_ASM_>` for details. For example, the following example will print out ``I received: 100`` and then ``101``.
.. code-block:: cpp
int x = EM_ASM_INT({
Module.print('I received: ' + $0);
@ -86,13 +152,27 @@ You can also send values from C into JS inside :c:macro:`EM_ASM`, as well as rec
}, 100);
printf("%d\n", x);
This will print out ``I received: 100`` and then ``101``. Note how you need to specify if the return value is an int or a double (with ``_INT`` here), also how the input values appear as ``$0, $1, etc.``, how ``return`` is used to provide the value sent from JS back to C, and finally how ``{, }`` are used here to enclose the code (this is necessary because of how C macros work, to differentiate the code from the arguments passed later which are the input values).
.. note::
.. note:: When using the :c:macro:`EM_ASM` macro, ensure that you only use single quotes('). Double quotes(") will cause a syntax error that is not detected by the compiler and is only shown when looking at a JavaScript console while running the offending code.
- You need to specify if the return value is an ``int`` or a ``double`` using the appropriate macro :c:macro:`EM_ASM_INT` or :c:macro:`EM_ASM_DOUBLE`.
- The input values appear as ``$0``, ``$1``, etc.
- ``return`` is used to provide the value sent from JavaScript back to C.
- See how ``{, }`` are used here to enclose the code. This is necessary to differentiate the code from the arguments passed later which are the input values (this is how C macros work).
- When using the :c:macro:`EM_ASM` macro, ensure that you only use single quotes('). Double quotes(") will cause a syntax error that is not detected by the compiler and is only shown when looking at a JavaScript console while running the offending code.
More generally, our entire libc, SDL etc. implementations are exactly JS code that is called from C/C++ using a C API. You can add your own libraries as well. For example, if you have some C code like this
::
Implement a C API in JavaScript
===============================
It is possible to implement a C API in JavaScript! This is the approach that was used to write Emscripten's implementations of :term:`SDL` and *libc*.
You can use it to write your own APIs to call from C/C++. To do this you define the interface, decorating with ``extern`` to mark the methods in the API as external symbols. You then implement the symbols in JavaScript by simply adding their definition to `library.js <https://github.com/kripken/emscripten/blob/master/src/library.js>`_ (by default). When compiling the C code, the compiler looks in the JavaScript libraries for relevant external symbols.
By default, the implementation is added to **library.js** (and this is where you'll find the Emscripten implementation of *libc*). You can put the JavaScript implementation in your own library file and add it using the :ref:`emcc option <emcc-js-library>` ``--js-library``. See `test_js_libraries <https://github.com/kripken/emscripten/blob/master/tests/test_core.py#L4800>`_ in **tests/test_other.py** for a complete working example, including the syntax you should use inside the JavaScript library file.
As a simple example, consider the case where you have some C code like this:
.. code-block:: c
extern void my_js();
@ -101,73 +181,94 @@ More generally, our entire libc, SDL etc. implementations are exactly JS code th
return 1;
}
.. note:: When using C++ you should encapsulate ``extern void my_js();`` in ``extern "C" {}`` block:
.. note:: When using C++ you should encapsulate ``extern void my_js();`` in ``extern "C" {}`` block to prevent C++ name mangling:
::
.. code-block:: cpp
extern "C" {
extern void my_js();
}
extern "C" {
extern void my_js();
}
then you can implement ``my_js`` in JS by simply adding
Then you can implement ``my_js`` in JavaScript by simply adding the implementation to **library.js** (or your own file). Like our other examples of calling JavaScript from C, the example below just launches the browser ``alert()``:
::
.. code-block:: javascript
my_js: function() {
alert('hi');
},
to **library.js**. How this works is that when there is an external symbol, the compiler looks in the JS libraries and pulls in the relevant symbols. See the library\*.js files for more details and examples.
You can use the emcc option ``--js-library`` to add a file with such code, instead of placing it inside **library.js**. This lets you be more modular. See ``test_js_libraries`` in ``tests/test_other.py`` for a complete working example, including the syntax you should use inside the JS library file.
See the `library_*.js <https://github.com/kripken/emscripten/tree/master/src>`_ files for other examples.
- JS libraries can declare dependencies (``__deps``, see examples in ``library*.js``), however those are only for other JS libraries. If a JS library depends on a compiled C library (like most of libc), you must edit ``src/deps_info.json``, see ``tools/system_libs.py`` (search for deps\_info).
.. note::
Calling JS functions as function pointers from C
--------------------------------------------------
- JavaScript libraries can declare dependencies (``__deps``), however those are only for other JavaScript libraries. See examples in **library*.js**)
- If a JavaScript library depends on a compiled C library (like most of *libc*), you must edit `src/deps_info.json <https://github.com/kripken/emscripten/blob/master/src/deps_info.json>`_. Search for "deps_info" in `tools/system_libs.py <https://github.com/kripken/emscripten/blob/master/tools/system_libs.py>`_.
You can use ``Runtime.addFunction`` to return an integer value that represents a function pointer. Passing that integer to C code then lets it call that value as a function pointer, and the JS function you sent to ``Runtime.addFunction`` will be called. See ``test_add_function`` in ``tests/test_core.py`` for an example.
Calling JavaScript functions as function pointers from C
========================================================
WebIDL Binder
=============
You can use ``Runtime.addFunction`` to return an integer value that represents a function pointer. Passing that integer to C code then lets it call that value as a function pointer, and the JavaScript function you sent to ``Runtime.addFunction`` will be called.
The :ref:`WebIDL-Binder` is a tool to make C++ classes usable from JS as JS classes. It is used to port Bullet Physics to the web in the **ammo.js** project, and is a fairly simple lightweight approach to binding between the two languages.
See ``test_add_function`` in `tests/test_core.py <https://github.com/kripken/emscripten/blob/master/tests/test_core.py#L5904>`_ for an example.
Embind
======
Embind is a method to communicate from JS to C++ and C++ to JS, in a C++-like manner (whereas JS libraries are using C APIs, and just one direction). The only downside is that it is not as lightweight as JS libraries or the WebIDL binder. Docs: :ref:`embind`.
Access memory from JavaScript
=============================
Other methods
=============
You can access memory using :js:func:`getValue(ptr, type) <getValue>` and :js:func:`setValue(ptr, value, type) <setValue>`. The first argument is a pointer (a number representing a memory address). ``type`` must be an LLVM IR type, one of ``i8``, ``i16``, ``i32``, ``i64``, ``float``, ``double`` or a pointer type like ``i8*`` (or just ``*``).
You can directly interact in various other ways with the compiled code:
There are example of these functions being used in the tests `here <https://github.com/kripken/emscripten/blob/master/tests/core/test_utf.in>`_ and `here <https://github.com/kripken/emscripten/blob/master/tests/test_core.py#L5704>`_.
- Functions in the original source become JS functions, so you can call them directly if you do type translations yourself - this will be faster than using :js:func:`ccall` or :js:func:`cwrap`, but a little more complex. To call the method directly, you will need to use the full name as it appears in the generated code, and note that a leading ``_`` is added to all C methods. (However if you use :js:func:`ccall` or :js:func:`cwrap`, you do not need to prefix function calls with ``_`` - :js:func:`ccall` and :js:func:`cwrap` use the C name, which has no extra leading ``_``).
- The types of the parameters you pass to functions need to make sense. Integers and floating point values can be passed as is. Aside from those, there are pointers, which are simply integers in the generated code.
- Strings in JavaScript must be converted to pointers for compiled code, the relevant functions are :js:func:`Pointer_stringify` which given a pointer returns a JavaScript string, and the other direction can be accomplished by ``allocate(intArrayFromString(someString), 'i8', ALLOC_STACK)`` which will convert a JavaScript string ``someString`` to a pointer. Note that conversion to a pointer allocates memory (that's the call to ``allocate`` there), and in this case we allocate it on the stack (if you are called from a compiled function, it will rewind the stack for you; otherwise, you should do ``Runtime.stackSave()`` before and ``Runtime.stackRestore(..that value..)`` afterwards).
- There are various other convenience functions, see **preamble.js** (that file will be included with the generated code).
- For filesystem-related manners, see the :ref:`Filesystem-Guide`.
.. note:: This is a lower-level operation than :js:func:`ccall` and :js:func:`cwrap` — we *do* need to care what specific type (e.g. integer) is being used.
Affecting execution
===================
You can also access memory 'directly' by manipulating the arrays that represent memory. This is not recommended unless you are sure you know what you are doing, and need the additional speed over :js:func:`getValue`/:js:func:`setValue`.
A case where this might be required is if you want to import a large amount of data from JavaScript to be processed by compiled code. For example, the following code allocates a buffer, copies in some data, calls a C function to process the data, and finally frees the buffer.
.. code-block:: javascript
var buf = Module._malloc(myTypedArray.length*myTypedArray.BYTES_PER_ELEMENT);
Module.HEAPU8.set(myTypedArray, buf);
Module.ccall('my_function', 'number', ['number'], [buf]);
Module._free(buf);
Here ``my_function`` is a C function that receives a single integer parameter (or a pointer, they are both just 32-bit integers for us) and returns an integer. This could be something like ``int my_function(char *buf)``.
Affect execution behaviour
==========================
``Module`` is a global JavaScript object, with attributes that Emscripten-generated code calls at various points in its execution.
Developers provide an implementation of ``Module`` to control, for example, how notifications from Emscripten are displayed, which files that are loaded before the main loop is run, etc. For more information see :ref:`module`.
Developers provide an implementation of ``Module`` to control how notifications from Emscripten are displayed, which files that are loaded before the main loop is run, and a number other behaviours. For more information see :ref:`module`.
Environment variables
==============================
=====================
Sometimes compiled code needs to access environment variables (for instance, in C, by calling the ``getenv()`` function). Just as with the file system, Emscripten generated JavaScript cannot access the computer's environment variables directly, so a virtualised environment is provided.
Sometimes compiled code needs to access environment variables (for instance, in C, by calling the ``getenv()`` function). Emscripten generated JavaScript cannot access the computer's environment variables directly, so a "virtualised" environment is provided.
The JavaScript object ``ENV`` contains these virtualised environment variables, and by modifying it you can pass variables to your compiled code. Care must be taken to ensure that the ``ENV`` variable has been initialised by Emscripten before it is modified - using :js:attr:`Module.preRun` is a convenient way to do this.
The JavaScript object ``ENV`` contains the virtualised environment variables, and by modifying it you can pass variables to your compiled code. Care must be taken to ensure that the ``ENV`` variable has been initialised by Emscripten before it is modified using :js:attr:`Module.preRun` is a convenient way to do this.
For example to set an environment variable ``MY_FILE_ROOT`` to be ``"/usr/lib/test/"`` you could add the following JavaScript to your :ref:`setup code <module-creating>`:
For example, to set an environment variable ``MY_FILE_ROOT`` to be ``"/usr/lib/test/"`` you could add the following JavaScript to your ``Module`` :ref:`setup code <module-creating>`:
.. code:: javascript
Module.preRun.push(function() {ENV.MY_FILE_ROOT = "/usr/lib/test"})
WebIDL Binder
=============
The :ref:`WebIDL-Binder` is a tool to make C++ classes usable from JavaScript, as JavaScript classes.
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.
Embind
======
:ref:`embind` is a tool to communicate between JavaScript and C++, in a C++-like manner. It is not as lightweight or as optimizable as Emscripten JavaScript libraries or the :ref:`WebIDL-Binder`, but it does allow communication in both directions between JavaScript and the C code.

Просмотреть файл

@ -5,52 +5,20 @@ WebIDL Binder (wiki-import)
===========================
.. note:: This article was migrated from the wiki (Fri, 25 Jul 2014 04:21) and is now the "master copy" (the version in the wiki will be deleted). It may not be a perfect rendering of the original but we hope to fix that soon!
WebIDL Binder
=============
The WebIDL binder is the third tool providing bindings glue between C++ and JavaScript in Emscripten. The first was the "bindings generator", which was a hackish experiment (despite its hackishness, it managed to be good enough for ammo.js and box2d.js). The second was Embind, which is fairly high-level and supports mapping sophisticated C++11 constructs between C++ and JavaScript.
The WebIDL binder is the third tool providing bindings glue between C++
and JS in Emscripten. The first was the "bindings generator", which was
a hackish experiment (despite its hackishness, it managed to be good
enough for ammo.js and box2d.js). The second was Embind, which is fairly
high-level and supports mapping sophisticated C++11 constructs between
C++ and JS.
The goals of the WebIDL binder is to be low-level, efficient, and simpler than Embind, while robust and more maintainable than the bindings generator. Like Embind, it requires explicit declarations of what to bind together, but like the bindings generator, it does so at a low-level which is simpler to optimize.
The goals of the WebIDL binder is to be low-level, efficient, and
simpler than Embind, while robust and more maintainable than the
bindings generator. Like Embind, it requires explicit declarations of
what to bind together, but like the bindings generator, it does so at a
low-level which is simpler to optimize.
The level at which it works is `WebIDL <http://heycam.github.io/webidl/>`_, a language used to describe the interfaces between JavaScript and the browser's native code in C++. WebIDL was designed for the particular purpose of gluing together C++ and JavaScript, so it is a natural choice here; also, there are lots of already-written WebIDL files for browser APIs, which could eventually be reused.
The level at which it works is
`WebIDL <http://heycam.github.io/webidl/>`__, a language used to
describe the interfaces between JS and the browser's native code in C++.
WebIDL was designed for the particular purpose of gluing together C++
and JS, so it is a natural choice here; also, there are lots of
already-written WebIDL files for browser APIs, which could eventually be
reused.
The WebIDL binder is currently focused on wrapping C++ code so it is usable from JavaScript (write a C++ class, use it as a library in JavaScript as if it were a normal JavaScript library), which is what the bindings generator does. Embind also supports wrapping in the other direction, which the WebIDL binder might do some day. In particular, it should be easy to let C++ call web APIs in a natural way by using WebIDL files describing those APIs.
The WebIDL binder is currently focused on wrapping C++ code so it is
usable from JS (write a C++ class, use it as a library in JS as if it
were a normal JS library), which is what the bindings generator does.
Embind also supports wrapping in the other direction, which the WebIDL
binder might do some day. In particular, it should be easy to let C++
call web APIs in a natural way by using WebIDL files describing those
APIs.
For a complete working example, see ``test_webidl`` in the test suite.
As always, the test suite code is guaranteed to work, so it's a good
place to learn from. Another good example is
`ammo.js <https://github.com/kripken/ammo.js/blob/webidl/ammo.idl>`__
which is the primary consumer of this tool.
For a complete working example, see ``test_webidl`` in the test suite. As always, the test suite code is guaranteed to work, so it's a good place to learn from. Another good example is `ammo.js <https://github.com/kripken/ammo.js/blob/webidl/ammo.idl>`_ which is the primary consumer of this tool.
Wrapping a C++ class
--------------------
====================
The first stage is to create a WebIDL file. That describes the C++ class
you are going to wrap. This basically duplicates a little info from your
C++ header file, in a format that is explicitly designed for easy
parsing and to be able to represent things in a way that is convenient
for connecting to JS.
The first stage is to create a WebIDL file. That describes the C++ class you are going to wrap. This basically duplicates a little info from your C++ header file, in a format that is explicitly designed for easy parsing and to be able to represent things in a way that is convenient for connecting to JavaScript.
For example, let's say you have these C++ classes:
@ -83,13 +51,9 @@ You could write this IDL file to describe them:
void doSomething();
};
Note how both have a constructor (a function returning void that has the
same name as the interface), which allows you to construct them from
JS). Otherwise the definitions are fairly straightforward.
Note how both have a constructor (a function returning void that has the same name as the interface), which allows you to construct them from JavaScript). Otherwise the definitions are fairly straightforward.
Note that types in WebIDL are not identically named to 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 JS string.
Note that types in WebIDL are not identically named to 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.
To generate bindings code, run
@ -97,9 +61,7 @@ To generate bindings code, run
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``.
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
@ -113,12 +75,10 @@ To use those files, you should
#include<glue.cpp>
That way your emcc command will include both the C++ glue and the JS
glue, which are built to work together. The output should contain
everything you need, with the classes now usable through JS.
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 JS
-----------------------
Using C++ classes in JavaScript
=======================================
Continuing the above example, you can write things like
@ -134,14 +94,12 @@ Continuing the above example, you can write things like
and so forth.
Extended properties
-------------------
==========================
By default normal-looking IDL can bind a lot of regular C++ to JS.
However, you may need to use IDL extended properties to handle various
things.
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
~~~~~~~~~~~~~
-------------
For example, the IDL binder assumes when it sees
@ -149,33 +107,26 @@ For example, the IDL binder assumes when it sees
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
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 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
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.
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
If a class cannot be deleted (because the destructor is private), you can do
::
@ -185,17 +136,14 @@ can do
};
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.
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,
If you have a C++ class that is inside a namespace or another class, you can still wrap it, by giving a prefix,
::
@ -204,11 +152,10 @@ can still wrap it, by giving a prefix,
..
};
Everywhere that ``Inner`` appears in the C++ glue code, it will show up
as ``Space::Inner``, and compilation can succeed.
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
@ -216,15 +163,12 @@ 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).
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 JS
----------------------------------
JSImplementation: Subclasses in JavaScript
====================================================
Imagine you have a class that has a virtual method called from C++, and
you want to subclass it and implement it in JS. To do so, you can use
the JSImplementation option, for example in this IDL:
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:
::
@ -235,96 +179,50 @@ the JSImplementation option, for example in this IDL:
void virtualFunc2();
};
``Base`` is the C++ class, and ImplJS does not exist in your C++ code.
``JSImplementation="Base"`` means "this class will be a JS
implementation of Base". After running the bindings generator and
compiling, you can do this:
``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:
::
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 JS code written here.
When C++ code has a pointer to a ``Base`` instance and calls ``virtualFunc``, that call will reach the JavaScript code written here.
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 JS. If there is nothing in JS to be called, it
goes up through the prototype chain and calls that same C++ function
once more).
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).
Other Issues
------------
=============
Object cleanup
~~~~~~~~~~~~~~
--------------------------
If you create a JS object wrapping a C++ object, we call the C++
constructor for you transparently. However, if nothing else holds on to
the JS object, it will be GC'd, but we have no way to know that (due to
how JS works) - so we can't call the destructor for you.
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 JS object as well.
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).
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
~~~~~~~~~~~~~~~~~~~~~~~~
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, 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:
- ``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!
- ``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
- ``castObject(object, Class)`` - Returns a wrapping of the same pointer but to another class ``compare(object1, object2)`` - Compares two objects' pointers
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 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()``.
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/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.
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 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).

Просмотреть файл

@ -1,7 +1,7 @@
.. _embind:
====================
embind (wiki-import)
Embind (wiki-import)
====================
.. note:: This article was migrated from the wiki (Fri, 25 Jul 2014 04:21) and is now the "master copy" (the version in the wiki will be deleted). It may not be a perfect rendering of the original but we hope to fix that soon!
@ -33,9 +33,7 @@ Imagine we want to expose a simple C++ function to JavaScript.
}
To compile the above example, run
``emcc --bind -o quick_example.js quick_example.cpp``. The resulting
**quick\_example.js** file can be loaded as a script tag or a node
module.
``emcc --bind -o quick_example.js quick_example.cpp``. The resulting **quick\_example.js** file can be loaded as a script tag or a node module.
.. code:: html
@ -47,10 +45,7 @@ module.
</script>
</html>
The code in the ``EMSCRIPTEN_BINDINGS`` block runs when the JavaScript
file is initially loaded. Notice that lerp's parameter types and return
type are automatically inferred by *embind*. All symbols exposed by
*embind* are available on the Emscripten ``Module`` object.
The code in the ``EMSCRIPTEN_BINDINGS`` block runs when the JavaScript file is initially loaded. Notice that lerp's parameter types and return type are automatically inferred by *embind*. All symbols exposed by *embind* are available on the Emscripten ``Module`` object.
Classes
=======
@ -104,10 +99,7 @@ Exposing classes to JavaScript requires a few more steps. An example:
Memory Management
=================
JavaScript, specifically ECMA-262 Edition 5.1, does not support
finalizers or weak references with callbacks. Thus, JavaScript code must
explicitly delete any C++ object handles it has received. Otherwise the
Emscripten heap will grow indefinitely.
JavaScript, specifically ECMA-262 Edition 5.1, does not support finalizers or weak references with callbacks. Thus, JavaScript code must explicitly delete any C++ object handles it has received. Otherwise the Emscripten heap will grow indefinitely.
.. code:: javascript
@ -122,10 +114,7 @@ Emscripten heap will grow indefinitely.
Value Types
===========
Imagine a common, small data type, like ``Point2f``. Because manual
memory management for basic types is onerous, *embind* provides support
for value types. Value arrays are converted to and from JavaScript
Arrays and value objects are converted to and from JavaScript Objects.
Imagine a common, small data type, like ``Point2f``. Because manual memory management for basic types is onerous, *embind* provides support for value types. Value arrays are converted to and from JavaScript Arrays and value objects are converted to and from JavaScript Objects.
.. code:: cpp
@ -168,8 +157,7 @@ Advanced Class Concepts
Raw Pointers
------------
Because raw pointers have unclear lifetime semantics, *embind* requires
their use to be marked with ``allow_raw_pointers()``.
Because raw pointers have unclear lifetime semantics, *embind* requires their use to be marked with ``allow_raw_pointers()``.
.. code:: cpp
@ -183,11 +171,7 @@ their use to be marked with ``allow_raw_pointers()``.
External Constructors
---------------------
There are two ways to specify constructors on a class. The zero-argument
template form invokes the natural constructor with the arguments
specified in the template. However, if you pass a function pointer as
the constructor, then invoking ``new`` from JavaScript calls said
function and returns its result.
There are two ways to specify constructors on a class. The zero-argument template form invokes the natural constructor with the arguments specified in the template. However, if you pass a function pointer as the constructor, then invoking ``new`` from JavaScript calls said function and returns its result.
.. code:: cpp
@ -204,9 +188,7 @@ function and returns its result.
Smart Pointers
--------------
To manage object lifetime with smart pointers, *embind* must be told
about the smart pointer type. For example, imagine managing a class C's
lifetime with ``std::shared_ptr<C>``.
To manage object lifetime with smart pointers, *embind* must be told about the smart pointer type. For example, imagine managing a class C's lifetime with ``std::shared_ptr<C>``.
.. code:: cpp
@ -217,12 +199,9 @@ lifetime with ``std::shared_ptr<C>``.
;
}
At this point, functions can return ``std::shared_ptr<C>`` or take
``std::shared_ptr<C>`` as arguments. However, ``new Module.C()`` would
still return a raw pointer.
At this point, functions can return ``std::shared_ptr<C>`` or take ``std::shared_ptr<C>`` as arguments. However, ``new Module.C()`` would still return a raw pointer.
To return a ``shared_ptr<C>`` from the constructor, write the following
instead:
To return a ``shared_ptr<C>`` from the constructor, write the following instead:
.. code:: cpp
@ -243,17 +222,12 @@ unique\_ptr
Custom Smart Pointers
~~~~~~~~~~~~~~~~~~~~~
To teach *embind* about custom smart pointer templates, specialize the
``smart_ptr_trait`` template. See **bind.h** for details and an example.
To teach *embind* about custom smart pointer templates, specialize the ``smart_ptr_trait`` template. See **bind.h** for details and an example.
Non-member-functions on the JavaScript prototype
------------------------------------------------
Methods on the JavaScript class prototype can be non-member functions,
as long as the instance handle can be converted to the first argument of
the non-member function. The classic example is when the function
exposed to JavaScript does not exactly match the behavior of a C++
method.
Methods on the JavaScript class prototype can be non-member functions, as long as the instance handle can be converted to the first argument of the non-member function. The classic example is when the function exposed to JavaScript does not exactly match the behavior of a C++ method.
.. code:: cpp
@ -278,22 +252,17 @@ method.
;
}
If JavaScript calls ``Array10.prototype.get`` with an invalid index, it
will return ``undefined``.
If JavaScript calls ``Array10.prototype.get`` with an invalid index, it will return ``undefined``.
Deriving From C++ Classes in JavaScript
---------------------------------------
If C++ classes have virtual or abstract member functions, it's possible
to override them in JavaScript. Because JavaScript has no knowledge of
the C++ vtable, *embind* needs a bit of glue code to convert C++ virtual
function calls into JavaScript calls.
If C++ classes have virtual or abstract member functions, it's possible to override them in JavaScript. Because JavaScript has no knowledge of the C++ vtable, *embind* needs a bit of glue code to convert C++ virtual function calls into JavaScript calls.
Abstract Methods
~~~~~~~~~~~~~~~~
Let's begin with a simple case: pure virtual functions that must be
implemented in JavaScript.
Let's begin with a simple case: pure virtual functions that must be implemented in JavaScript.
.. code:: cpp
@ -315,16 +284,9 @@ implemented in JavaScript.
;
}
``allow_subclass`` adds two special methods to the Interface binding:
``extend`` and ``implement``. ``extend`` allows JavaScript to subclass
in the style exemplified by **Backbone.js**. ``implement`` is used when
you have a JavaScript object, perhaps provided by the browser or some
other library, and you want to use it to implement a C++ interface.
``allow_subclass`` adds two special methods to the Interface binding: ``extend`` and ``implement``. ``extend`` allows JavaScript to subclass in the style exemplified by **Backbone.js**. ``implement`` is used when you have a JavaScript object, perhaps provided by the browser or some other library, and you want to use it to implement a C++ interface.
By the way, note the ``pure_virtual()`` annotation on the function
binding. Specifying ``pure_virtual()`` allows JavaScript to throw a
helpful error if the JavaScript class does not override invoke().
Otherwise, you may run into confusing errors.
By the way, note the ``pure_virtual()`` annotation on the function binding. Specifying ``pure_virtual()`` allows JavaScript to throw a helpful error if the JavaScript class does not override invoke(). Otherwise, you may run into confusing errors.
``extend`` Example
~~~~~~~~~~~~~~~~~~
@ -361,15 +323,12 @@ Otherwise, you may run into confusing errors.
};
var interfaceObject = Module.Interface.implement(x);
Now ``interfaceObject`` can be passed to any function that takes an
``Interface`` pointer or reference.
Now ``interfaceObject`` can be passed to any function that takes an ``Interface`` pointer or reference.
Non-Abstract Virtual Methods
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If a C++ class has a non-pure virtual function, it can be overridden but
does not have to be. This requires a slightly different wrapper
implementation:
If a C++ class has a non-pure virtual function, it can be overridden but does not have to be. This requires a slightly different wrapper implementation:
.. code:: cpp
@ -395,9 +354,7 @@ implementation:
;
}
When implementing Base with a JavaScript object, overriding ``invoke``
is optional. The special lambda binding for invoke is necessary to avoid
infinite mutual recursion between the wrapper and JavaScript.
When implementing Base with a JavaScript object, overriding ``invoke`` is optional. The special lambda binding for invoke is necessary to avoid infinite mutual recursion between the wrapper and JavaScript.
Base Classes
------------
@ -409,15 +366,12 @@ Base Classes
class_<DerivedClass, base<BaseClass>>("DerivedClass");
}
Any member functions defined on ``BaseClass`` are then accessible to
instances of ``DerivedClass``. In addition, any function that accepts an
instance of ``BaseClass`` can be given an instance of ``DerivedClass``.
Any member functions defined on ``BaseClass`` are then accessible to instances of ``DerivedClass``. In addition, any function that accepts an instance of ``BaseClass`` can be given an instance of ``DerivedClass``.
Automatic Downcasting
~~~~~~~~~~~~~~~~~~~~~
If a C++ class is polymorphic (that is, it has a virtual method), then
*embind* supports automatic downcasting of function return values.
If a C++ class is polymorphic (that is, it has a virtual method), then *embind* supports automatic downcasting of function return values.
.. code:: cpp
@ -432,19 +386,14 @@ If a C++ class is polymorphic (that is, it has a virtual method), then
function("getDerivedInstance", &getDerivedInstance, allow_raw_pointers());
}
Calling Module.getDerivedInstance from JavaScript will return a Derived
instance handle from which all of Derived's methods are available.
Calling Module.getDerivedInstance from JavaScript will return a Derived instance handle from which all of Derived's methods are available.
Note that the *embind* must understand the fully-derived type for
automatic downcasting to work.
Note that the *embind* must understand the fully-derived type for automatic downcasting to work.
Overloaded Functions
====================
Constructors and functions can be overloaded on the number of arguments.
*embind* does not support overloading based on type. When specifying an
overload, use the ``select_overload`` helper function to select the
appropriate signature.
Constructors and functions can be overloaded on the number of arguments. *embind* does not support overloading based on type. When specifying an overload, use the ``select_overload`` helper function to select the appropriate signature.
.. code:: cpp
@ -492,8 +441,7 @@ classes".
;
}
In both cases, JavaScript accesses enumeration values as properties of
the type.
In both cases, JavaScript accesses enumeration values as properties of the type.
.. code:: javascript
@ -516,8 +464,7 @@ To expose a C++ constant to JavaScript, simply write:
val
===
``emscripten::val`` is a data type that represents any JavaScript
object.
``emscripten::val`` is a data type that represents any JavaScript object.
``val::array()`` creates a new Array.
@ -589,8 +536,7 @@ types:
| emscripten::val | anything |
+-------------------+-------------------------------------------------+
For convenience, *embind* provides factory functions to register
``std::vector<T>`` and ``std::map<K, V>`` types:
For convenience, *embind* provides factory functions to register ``std::vector<T>`` and ``std::map<K, V>`` types:
.. code:: cpp
@ -599,18 +545,20 @@ For convenience, *embind* provides factory functions to register
register_map<int,int>("MapIntInt");
}
Performance
===========
.. todo:: The following text was marked up as "Todo" in the original wiki content.
[TODO: Jukka, want to flesh this out?]
Performance
===========
Future Work
===========
[TODO: Jukka, want to flesh this out?]
- global variables
- class static variables
Future Work
===========
How does it work?
=================
- global variables
- class static variables
[TODO]
How does it work?
=================
[TODO]

Просмотреть файл

@ -1,59 +1,59 @@
.. _Using-Web-Audio-API-from-Cpp-with-the-Embind-val-class:
====================================================================
Using Web Audio API from Cpp with the Embind val class (wiki-import)
====================================================================
==========================================================================
Using Web Audio API from C++ with the Embind::val class (wiki-import)
==========================================================================
.. note:: This article was migrated from the wiki (Mon, 04 Aug 2014 23:20) and is now the "master copy" (the version in the wiki will be deleted). It may not be a perfect rendering of the original but we hope to fix that soon!
(The title of this page is supposed to say C++ but GitHub turns the
pluses into spaces.)
(The title of this page is supposed to say C++ but GitHub turns the pluses into spaces.)
Overview
--------
========
embind provides a C++ class, ``emscripten::val``, that offers convenient
access to JavaScript values.
embind provides a C++ class, ``emscripten::val``, that offers convenient access to JavaScript values.
With an ``emscripten::val``, you can call it, read and write properties,
or coerce it to a C++ value like a ``bool``, ``int``, or
``std::string``.
With an :cpp:func:`emscripten::val`, you can call it, read and write properties, or coerce it to a C++ value like a ``bool``, ``int``, or ``std::string``.
Its specific capabilities are enumerated in
:ref:`https://github.com/kripken/emscripten/blob/master/system/include/emscripten/val.h`
Its specific capabilities are enumerated in :ref:`val-h`
Example
-------
========
\`\`\`c++ #include #include #include
.. code-block:: cpp
using namespace emscripten;
#include <emscripten/val.h>
#include <stdio.h>
#include <math.h>
// based on the excellent Web Audio tutorial at //
http://stuartmemo.com/making-sine-square-sawtooth-and-triangle-waves/
using namespace emscripten;
int main() { val AudioContext = val::global("AudioContext"); if
(!AudioContext.as()) { printf("No global AudioContext, trying
webkitAudioContext"); AudioContext = val::global("webkitAudioContext");
} printf("Got an AudioContext"); val context = AudioContext.new\_(); val
oscillator = context.call("createOscillator");
// based on the excellent Web Audio tutorial at
// http://stuartmemo.com/making-sine-square-sawtooth-and-triangle-waves/
int main() {
val AudioContext = val::global("AudioContext");
if (!AudioContext.as<bool>()) {
printf("No global AudioContext, trying webkitAudioContext\n");
AudioContext = val::global("webkitAudioContext");
}
printf("Got an AudioContext\n");
val context = AudioContext.new_();
val oscillator = context.call<val>("createOscillator");
printf("Configuring oscillator\n");
oscillator.set("type", val("triangle"));
oscillator["frequency"].set("value", val(261.63)); // Middle C
printf("Playing\n");
oscillator.call<void>("connect", context["destination"]);
oscillator.call<void>("start", 0);
printf("All done!\n");
}
Compile with:
::
printf("Configuring oscillator\n");
oscillator.set("type", val("triangle"));
oscillator["frequency"].set("value", val(261.63)); // Middle C
printf("Playing\n");
oscillator.call<void>("connect", context["destination"]);
oscillator.call<void>("start", 0);
printf("All done!\n");
}
::
Compile with:
em++ -O2 -Wall -Werror --bind -o oscillator.html oscillator.cpp \`\`\`
em++ -O2 -Wall -Werror --bind -o oscillator.html oscillator.cpp

Просмотреть файл

@ -1,15 +1,11 @@
=================================================
Function Pointer Issues (under-construction)
=================================================
==========================================
Function Pointer Issues (ready-for-review)
==========================================
There are three general issues with function pointers:
#.
:term:`Clang` generates different code for C and C++ calls when a structure is passed **by value** (for completeness, one convention is ``struct byval`` and the other is ``field a, field b``). The two formats are incompatible with each other, and you should get a warning during compilation:
::
Warning: Casting potentially incompatible function pointer
:term:`Clang` generates different code for C and C++ calls when a structure is passed **by value** (for completeness, one convention is ``struct byval`` and the other is ``field a, field b``). The two formats are incompatible with each other, and you may get a runtime warning.
The workaround is to pass the structure by reference, or simply not mix C and C++ in that location (for example, rename the **.c** file to **.cpp**).
@ -18,9 +14,11 @@ There are three general issues with function pointers:
#.
:ref:`Function pointer casts <Asm-pointer-casts>` can cause function pointer calls to fail.
Each function type in **asm.js** has its own table — if you change the type of the pointer the calling code will look for the function pointer in the wrong table. For example, consider a function that is say ``int (int)`` (return ``int``, receive ``int``) and that will be indexed in the table ``FUNCTION_TABLE_ii``. If you cast a function pointer to it to ``void (int)`` (no return, receive ``int``), then the code will look for the function in ``FUNCTION_TABLE_vi``.
Function pointers are stored in a specific table based on their signature when they are *declared*. When a function is called the code searches for it in the table associated with its *current* function pointer signature. If the function pointer used to call the function does not have the same signature as the original type, the calling code will look for it in the wrong table.
You should see compilation warnings for this error:
For example, consider a function declared as ``int (int)`` (return ``int``, receive ``int``) and hence added to the table ``FUNCTION_TABLE_ii``. If you cast a function pointer to it to ``void (int)`` (no return, receive ``int``), then the code will look for the function in ``FUNCTION_TABLE_vi``.
You may see compilation warnings for this error:
::
@ -31,16 +29,18 @@ There are three general issues with function pointers:
#.
When using optimisation :ref:`-O2 <emcc-O2>` and above, comparing function pointers of different types can give false positives, and bugs with incorrect function pointers potentially more misleading. To check if this is causing issues, you can compile with `ALIASING_FUNCTION_POINTERS <https://github.com/kripken/emscripten/blob/master/src/settings.js#L201>`_ unset (``-s ALIASING_FUNCTION_POINTERS=0``).
When using optimisation :ref:`-O2 <emcc-O2>` and above, comparing function pointers of different types can give false positives, and bugs with incorrect function pointers are potentially more misleading. To check if this is the cause of problems with your code, you can compile with `ALIASING_FUNCTION_POINTERS <https://github.com/kripken/emscripten/blob/master/src/settings.js#L201>`_ unset (``-s ALIASING_FUNCTION_POINTERS=0``).
.. note:: In **asm.js** function pointers are stored within a function-type specific table.
.. note:: In **asm.js**, function pointers are stored within a function-type specific table.
At lower levels of optimisation each function pointer has a unique index value across all the function-type tables (a function pointer will exist at a specific index in one table only, and there will be an empty slot at that index in all the other tables). As a result, comparing function pointers (indexes) gives an accurate result, and attempting to call a function pointer in the wrong table will throw an error as that index will be empty.
A optimisation ``O2`` and above, the tables are optimised so that all the function pointers are in sequential indexes. This is a useful optimisation because the tables are much more compact without all the empty slots, but it does mean that the function index is no longer "globally" unique. Function is now uniquely indexed using both its table and its index within that table. As a result:
A optimisation ``O2`` and above, the tables are optimised so that all the function pointers are in sequential indexes. This is a useful optimisation because the tables are much more compact without all the empty slots, but it does mean that the function index is no longer "globally" unique. Each function is now uniquely indexed using both its table and its index within that table.
- Comparisons of the function pointers can give a false positive, because functions of different types can share the same index.
- Mistakes in function pointer code can be more difficult to debug, because they result in the wrong code being called rather than an explicit error (as in the case of a "hole" in the table).
As a result, at higher optimisations:
- Comparisons of the function pointers can give a false positive, because functions of different types can have the same index (albeit in different tables).
- Mistakes in function pointer code can be more difficult to debug, because they result in the wrong code being called rather than an explicit error (as is raised in the case of a "hole" in the table).
.. _Asm-pointer-casts:
@ -48,11 +48,16 @@ There are three general issues with function pointers:
Asm pointer casts
=================
As mentioned :ref:`above <function-pointer-issues-point-asmjs>`, in **asm.js** mode function pointers must be called using their correct type. This is because each function pointer type has its own table: casting the pointer to another type will cause code to look for the function pointer in the wrong table (and so the call will fail).
As mentioned :ref:`above <function-pointer-issues-point-asmjs>`, in **asm.js** mode function pointers must be called using their correct type or the call will fail. This is because each function pointer is stored in a specific table based on its signature when it is declared: casting the pointer to another type will cause calling code to look for the function pointer in the wrong place.
.. note:: Having a separate table for each type of function pointer allows the JavaScript engine to know the exact type of each function pointer call, and optimize those calls much better than normally.
.. note:: Having a separate table for each type of function pointer allows the JavaScript engine to know the exact type of each function pointer call, and optimize those calls much better than would otherwise be possible.
Let's look at an example:
There are two solutions to this problem (the second is preferred):
- Cast the function pointer back to the correct type before it is called. This is problematic because it requires that the caller knows the original type.
- Make an adapter function that does not need to be cast, and will hence be found in the correct function-pointer table. From the adaptor function call the original function.
For a real-world example, consider the code below:
.. code:: cpp
@ -92,7 +97,9 @@ Let's look at an example:
callFunctions(functionList, 3);
}
This code runs (and works) when compiled to machine code. You can try it by saving the code as **main.c** and executing: **cc main.c** and then **./a.out**. You'll see this output:
The code defines three functions with different signatures: ``voidReturn`` of type ``vi`` (``void (int)``), ``intReturn`` of type ``ii``, and ``voidReturnNoParam`` of type ``v``. These function pointers are cast to type ``vi`` and added to a list. The functions are then called using the function pointers in the list.
The code runs (and works) when compiled to machine code. You can try it by saving the code as **main.c** and executing: **cc main.c** and then **./a.out**. You'll see this output:
::
@ -100,9 +107,15 @@ This code runs (and works) when compiled to machine code. You can try it by savi
intReturn: hello world
voidReturnNoParam:
However, this code will break in Emscripten. You can compile this on your own by saving this code as ``main.c`` and executing: ``emcc -O2 -g -s ASM_JS=1 main.c -o main.html``. Then load it into your browser.
However, the code fails with a runtime exception in Emscripten, and displays the console output:
To see why this breaks, let's look at the html source. You should be able to find this section:
::
voidReturn: hello world
.. note:: You can try this yourself. Save the code as **main.c**, compile using ``emcc -O0 main.c -o main.html``, and then load **main.html** into a browser.
To see why this breaks, look at the html source. You should be able to find the section below in the code, showing the methods are stored in function tables based on their *original* function signatures.
.. code:: javascript
@ -111,11 +124,12 @@ To see why this breaks, let's look at the html source. You should be able to fin
var FUNCTION_TABLE_iii = [b2,b2,b2,b2,b2,b2,b2,b2];
var FUNCTION_TABLE_v = [b3,b3,b3,b3,_voidReturnNoParam,b3,b3,b3];
.. note:: Because we are compiling using :ref:`-g <emcc-g>` the function names aren't mangled and we can see there are different function tables categorized by the signature of the function.
When Emscripten-generated code executes a function, it looks up the function in the table based on its signature. In our ``callFunctions()`` method we are passed a list of functions with the signature ``vi``, so Emscripten looks for all the functions in the table ``FUNCTION_TABLE_vi``. Only the function ``voidReturn`` is found in ``FUNCTION_TABLE_vi``. The other two functions are not found, causing the exception.
When Emscripten-generated code executes a function, it will look up the function in the table based on its signature and execute it. In our ``callFunctions()`` method we are passed a list of functions of the signature ``vi`` and for that reason, ``FUNCTION_TABLE_vi`` is the table used to find them. It doesn't matter that in ``main()`` we've added compatible functions to ``functionList``. They will not be found because their signature is different.
As stated above, there are two solutions to this problem.
The code fragment below shows how we can cast the function pointer back to its original signature just before calling it, so that it is found in the correct table. This requires the receiver of the table to have special knowledge about what is in the list (you can see this in the special case for index ``1`` in the while loop). Additionally, :ref:`emcc <emccdoc>` will continue to complain about the original cast taking place in ``main()`` when adding the function to ``functionList[1]``.
There are two ways to fix this. The first is to cast the function pointer back to its original signature just before calling it:
.. code:: cpp
@ -131,9 +145,7 @@ There are two ways to fix this. The first is to cast the function pointer back t
}
}
This requires the receiver of the table to have special knowledge about what is in the list (you can see this in the special case for index ``1`` in the while loop). Additionally, :ref:`emcc <emccdoc>` will continue to complain about the original cast taking place in ``main()`` when adding the function to ``functionList[1]``.
A second (and better) solution is to make an adapter function which calls the original function and to place the *adapter* in the list.
The code fragment below shows how to make and use an adapter function which calls the original function. The adapter is defined with the same signature as it will have when called, and is hence available in the expected function-pointer table.
.. code:: cpp
@ -150,5 +162,3 @@ A second (and better) solution is to make an adapter function which calls the or
callFunctions(functionList, 3);
}
Here, we've made a function ``voidReturnNoParamAdapter()`` and added that to ``functionList[2]``.