URL: https://linuxfr.org/news/quelques-cadriciels-web-c-2-2 Title: Quelques cadriciels Web C++ (2/2) Authors: nokomprendo Davy Defaud, BenoĂźt Sibaud, ZeroHeure, claudex, palm123 et bubarđŸŠ„ Date: 2018ćčŽ12月06æ—„T02:46:39+01:00 License: CC By-SA Tags: c++ et nix Score: 38 Actuellement, il existe de nombreux langages et cadriciels intĂ©ressants pour le dĂ©veloppement Web _back‐end_. Dans ce domaine, le C++ n’est pas le langage le plus Ă  la mode, mais il possĂšde cependant des atouts intĂ©ressants. En effet, le C++ possĂšde de nombreuses bibliothĂšques (dont des cadriciels Web), il est rĂ©putĂ© pour ses performances et, enfin, ses derniĂšres normes le rendent plus agrĂ©able Ă  utiliser. L’objectif de cet article est de donner un aperçu des outils C++ disponibles pour le dĂ©veloppement Web _back‐end_, Ă  partir d’un exemple d’application. Les codes sources prĂ©sentĂ©s ici sont disponibles sur ce [dĂ©pĂŽt Git](https://framagit.org/nokomprendo/tuto_fonctionnel/tree/master/posts/tuto_fonctionnel_31). Les diffĂ©rents cadriciels utilisĂ©s sont rĂ©sumĂ©s en annexe (partie 2). Enfin, une liste de bibliothĂšques C++ est disponible sur [Awesome C++](http://fffaraz.github.io/awesome-cpp/). Partie 2 : les cadriciels Web. ---- [Quelques cadriciels Web C++ (1/2)](https://linuxfr.org/news/quelques-frameworks-web-cpp-1-2) ---- # Les cadriciels Web ## Les micro‐cadriciels, Ă  la Sinatra/Flask Les micro‐cadriciels Web, comme Sinatra en Ruby ou Flask en Python, ont pour objectif d’ĂȘtre simples et lĂ©gers. Ils proposent principalement des fonctionnalitĂ©s pour traiter des requĂȘtes HTTP ainsi qu’un mĂ©canisme de routage d’URL. Si nĂ©cessaire, ils peuvent ĂȘtre complĂ©tĂ©s par d’autres bibliothĂšques (gĂ©nĂ©ration de HTML, accĂšs Ă  une base de donnĂ©es en SQL...). Il existe plusieurs micro‐cadriciels C++, par exemple [crow](https://github.com/ipkn/crow) (voir [animals-crow](https://framagit.org/nokomprendo/tuto_fonctionnel/blob/master/posts/tuto_fonctionnel_31/animals-crow/src/main.cpp)) ou [silicon](http://siliconframework.org/) (voir [animals-silicon](https://framagit.org/nokomprendo/tuto_fonctionnel/blob/master/posts/tuto_fonctionnel_31/animals-silicon/src/main.cpp)) : ```cpp #include #include #include #include #include "symbols.hh" using namespace sl; using namespace std; ... int main() { // create app auto api = http_api( // serve the about page GET / _about = [] () { return renderAbout(); }, // serve the home page (and filter the animals using the "myquery" parameter) GET / _animals * get_parameters(_myquery = optional(string())) = [] (const auto & p, sqlite_connection & c) { vector animals = getAnimals(p.myquery, c); return renderHome(p.myquery, animals); }, // serve static files (located in the "mystatic" directory) GET / _mystatic = file_server("./mystatic") ); // create a connection factory to the database auto factory = middleware_factories( sqlite_connection_factory("animals.db") ); // run a server on port 3000 mhd_json_serve(api, factory, 3000); } ``` Ici, les fonctionnalitĂ©s du C++ moderne rendent le code concis et plutĂŽt agrĂ©able Ă  lire (par exemple, la lambda pour la route `_animals`). Dans une phase de prĂ©traitement, Silicon gĂ©nĂšre le fichier `symbols.hh`, qui dĂ©clare les symboles dĂ©finis par le programmeur, notamment les routes (`_about`, `_home`, `_mystatic`...). Ceci permet de vĂ©rifier statiquement que les routes sont utilisĂ©es correctement dans le code. D’autres langages utilisent l’introspection pour effectuer ce genre de vĂ©rification, mais C++ ne possĂšde pas cette fonctionnalitĂ©. ## Les cadriciels asynchrones, Ă  la Node.js Les cadriciels asynchrones, comme Node.js/Express en JavaScript, proposent les mĂȘmes fonctionnalitĂ©s que les micro‐cadriciels classiques mais via des fonctions non bloquantes. Ainsi, si une requĂȘte a besoin d’une ressource, l’application peut passer Ă  une autre requĂȘte en attendant que la ressource soit disponible. Ceci permet d’amĂ©liorer les performances gĂ©nĂ©rales de l’application mais nĂ©cessite un style de programmation particulier, Ă  base de *promesses* connectĂ©es Ă  des fonctions de *call‐back* par des *then* pour former une chaĂźne de traitements asynchrones. Il existe diffĂ©rents cadriciels asynchrones en C++, par exemple [cpprestsdk](https://github.com/Microsoft/cpprestsdk) (voir [animals-cpprestsdk](https://framagit.org/nokomprendo/tuto_fonctionnel/blob/master/posts/tuto_fonctionnel_31/animals-cpprestsdk/src/main.cpp)) et [pistache](http://pistache.io/) (voir [animals-pistache](https://framagit.org/nokomprendo/tuto_fonctionnel/tree/master/posts/tuto_fonctionnel_31/animals-pistache/src/main.cpp)) : ```cpp #include "Animal.hpp" #include "View.hpp" #include #include #include using namespace Pistache; using namespace std; // define server app class App : public Http::Endpoint { private: Rest::Router router; public: App(Address addr) : Http::Endpoint(addr) { auto opts = Http::Endpoint::options() .flags(Tcp::Options::InstallSignalHandler) .flags(Tcp::Options::ReuseAddr); init(opts); // create a route for the about page Rest::Routes::Get(router, "/about", [=](const Rest::Request &, Http::ResponseWriter response) { response.send(Http::Code::Ok, renderAbout()); return Rest::Route::Result::Ok; }); // create a route for the home page Rest::Routes::Get(router, "/", [=](const Rest::Request & request, Http::ResponseWriter response) { auto myquery = request.query().get("myquery").getOrElse(""); const vector animals = getAnimals(myquery); response.send(Http::Code::Ok, renderHome(move(myquery), move(animals))); return Rest::Route::Result::Ok; }); // create a route for serving static files Rest::Routes::Get(router, "/static/:filename", [=](const Rest::Request & request, Http::ResponseWriter response) { auto filename = request.param(":filename").as(); // the Pistache API is non-blocking; for example, serveFile returns // a Promise, for attaching a callback function Http::serveFile(response, "static/" + filename) .then( [=](ssize_t s){ cout << filename << " (" << s << " bytes)" << endl; }, Async::NoExcept); return Rest::Route::Result::Ok; }); setHandler(router.handler()); } }; // run server app on port 3000 int main() { App app({Ipv4::any(), 3000}); app.serve(); } ``` On retrouve ici une gestion classique des routes (avec le nom de la route et sa fonction de traitement). Cependant, on a dĂ©sormais un fonctionnement asynchrone, via des fonctions non bloquantes. Par exemple, pour la route « _static_ », la fonction `serveFile` retourne une promesse que l’on connecte Ă  une fonction _call‐back_, qui affiche un message de journal une fois la promesse rĂ©solue. ## Les cadriciels MVC, Ă  la RoR/Django Les cadriciels Web MVC, comme Ruby on Rails ou Python Django, sont des outils classiques dont l’objectif est d’implĂ©menter tout type d’application Web. Ils fournissent gĂ©nĂ©ralement toutes les fonctionnalitĂ©s nĂ©cessaires : routage d’URL, systĂšme de patrons, accĂšs Ă  des bases de donnĂ©es, systĂšme d’authentification... Les cadriciels MVC ne semblent pas ĂȘtre le domaine de prĂ©dilection du C++, mais on trouve tout de mĂȘme quelques outils intĂ©ressants, notamment [cppcms](http://cppcms.com/wikipp/en/page/main/). En plus des fonctionnalitĂ©s classiques d’un cadriciel MVC, cppcms propose un systĂšme de patrons assez Ă©voluĂ©, avec hĂ©ritage de vues et gestion de contenu. Par exemple, on peut dĂ©finir une vue principale `MasterView` et en dĂ©river des vues `AboutView` et `HomeView` qui hĂ©ritent des caractĂ©ristiques de `MasterView` et les complĂštent. Enfin, on peut associer un contenu Ă  ces vues (paramĂštres des patrons), Ă©galement avec un systĂšme d’hĂ©ritage. En reprenant l’exemple prĂ©cĂ©dent, on peut dĂ©finir un contenu `MasterContent` pour la vue `MasterView`, la dĂ©river en `HomeContent` pour la vue `HomeView` et utiliser directement `MasterContent` pour la vue `AboutView` (pas de nouveau paramĂštre dans le patron). Au niveau du code, le fichier [`animals-cppcms/src/content.h`](https://framagit.org/nokomprendo/tuto_fonctionnel/tree/master/posts/tuto_fonctionnel_31/animals-cppcms/src/content.h) dĂ©finit les contenus : ```cpp // define how to exchange data between the C++ code and the templates namespace content { // content for MasterView and AboutView struct MasterContent : cppcms::base_content { std::string title; // the "title" parameter in master.tmpl }; // datatype for the form in HomeView struct InfoForm : cppcms::form { cppcms::widgets::text myquery; InfoForm() { add(myquery); } }; // content for HomeView // inherits from MasterContent, because HomeView inherits from MasterView struct HomeContent : MasterContent { std::vector animals; // the "animals" parameter in home.tmpl InfoForm info; // the "info" parameter in home.tmpl }; } ``` Le fichier [`animals-cppcms/src/master.tmpl`](https://framagit.org/nokomprendo/tuto_fonctionnel/tree/master/posts/tuto_fonctionnel_31/animals-cppcms/src/master.tmpl) dĂ©finit la vue `MasterView` : ```html <% c++ #include "content.h" %> <% skin myskin %> <% view MasterView uses content::MasterContent %> <% template title() %> <%= title %> <% end %> <% template page_content() %> to be overriden in sub-templates <% end %> <% template render() %>

