Skip to content

Commit

Permalink
Update Tutorials with [[cheerp::genericjs]]
Browse files Browse the repository at this point in the history
  • Loading branch information
carlopi committed Feb 25, 2022
1 parent 6fe07d2 commit 84cb908
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 61 deletions.
6 changes: 5 additions & 1 deletion pages/Browser-side-programming-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,15 @@ You can access the ```document``` global object directly from C++ code. In the n
#include <cheerp/client.h> //Misc client side stuff
#include <cheerp/clientlib.h> //Complete DOM/HTML5 interface

[[cheerp::genericjs]]
void outputNumberOfElementsToTheConsole()
{
double number=client::document.getElementsByTagName("*")->get_length();
client::console.log("Live elements = ", number);
}

//This function will be called only after the DOM is fully loaded
[[cheerp::genericjs]]
void loadCallback()
{
client::console.log("Hi from loadCallback!");
Expand All @@ -49,6 +51,7 @@ void loadCallback()
client::console.log("Bye from loadCallback!");
}

[[cheerp::genericjs]]
void webMain()
{
client::console.log("Hi from webMain!");
Expand All @@ -61,7 +64,7 @@ void webMain()
```

Compiling with:
```/opt/cheerp/bin/clang++ -target cheerp dom.cpp -o dom.js```
```/opt/cheerp/bin/clang++ -O3 dom.cpp -o dom.js```

Now we need a [html file](/tutorials/dom_access/dom.html):
```html
Expand Down Expand Up @@ -123,6 +126,7 @@ Cheerp works at the same level as JavaScript. It is designed to complement or re
If you want to manipulate the DOM at run-time you can use the same APIs you would use when writing JavaScript. In the following example we will create two DOM elements and set up event handling using the DOM APIs exposed by the browser.

```c++
[[cheerp::genericjs]]
void setupInputAndDisplay()
{
using namespace client;
Expand Down
23 changes: 13 additions & 10 deletions pages/Cheerp-Tutorial-DOM-Manipulation.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,23 @@ title: Tutorial - DOM manipulation

# Using DOM APIs

The following example shows how to write a simple Web application that uses the Browser's DOM functionalities. Check our [online API reference](http://leaningtech.com/API) for extensive documentation of Web APIs in the client namespace using Cheerp.
The following example shows how to write a simple Web application that uses the Browser's DOM functionalities. Check our for example [clientlib.h](https://github.com/leaningtech/cheerp-utils/blob/master/include/client/clientlib.h) for a glance of the Web API forward declared in the client namespace using Cheerp.

```c++
#include <cheerp/clientlib.h>

[[cheerp::genericjs]]
void webMain()
{
// client::document represent the Javascript document object
// client::document represent the JavaScript document object
client::Element* titleElement = \
client::document.getElementById("pagetitle");
// Wide strings can also be implicitly converted to JavaScript Strings
titleElement->set_textContent(L"Exciting dynamic title,\
with non-Latin letters ΩЯÅ");
}
```
Save it as `dom1.cpp` and compile it (how? `/opt/cheerp/bin/clang++ -target cheerp dom1.cpp -o dom1.js`)
Save it as `dom1.cpp` and compile it (how? `/opt/cheerp/bin/clang++ -O3 dom1.cpp -o dom1.js`)

Now embed it in a web page like:

Expand Down Expand Up @@ -54,6 +55,7 @@ Let's extend the previous program to revert the original text after 3 seconds.
// client is a regular C++ namespace, so we can reduce verbosity by using it
using namespace client;

[[cheerp::genericjs]]
void webMain()
{
Element* titleElement=document.getElementById("pagetitle");
Expand Down Expand Up @@ -106,7 +108,7 @@ Let's use the JavaScript ```changeTitle``` function from C++

// We need to extend the client namespace to declare our
// custom JavaScript function
namespace client
namespace [[cheerp::genericjs]] client
{
// The name should be the same as the JavaScript one
// The parameters needs to be a const client::String reference
Expand All @@ -116,6 +118,7 @@ namespace client

using namespace client;

[[cheerp::genericjs]]
void webMain()
{
Element* titleElement=document.getElementById("pagetitle");
Expand All @@ -138,6 +141,7 @@ When declaring JavsScript methods in C++ you can use the following data types:
You can also use the ```__asm__``` keyword to inline JavaScript code in the middle of C++ code

```c++
[[cheerp::genericjs]]
void webMain()
{
__asm__("console.log('Inline JS example')");
Expand All @@ -157,7 +161,7 @@ using namespace client;
// The class can of course have any name
// The [[cheerp::jsexport]] attribute tells Cheerp to make
// the class available to JavaScript code
class [[cheerp::jsexport]] JsBridge
class [[cheerp::jsexport]][[cheerp::genericjs]] JsBridge
{
private:
// The class is allowed to have member variables
Expand All @@ -175,10 +179,7 @@ public:
}
};

// An entry point, even if empty, is still required
void webMain()
{
}
// Note that entry point is missing, this is still fine
```

We also need to modify the HTML page to interact with the jsexport-ed class
Expand All @@ -193,8 +194,9 @@ We also need to modify the HTML page to interact with the jsexport-ed class
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script>
var jsBridge=null;
function callCPPCode()
async function callCPPCode()
{
await JsBridge.promise;
if(!jsBridge)
jsBridge=new JsBridge();
var ret=jsBridge.addAndReturn(3);
Expand All @@ -221,6 +223,7 @@ We will show how to use ```XMLHttpRequest``` to retrieve a file. Please note tha

using namespace client;

[[cheerp::genericjs]]
void webMain()
{
XMLHttpRequest* xhr=new XMLHttpRequest();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ We only need to reference the JavaScript loader, and the WebAssembly binary will

You now need to start a local HTTP server in the directory containing pong.html, pong.js and pong.wasm. Node’s ```http-server -p 8081``` command work well, but any server will do.

Visit your page, for example “http://127.0.0.1:8081” with any browser, and Yyou should see something like this:
Visit your page, for example “http://127.0.0.1:8081” with any browser, and you should see something like this:

<img src="{{site.baseurl}}/tutorials/tutorial_1/pong1/pong1.png" width="600px">

Expand Down
18 changes: 11 additions & 7 deletions pages/JavaScript-interoperability.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The ```[[cheerp::jsexport]]``` attribute can be applied to C++ class and struct
A basic example of its usage would be:

```c++
class [[cheerp::jsexport]] JsStruct
class [[cheerp::jsexport]][[cheerp::genericjs]] JsStruct
{
private:
float a;
Expand All @@ -32,7 +32,8 @@ public:
}
};

