Ceci est une ancienne révision du document !
Table des matières
Téléchargement et installation
git clone --depth 1 https://github.com/juj/emsdk.git cd emsdk ./emsdk update ./emsdk install latest ./emsdk activate latest source ./emsdk_env.sh
Utilisations
Emscripten va permettre de générer un fichier .js
qui va contenir le code passerelle entre le code JavaScript et le code WebAssembly qui sera dans le fichier .wasm
.
C'est donc un outil plutôt adapté pour créer des librairies, même s'il est possible de créer des programmes entiers avec.
Exemples
Librairies
Une simple librairie
- Code source
#include <emscripten.h> EMSCRIPTEN_KEEPALIVE int add(int a, int b) { return a + b; }
- Compilation
emcc add.c -o add.js -s EXPORT_NAME="'ModuleName'"
- Utilisation depuis du code html
Puis on peut utiliser la fonction add depuis le code HTML en passant par la librairie JavaScript générée. Il faudra bien mettre en ligne les deux fichiers .js
et .wasm
.
Par défaut, tous les objets exportés sont regroupés dans un objet global ModuleName
. La fonction est accessible depuis son nom “mangled” (func1@a@@AAEXH@Z
par exemple), avec l'ajout d'un préfix _
. Decorated Names Archive du 05/09/2018 le 04/01/2021
<head> <script type="text/javascript"> var ModuleName = { onRuntimeInitialized: function() { const a = 1.5; const b = 3.8; const ret = ModuleName._add(1.5, 3.8); console.log(`${a} + ${b} = ${ret}`); } }; </script> <script type="text/javascript" src="add.js"></script> </head> <body> </body>
How To Write A WebAssembly App in C/C++ Archive du 12/11/2020 le 04/01/2021
Module
Certaines librairies imposent l'utilisation des modules (jest).
Il faut utiliser l'option -s MODULARIZE=1
.
- Utilisation en HTML
Il faut bien mettre type="test/javascript"
et pas type="module"
.
<script type="text/javascript" src="add.js"></script> <script type="text/javascript"> ModuleName().then(async instance => { const a = 1.5; const b = 3.8; const ret = instance.__Z3addii(1.5, 3.8); console.log(`${a} + ${b} = ${ret}`); }); </script>
- Utilisation avec Jest
const Module = require('./add.js'); test('test name', async () => { return Module().then(async instance => { instance.__Z3addii(...); }); });
Bindings
C'est quand même plus pratique d'utiliser du style orienté objet plutôt que des symboles “mangled”.
Il faut donc déclarer manuellement :
- toutes les classes que l'on souhaite utiliser,
- toutes les fonctions, y compris constructeur, que l'on souhaite utiliser pour chaque classe,
- toutes les classes utilisées dans les arguments et les valeurs retours des fonctions qui seront utilisées,
- les relations d'héritage si le type parent est utilisé pour exécuter les fonctions de l'enfant (polymorphisme).
Dans mes tests, j'ai vu que le lieur d'emscripten semble avoir du mal avec les symboles weak multiples. Il est préférable de tout mettre dans un même fichier source.
EMSCRIPTEN_BINDINGS(jessica) { class_<Jessica::Data::Geotechnical::IFoundationStrip>("IFoundationStrip") .smart_ptr< std::shared_ptr<Jessica::Data::Geotechnical::IFoundationStrip>>( "IFoundationStrip") .function( "setB", select_overload<std::shared_ptr< Jessica::Data::Geotechnical::IFoundationStrip>(double) const>( &Jessica::Data::Geotechnical::IFoundationStrip::B)) .function("getB", select_overload<double() const>( &Jessica::Data::Geotechnical::IFoundationStrip::B)); class_<Jessica::Data::Geotechnical::FoundationStrip< Jessica::Util::Decorator::LogCall< Jessica::Util::Decorator::LogDuration< Jessica::Data::Geotechnical::FoundationStripImpl>>>, base<Jessica::Data::Geotechnical::IFoundationStrip>>( "FoundationStripDeco") .constructor<>() .function( "clone", &Jessica::Data::Geotechnical::FoundationStrip< Jessica::Util::Decorator::LogCall< Jessica::Util::Decorator::LogDuration< Jessica::Data::Geotechnical::FoundationStripImpl>>>:: Clone) .function( "setB", select_overload<std::shared_ptr< Jessica::Data::Geotechnical::IFoundationStrip>(double) const>( &Jessica::Data::Geotechnical::FoundationStrip< Jessica::Util::Decorator::LogCall< Jessica::Util::Decorator::LogDuration< Jessica::Data::Geotechnical::FoundationStripImpl>>>:: B)) .function( "getB", select_overload<double() const>( &Jessica::Data::Geotechnical::FoundationStrip< Jessica::Util::Decorator::LogCall< Jessica::Util::Decorator::LogDuration< Jessica::Data::Geotechnical::FoundationStripImpl>>>:: B)); }
Erreurs
UnboundTypeError: Cannot call FoundationStripRaw.setB due to unbound types: N7Jessica4Data12Geotechnical19FoundationStripImpl4SetBE
Ici, la fonction FoundationStripRaw.setB
a besoin du type N7Jessica4Data12Geotechnical19FoundationStripImpl4SetBE
(classe Jessica::Data::Geotechnical::FoundationStripImpl::SetB
).
Il suffit de rajouter dans EMSCRIPTEN_BINDINGS
le class_<Jessica::Data::Load::VerticalEccentricImpl::SetE>("VerticalEccentricImpl_SetE")
adapté si besoin.