<% include title() %>

<% include page_content() %>

AltStyle ă«ă‚ˆăŁăŠć€‰æ›ă•ă‚ŒăŸăƒšăƒŒă‚ž (->ă‚ȘăƒȘă‚žăƒŠăƒ«) /

<% end template %> <% end view %> <% end skin %> ``` Le fichier [`animals-cppcms/src/about.tmpl`](https://framagit.org/nokomprendo/tuto_fonctionnel/tree/master/posts/tuto_fonctionnel_31/animals-cppcms/src/about.tmpl) définit la vue `AboutView` : ```html <% skin myskin %> <% view AboutView uses content::MasterContent extends MasterView %> <% template page_content() %>

Generated by Cppcms

Home

<% end template %> <% end view %> <% end skin %> ``` Le fichier [`animals-cppcms/src/about.tmpl`](https://framagit.org/nokomprendo/tuto_fonctionnel/tree/master/posts/tuto_fonctionnel_31/animals-cppcms/src/about.tmpl) définit la vue `HomeView` : ```html <% skin myskin %> <% view HomeView uses content::HomeContent extends MasterView %> <% template page_content() %>
<% form as_p info %>
<% foreach animal in animals %> <% item %>

<%= animal.name %>

" />
<% end %> <% end foreach %>

About