[[cheerp::jsexport]] int factorial(int N) {
[[cheerp::jsexport]]
int factorial(int N) {
if (N < 2)
return 1;
return N * factorial(N-1);
Expand All @@ -48,8 +49,7 @@ testExport.test();
console.log(factorial(5));
```


Special considerations apply when using the ```jsexport``` attribute and WebAssembly output, for more information [see here](WebAssembly-output#using-cheerpjsexport-in-combination-with-webassembly).
Classes or struct that have to be JSExported have to tagged with both ```[[cheerp::jsexport]]``` and ```[[cheerp::genericjs]]```.

# The \__asm__ keyword

Expand All @@ -71,7 +71,7 @@ assert(stringLength == stringFromDom->get_length());
The syntax follows the usual conventions of inline asm code, namely
```
``
__asm__(code : output constraints : input constraints : clobber constraints)
```

Expand All @@ -84,6 +84,9 @@ There are some Cheerp specific limitations at this time:

A working example/tutorial is here: [example of _ asmjs _ and namespace client on Github](https://gist.github.com/carlopi/c36e9b8f0eaf72c569491fadac331707)

Code inside ```__asm__``` tag should never throw to the external scope.


## Clobbering names

Cheerp minifies the output by default (unless the ```-cheerp-pretty-code``` option is used). This happens by assigning the smallest possible symbols to the most used local or global variables. If you need to use temporary variables in inline asm code you need to declare those variables in the clobber list, for example
Expand All @@ -100,7 +103,7 @@ All names declared as clobbered will be globally excluded from the list of symbo

# The CHEERP_OBJECT macro

A common use case for inline asm is to return a literal object to javascript:
A common use case for inline asm is to return a literal object to JavaScript:
```
double field1 = 1;
client::String* field2 = new client::String("hello");
Expand All @@ -126,11 +129,12 @@ Currently the macro has the following limitations:
Cheerp treats every function and class inside the ```client``` namespace as a declaration for something implemented by the browser or JavaScript. You are free to add new declarations there for functions implemented in JavaScript. For example:

```c++
namespace client
namespace [[cheerp::genericjs]] client
{
int someJavaScriptMethod(int arg1, const String& arg2);
}

[[cheerp::genericjs]]
void webMain()
{
printf("JavaScript returned %i\n", client::someJavaScriptMethod(42, "This is converted to a JavaScript String"));
Expand Down
79 changes: 39 additions & 40 deletions pages/Tutorial-Hello-Wasm.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Tutorial - Hello, Wasm!
---

This tutorial will cover the basics of using Cheerp to compile a generic C++ source file to Javascript and Javascript+WebAssembly.
This tutorial will cover the basics of using Cheerp to compile a generic C++ source file to JavaScript and JavaScript+WebAssembly.

We will show what Wasm is expecially good at: doing the computational heavy lifting.

Expand All @@ -23,43 +23,43 @@ and then we run it:

the output to the console will be something like:
```
1.6547e-05s to sieve in the interval (1, 10) 4 primes found
2.13e-05s to sieve in the interval (1, 100) 25 primes found
7.999e-06s to sieve in the interval (1, 1000) 168 primes found
1.0718e-05s to sieve in the interval (1, 10000) 1229 primes found
5.3065e-05s to sieve in the interval (1, 100000) 9592 primes found
0.000476074s to sieve in the interval (1, 1e+06) 78498 primes found
0.00526432s to sieve in the interval (1, 1e+07) 664579 primes found
0.0631561s to sieve in the interval (1, 1e+08) 5761455 primes found
0.776708s to sieve in the interval (1, 1e+09) 50847534 primes found
11.7862s to sieve in the interval (1, 1e+10) 455052511 primes found
4.0052e-05s to sieve in the interval (1, 10) 4 primes found
5.8115e-05s to sieve in the interval (1, 100) 25 primes found
2.2484e-05s to sieve in the interval (1, 1000) 168 primes found
3.3346e-05s to sieve in the interval (1, 10000) 1229 primes found
0.000181305s to sieve in the interval (1, 100000) 9592 primes found
0.00166905s to sieve in the interval (1, 1e+06) 78498 primes found
0.0170393s to sieve in the interval (1, 1e+07) 664579 primes found
0.103364s to sieve in the interval (1, 1e+08) 5761455 primes found
0.934908s to sieve in the interval (1, 1e+09) 50847534 primes found
16.2481s to sieve in the interval (1, 1e+10) 455052511 primes found
```

Nice, the program works, we can give it a go with Cheerp.


Cheerp Javascript compiling
Cheerp JavaScript compiling
-------

<code><b>/opt/cheerp/bin/clang++ -target cheerp</b> segmented_sieve.cpp -o segmented_sieve<b>.js</b> -O3</code>
<code><b>/opt/cheerp/bin/clang++ -target cheerp</b> segmented_sieve.cpp -o segmented_sieve.js -O3</code>

and then we run it:
`nodejs segmented_sieve.js`

the output to the console will be something like:
```
0.005s to sieve in the interval (1, 10) 4 primes found
0.009s to sieve in the interval (1, 100) 25 primes found
0.007s to sieve in the interval (1, 1000) 168 primes found
0.01s to sieve in the interval (1, 10000) 1229 primes found
0.035s to sieve in the interval (1, 100000) 9592 primes found
0.025s to sieve in the interval (1, 1e+06) 78498 primes found
0.053s to sieve in the interval (1, 1e+07) 664579 primes found
0.516s to sieve in the interval (1, 1e+08) 5761455 primes found
6.606s to sieve in the interval (1, 1e+09) 50847534 primes found
0.006s to sieve in the interval (1, 10) 4 primes found
0.007s to sieve in the interval (1, 100) 25 primes found
0.008s to sieve in the interval (1, 1000) 168 primes found
0.007s to sieve in the interval (1, 10000) 1229 primes found
0.044s to sieve in the interval (1, 100000) 9592 primes found
0.014s to sieve in the interval (1, 1e+06) 78498 primes found
0.06s to sieve in the interval (1, 1e+07) 664579 primes found
0.456s to sieve in the interval (1, 1e+08) 5761455 primes found
4.974s to sieve in the interval (1, 1e+09) 50847534 primes found
```

It works, internally it does equivalents calculations. There is a performance slowdown (circa 9x), but actually the relevant and non obvious thing is that automatically, without having to touch/understand the code, a C++ program has been compiled to be executable inside a browser or a JavaScript engine.
It works, internally it does equivalents calculations. There is a performance slowdown (circa 5x), but actually the relevant and non obvious thing is that automatically, without having to touch/understand the code, a C++ program has been compiled to be executable inside a browser or a JavaScript engine.

Want to see it inside a browser?

Expand All @@ -84,38 +84,37 @@ Save this [html file](tutorials/hello_wasm/segmented_sieve.html) in the same fol
Enter WebAssembly / Wasm
------

Now we will get to the serious stuff, compiling to a mix of Javascript (that will take care of managing/allocating memory and forwarding input and output) and Wasm.
Now we will get to the serious stuff, compiling to a mix of JavaScript (that will take care of managing/allocating memory and forwarding input and output) and Wasm.

The command line it's basically the same, just changing the target:

<code>/opt/cheerp/bin/clang++ -target cheerp-wasm segmented_sieve.cpp -o segmented_sieve.js -O3</code>
<code>/opt/cheerp/bin/clang++ -target cheerp-wasm segmented_sieve.cpp -o segmented_sieve_wasm.js -O3</code>

Note that we are using the **cheerp-wasm** target, not the **cheerp** target. This marks all code to be compiled into wasm (or asmjs) by default, including the C and C++ standard libraries.

Now we just have to run it:
`nodejs segmented_sieve_loader.js`
`nodejs segmented_sieve_wasm.js`

The main file is still a `.js` file, but a `.wasm` file is also produced, and it is loaded and run from the `.js` one.
The main file to be invoked is still a `.js` file, but a `.wasm` file is also produced, and it is loaded and run from the `.js` one.

Now the result, on my machine is something like this:
Now the result, on my machine, is something like this:
```
0s to sieve in the interval (1, 10) 4 primes found
0.001s to sieve in the interval (1, 100) 25 primes found
0.001s to sieve in the interval (1, 10) 4 primes found
0s to sieve in the interval (1, 100) 25 primes found
0s to sieve in the interval (1, 1000) 168 primes found
0s to sieve in the interval (1, 10000) 1229 primes found
0s to sieve in the interval (1, 100000) 9592 primes found
0.003s to sieve in the interval (1, 1e+06) 78498 primes found
0.031s to sieve in the interval (1, 1e+07) 664579 primes found
0.326s to sieve in the interval (1, 1e+08) 5761455 primes found
3.633s to sieve in the interval (1, 1e+09) 50847534 primes found
0.001s to sieve in the interval (1, 10000) 1229 primes found
0.002s to sieve in the interval (1, 100000) 9592 primes found
0.016s to sieve in the interval (1, 1e+06) 78498 primes found
0.094s to sieve in the interval (1, 1e+07) 664579 primes found
0.156s to sieve in the interval (1, 1e+08) 5761455 primes found
1.868s to sieve in the interval (1, 1e+09) 50847534 primes found
```

Things of note:
+ the nodejs clock lacks precision under the 1/1000th of a second
+ there is roughly a 2x speedup between the Javascript version and the Javascript + Wasm
+ Javascript + Wasm trails natively compiled C++ by 4.5x here. What that means? Shouldn't be surprising that C++ compiled in his native environment can be fast
+ there is roughly a 3x speedup between the pure JavaScript version and the JavaScript + Wasm
+ JavaScript + Wasm trails natively compiled C++ by 2x here.

Is 4.5x good, bad, or what?
Is 2x good, bad, or what?
Here is not very relevant, I would say, since the main point of this page is showing that it should be possible to compile (most) C++ code to be run sandboxed in the browser.

Http-server for executing on the browser
Expand Down
6 changes: 4 additions & 2 deletions pages/Using-WebWorkers-with-Cheerp.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ You can use Cheerp to generate JavaScript running in the Worker, JavaScript in t

using namespace client;

[[cheerp::genericjs]]
void webMain()
{
Worker* w = new Worker("cheerpWorker.js");
Expand All @@ -45,6 +46,7 @@ void webMain()

using namespace client;

[[cheerp::genericjs]]
void webMain()
{
addEventListener("message", cheerp::Callback([](MessageEvent* e) {
Expand All @@ -56,8 +58,8 @@ void webMain()

You can build each files using the following command line (for more information see [Getting Started](Getting-started))
```
/opt/cheerp/bin/clang++ -target cheerp cheerpWorkerHost.cpp -o cheerpWorkerHost.js
/opt/cheerp/bin/clang++ -target cheerp cheerpWorker.cpp -o cheerpWorker.js
/opt/cheerp/bin/clang++ -O3 cheerpWorkerHost.cpp -o cheerpWorkerHost.js
/opt/cheerp/bin/clang++ -O3 cheerpWorker.cpp -o cheerpWorker.js
```
For your convenience here is the needed HTML to execute the code

Expand Down

0 comments on commit 84cb908

Please sign in to comment.