<% end template %> <% end view %> <% end skin %> ``` Enfin, le programme principal dĂ©finit le routage d’URL et initialise les contenus avant de lancer le rendu des vues. Fichier [`animals-cppcms/src/main.cpp`](https://framagit.org/nokomprendo/tuto_fonctionnel/tree/master/posts/tuto_fonctionnel_31/animals-cppcms/src/main.cpp) : ```cpp // main application class App : public cppcms::application { public: App(cppcms::service &srv) : cppcms::application(srv) { // about page dispatcher().assign("/about", &App::about, this); mapper().assign("about","/about"); // home page dispatcher().assign("", &App::home, this); mapper().assign(""); // images dispatcher().assign("/img/([a-z_0-9_\-]+\.jpg)", &App::serveJpg, this, 1); // root url mapper().root("/animals"); } private: void about() { // AboutView inherits from MasterView and uses the same content type (MasterContent) content::MasterContent c; c.title = "About (Cppcms)"; // render the AboutView template render("AboutView", c); } void home() { // HomeView inherits from MasterView and uses its own content type // (HomeContent, that inherits from MasterContent) content::HomeContent c; // data defined in MasterContent c.title = "Animals (Cppcms)"; // data defined in HomeContent c.info.load(context()); c.animals = getAnimals(c.info.myquery.value()); // render the HomeView template render("HomeView", c); } void serveJpg(string filename) { // open and send the image file ifstream ifs("img/" + filename); if (ifs) { response().content_type("image/jpeg"); response().out() << ifs.rdbuf(); } else { response().status(404); } } }; // create and run the application int main(int argc, char ** argv) { try { cppcms::service srv(argc, argv); srv.applications_pool().mount(cppcms::applications_factory()); srv.run(); } catch(exception const & e) { cerr << e.what() << endl; } return 0; } ``` Les cadriciels MVC sont des outils efficaces pour implĂ©menter des applications complexes. Cependant, ils nĂ©cessitent un apprentissage assez consĂ©quent et peuvent ĂȘtre surdimensionnĂ©s pour des petites applications simples. ## Les cadriciels basĂ©s patrons, Ă  la PHP Le cadriciel [tntnet](http://www.tntnet.org/tntnet.html) propose un systĂšme basĂ© sur les patrons, Ă  la maniĂšre de PHP. Si ce cadriciel est assez anecdotique dans l’écosystĂšme C++, il semble cependant plutĂŽt efficace dans son approche : Ă©crire du code HTML classique et y ajouter des sections de code C++ lĂ  oĂč c’est nĂ©cessaire. Par exemple, le fichier [`animals-tntent/src/myimg.ecpp`](https://framagit.org/nokomprendo/tuto_fonctionnel/tree/master/posts/tuto_fonctionnel_31/animals-tntnet/src/myimg.ecpp) dĂ©finit une application qui affiche une image dont le nom est passĂ© en paramĂštre : ```html <%args> filename; " /> ``` De mĂȘme, le fichier [`animals-tntent/src/home.ecpp`](https://framagit.org/nokomprendo/tuto_fonctionnel/tree/master/posts/tuto_fonctionnel_31/animals-tntnet/src/home.ecpp) dĂ©finit une application plus complexe (appel de fonction C++, gĂ©nĂ©ration de code HTML via une boucle en C++, etc.) : ```html <%args> myquery; <%pre> #include "Animal.hpp"

Animals (Tntnet)

<%cpp> for (const Animal & animal : getAnimals(myquery)) {

<$animal.name$>

" />
<%cpp> }

About

``` Enfin, tntnet propose diffĂ©rents types de dĂ©ploiement : programme CGI, serveur autonome, serveur d’applications tntnet compilĂ©es dans des bibliothĂšques dynamiques. Par exemple, pour implĂ©menter un serveur autonome ([`animals-tntent/src/main.cpp`](https://framagit.org/nokomprendo/tuto_fonctionnel/tree/master/posts/tuto_fonctionnel_31/animals-tntnet/src/main.cpp)) : ```cpp #include // run server on port 3000 int main() { try { tnt::Tntnet app; app.listen(3000); app.mapUrl("^/$", "home"); // route the "/" url to the "home" application app.mapUrl("^/about$", "about"); // route the "/about" url to the "about" application app.mapUrl("^/myimg$", "myimg"); // ... app.mapUrl("^/(static/.*)", "1ăƒ‰ăƒ«", "static@tntnet"); app.run(); return 0; } catch (const std::exception & e) { std::cerr << e.what() << std::endl; return -1; } } ``` À noter que ce type de cadriciel est peut‐ĂȘtre moins adaptĂ© au dĂ©veloppement d’applications complexes (lisibilitĂ© des patrons, rĂ©utilisation...). ## Les cadriciels basĂ©s widgets Ces outils s’inspirent des cadriciels d’interfaces graphiques de bureau, comme [Qt](https://www.qt.io/) ou [gtkmm](https://www.gtkmm.org/), c’est‐à‐dire basĂ©s sur une hiĂ©rarchie de _widgets_ composant l’interface et intĂ©ragissant via un mĂ©canisme de _signal‐slot_. Les cadriciels Web basĂ©s _widgets_ sont Ă©tonnament peu rĂ©pandus, mĂȘme tous langages confondus, alors que leur potentiel semble important. En effet, ils permettent de dĂ©velopper une application _fullstack_ client‐serveur en utilisant une bibliothĂšque d’interface graphique classique et sans avoir Ă  trop se prĂ©occuper de l’architecture rĂ©seau de l’application. En C++, le cadriciel le plus abouti dans cette catĂ©gorie est certainement [Wt](https://www.webtoolkit.eu/wt/). Wt possĂšde de nombreux _widgets_ classiques ou Ă©voluĂ©s, un ORM SQL, un systĂšme d’authentification, la possibilitĂ© de manipuler du HTML et du CSS, etc. En Wt, le programme principal se rĂ©sume Ă  router des URL vers les applications correspondantes (animals-wt/src/main.cpp) : ```cpp ... int main(int argc, char ** argv) { try { WServer server(argc, argv, WTHTTP_CONFIGURATION); // route the url "/about" to an application "AboutApp" server.addEntryPoint(EntryPointType::Application, [](const WEnvironment & env) { return make_unique(env); }, "/about"); // route the url "/" to an application "HomeApp" server.addEntryPoint(EntryPointType::Application, [=](const WEnvironment & env) { return make_unique(env); }, "/"); server.run(); } catch (Dbo::Exception & e) { cerr << "Dbo::Exception: " << e.what() << endl; } return 0; } ``` Ces applications Wt correspondent Ă  des interfaces graphiques classiques, mais avec une architecture client‐serveur. Par exemple, pour dĂ©finir l’application « about » (page statique) via le systĂšme de patrons HTML/CSS, il suffit de dĂ©finir la classe suivante ([`animals-wt/src/AboutApp.hpp`](https://framagit.org/nokomprendo/tuto_fonctionnel/tree/master/posts/tuto_fonctionnel_31/animals-wt/src/AboutApp.hpp)) : ```cpp ... // Application class implementing the about page class AboutApp : public Wt::WApplication { private: // main HTML template of the application const std::string _app_template = R"(

About (Wt)

Generated by Wt

Home

)"; public: // create the application AboutApp(const Wt::WEnvironment & env) : Wt::WApplication(env) { // load css useStyleSheet({"style.css"}); // create the main widget using the HTML template root()->addWidget(std::make_unique(_app_template)); } }; ``` Pour une application plus complexe, par exemple la page affichant les animaux, on peut définir un nouveau _widget_ `AnimalWidget` qui implémente une vignette, puis utiliser cette classe pour afficher tous les animaux lus dans la base de données (voir [`animals-wt/src/HomeApp.hpp`](https://framagit.org/nokomprendo/tuto_fonctionnel/tree/master/posts/tuto_fonctionnel_31/animals-wt/src/HomeApp.hpp)) : ```cpp ... bool isPrefixOf(const std::string & txt, const std::string & fullTxt) { return std::inner_product(std::begin(txt), std::end(txt), std::begin(fullTxt), true, std::logical_and(), std::equal_to()); } // widget showing an animal (name + image + anchor) class AnimalWidget : public Wt::WAnchor { private: // pointer to the WText that contains the animal name Wt::WText * _animalName; public: AnimalWidget(const Animal & animal) { // set anchor href const std::string imagePath = "img/" + animal.image; setLink(Wt::WLink(imagePath)); // create a container widget, inside the anchor widget auto cAnimal = addWidget(std::make_unique()); cAnimal->setStyleClass("divCss"); // create a text widget, inside the container auto cText = cAnimal->addWidget(std::make_unique()); cText->setPadding(Wt::WLength("1em")); _animalName = cText->addWidget(std::make_unique(animal.name)); // create an image widget, inside the container auto img = cAnimal->addWidget(std::make_unique(imagePath)); img->setStyleClass("imgCss"); } void filter(const std::string & txt) { // show the widget if txt is null or if it is a prefix of the animal name setHidden(txt != "" and not isPrefixOf(txt, _animalName->text().toUTF8())); } }; // Application class implementing the home page class HomeApp : public Wt::WApplication { private: // the line edit widget (for querying animal to show/hide) Wt::WLineEdit * _myquery; // the animal widgets std::vector _animalWidgets; // main HTML template of the application const std::string _app_template = R"(

Animals (Wt)

${myquery}

${animals}

About

)"; // show all animals that match the _myquery prefix void filterAnimals() { for(auto aw : _animalWidgets) aw->filter(_myquery->text().toUTF8()); } public: // create the application HomeApp(const Wt::WEnvironment & env) : WApplication(env) { // load css useStyleSheet({"style.css"}); // create the main widget using the HTML template auto r = root()->addWidget(std::make_unique(_app_template)); // create the remaining widgets and bind them to the template placeholders _myquery = r->bindWidget("myquery", std::make_unique()); // connect the widget _myquery to the function filterAnimals _myquery->textInput().connect(this, &HomeApp::filterAnimals); // create a container widget for the animals auto w = r->bindWidget("animals", std::make_unique()); // open the database Wt::Dbo::Session session; session.setConnection(std::make_unique("animals.db")); session.mapClass("animals"); // query the database Wt::Dbo::Transaction transaction(session); Wt::Dbo::collection> dboAnimals = session.find(); for (const Wt::Dbo::ptr & dboAnimal : dboAnimals) { // add a widget auto aw = w->addWidget(std::make_unique(*dboAnimal)); // store a pointer, for future updates _animalWidgets.push_back(aw); } } }; ``` À premiĂšre vue, cette implĂ©mentation peut sembler plus longue et plus compliquĂ©e que les implĂ©mentations prĂ©cĂ©dentes. Cependant, son code devrait sembler familier Ă  n’importe quel dĂ©veloppeur d’interface graphique de bureau. De plus, cette implĂ©mentation gĂšre l’ensemble de l’application (_fullstack_), et non la partie serveur uniquement. Par exemple, la connexion du signal `_myquery->textInput()` Ă  la fonction `HomeApp::filterAnimals` implique des mises Ă  jour en temps rĂ©el cĂŽtĂ© client, ce qui serait nettement plus difficile Ă  implĂ©menter avec les cadriciels prĂ©cĂ©dents. # IntĂ©gration avec Nix Avant de conclure, voici une petite remarque concernant l’intĂ©gration, dans un projet de code, des cadriciels prĂ©sentĂ©s. En effet, ces cadriciels sont rarement prĂ©sents dans les logithĂšques des systĂšmes d’exploitation et doivent donc gĂ©nĂ©ralement ĂȘtre installĂ©s manuellement. Pour cela, on choisit classiquement l’une des deux solutions suivantes : - tĂ©lĂ©charger le code source de la bibliothĂšque, l’installer dans un dossier local ou systĂšme et rĂ©gler les variables d’environnement de façon Ă  trouver la bibliothĂšque ; - intĂ©grer la bibliothĂšque directement au projet, par exemple via les sous‐modules Git, et configurer le projet de façon Ă  trouver la bibliothĂšque. Aucune de ces solutions n’est vraiment satisfaisante : la premiĂšre solution introduit une dĂ©pendance externe peu portable qu’il faut mettre Ă  jour manuellement ; la seconde solution introduit des duplications de code potentielles et nĂ©cessite une configuration de projet particuliĂšre. Une troisiĂšme solution, particuliĂšrement avantageuse, consiste Ă  utiliser le systĂšme de paquets [Nix](https://nixos.org/nix/). Par exemple, pour rĂ©cupĂ©rer et empaqueter cpprestsdk depuis son projet GitHub, il suffit d’écrire le fichier Nix suivant (voir [`animals-cpprestsdk/nix/cpprestsdk.nix`](https://framagit.org/nokomprendo/tuto_fonctionnel/blob/master/posts/tuto_fonctionnel_31/animals-cpprestsdk/nix/cpprestsdk.nix)) : ```nix { stdenv, fetchFromGitHub, cmake, boost, openssl, websocketpp, zlib }: stdenv.mkDerivation { name = "cpprestsdk"; src = fetchFromGitHub { owner = "Microsoft"; repo = "cpprestsdk"; rev = "204a52610234ac5180e80a6883b62c0ad085f51e"; sha256 = "0mj2m6n889zdhwxdx24ljxfqryivvn3w9vzs94ppzcx5apa5jb3w"; }; enableParallelBuilding = true; buildInputs = [ boost cmake openssl websocketpp zlib ]; } ``` Pour inclure, dans un projet, des cadriciels ainsi empaquetĂ©s, il suffit alors de les appeler dans le fichier de configuration `default.nix` du projet (voir [`animals-cpprestsdk/default.nix`](https://framagit.org/nokomprendo/tuto_fonctionnel/blob/master/posts/tuto_fonctionnel_31/animals-cpprestsdk/default.nix)) : ```nix with import {}; let _cpprestsdk = callPackage ./nix/cpprestsdk.nix {}; _ctml = callPackage ./nix/ctml.nix {}; _sqlite_orm = callPackage ./nix/sqlite_orm.nix {}; in stdenv.mkDerivation { name = "animals-cpprestsdk"; src = ./.; buildInputs = [ boost _cpprestsdk _ctml openssl sqlite _sqlite_orm ]; buildPhase = '' g++ -O2 -o animals-cpprestsdk src/*.cpp -lcpprest -lboost_system -lssl -lsqlite3 sqlite3 animals.db < animals.sql ''; installPhase = '' mkdir -p $out/static cp animals-cpprestsdk $out/ cp animals.db $out/ cp $src/static/* $out/static/ ''; } ``` On peut alors facilement compiler et lancer le programme, avec les commandes suivantes : ```sh nix-build cd result ./animals-cpprestsdk ``` Cette mĂ©thode Ă  l’avantage de rendre les dĂ©pendances explicites et isolĂ©es, mais sans duplication. Elle facilite Ă©galement la rĂ©utilisation et les mises Ă  jour. # Conclusion Pour dĂ©velopper des applications Web _back‐end_, le C++ est une option tout Ă  fait envisageable. Avec ses derniĂšres Ă©volutions, le langage est gĂ©nĂ©ralement plus simple et plus sĂ»r Ă  utiliser, sans compromis sur les performances. De nombreuses bibliothĂšques C++ sont disponibles pour le dĂ©veloppement Web : patrons, gĂ©nĂ©ration de HTML, connexion SQL, ORM... Les cadriciels Web sont Ă©galement nombreux et variĂ©s : cadriciel MVC Ă  la RoR/Django, micro‐cadriciel Ă  la Sinatra/Flask, cadriciel asynchrone Ă  la Node.js, cadriciel basĂ© patrons Ă  la PHP, et mĂȘme cadriciel _fullstack_ basĂ© _widgets_. Enfin, on notera qu’avec Nix, il est trĂšs facile de configurer un projet intĂ©grant ce genre de bibliothĂšques. Bien Ă©videmment, tout ceci intĂ©ressera essentiellement des dĂ©veloppeurs qui connaissent dĂ©jĂ  C++, car beaucoup d’autres langages ont Ă©galement des outils trĂšs intĂ©ressants pour le dĂ©veloppement Web. # Annexe : rĂ©sumĂ© des projets et des cadriciels prĂ©sentĂ©s À noter qu’il s’agit uniquement d’exemples d’associations. Il est gĂ©nĂ©ralement possible d’utiliser n’importe quel cadriciel Web avec n’importe quel gĂ©nĂ©rateur HTML et n’importe quelle interface SQL. | projet | cadriciel Web | gĂ©nĂ©rateur HTML | interface SQL | |---|---|---|---| | [animals-cppcms](https://framagit.org/nokomprendo/tuto_fonctionnel/tree/master/posts/tuto_fonctionnel_31/animals-cppcms) | [cppcms](http://cppcms.com/wikipp/en/page/main/) (cadriciel MVC) | cppcms (systĂšme de patrons) | cppcms (connecteur SQL) | | [animals-cpprestsdk](https://framagit.org/nokomprendo/tuto_fonctionnel/tree/master/posts/tuto_fonctionnel_31/animals-cpprestsdk) | [cpprestsdk](https://github.com/Microsoft/cpprestsdk) (cadriciel rĂ©seau asynchrone) | [ctml](https://github.com/tinfoilboy/CTML) (gĂ©nĂ©rateur de documents) | [sqlite_orm](https://github.com/fnc12/sqlite_orm) (ORM) | | [animals-crow](https://framagit.org/nokomprendo/tuto_fonctionnel/tree/master/posts/tuto_fonctionnel_31/animals-crow) | http: [crow](https://github.com/ipkn/crow) (micro‐cadriciel) | crow (systĂšme de patrons) | [sqlpp11](https://github.com/rbock/sqlpp11) (ORM) | | [animals-nodejs](https://framagit.org/nokomprendo/tuto_fonctionnel/tree/master/posts/tuto_fonctionnel_31/animals-nodejs) (Javascript/Node.js) | [express](https://expressjs.com/) (micro‐cadriciel asynchrone) | [pug](https://pugjs.org) (gĂ©nĂ©rateur de documents) | [better-sqlite3](https://github.com/JoshuaWise/better-sqlite3) (connecteur SQL) | | [animals-pistache](https://framagit.org/nokomprendo/tuto_fonctionnel/tree/master/posts/tuto_fonctionnel_31/animals-pistache) | [pistache](http://pistache.io/) (micro‐cadriciel asynchrone) | [kainjow mustache](https://github.com/kainjow/Mustache) (systĂšme de patrons) | [sqlite_modern_cpp](https://github.com/SqliteModernCpp/sqlite_modern_cpp) (connecteur SQL) | | [animals-scotty](https://framagit.org/nokomprendo/tuto_fonctionnel/tree/master/posts/tuto_fonctionnel_31/animals-scotty) (Haskell) | [scotty](http://hackage.haskell.org/package/scotty) (micro‐cadriciel) | [lucid](https://hackage.haskell.org/package/lucid) and [clay](https://hackage.haskell.org/package/clay) (gĂ©nĂ©rateurs de documents) | [sqlite-simple](https://hackage.haskell.org/package/sqlite-simple) (connecteur SQL) | | [animals-silicon](https://framagit.org/nokomprendo/tuto_fonctionnel/tree/master/posts/tuto_fonctionnel_31/animals-silicon) | [silicon](http://siliconframework.org/) (micro‐cadriciel) | aucun | silicon (connecteur SQL) | | [animals-tntnet](https://framagit.org/nokomprendo/tuto_fonctionnel/tree/master/posts/tuto_fonctionnel_31/animals-tntnet) | [tntnet](http://www.tntnet.org/tntnet.html) (cadriciel basĂ© patrons) | tntnet (systĂšme de patrons) | tntnet (connecteur SQL) | | [animals-wt](https://framagit.org/nokomprendo/tuto_fonctionnel/tree/master/posts/tuto_fonctionnel_31/animals-wt) | [wt](https://www.webtoolkit.eu/wt) (cadriciel basĂ© _widgets_) | wt (systĂšme de _widgets_ + patrons) | wt (ORM) |