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. 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++.
Partie 2 : les cadriciels Web.
Sommaire
- Les cadriciels Web
- Intégration avec Nix
- Conclusion
- Annexe : résumé des projets et des cadriciels présentés
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 (voir animals-crow) ou silicon (voir animals-silicon) :
#include <silicon/api.hh>
#include <silicon/backends/mhd.hh>
#include <silicon/middlewares/sqlite_connection.hh>
#include <silicon/middleware_factories.hh>
#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<Animal> 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 (voir animals-cpprestsdk) et pistache (voir animals-pistache) :
#include "Animal.hpp"
#include "View.hpp"
#include <pistache/http.h>
#include <pistache/router.h>
#include <pistache/endpoint.h>
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<Animal> 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<string>();
// 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.
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
définit les contenus :
// 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<Animal> animals; // the "animals" parameter in home.tmpl
InfoForm info; // the "info" parameter in home.tmpl
};
}
Le fichier animals-cppcms/src/master.tmpl
définit la vue MasterView
:
<% c++ #include "content.h" %>
<% skin myskin %>
<% view MasterView uses content::MasterContent %>
<!-- "title" is set in the C++ code and used in the "render" template below -->
<% template title() %> <%= title %> <% end %>
<!-- "page_content" is set in the sub-template and used in the "render" template below -->
<% template page_content() %> to be overriden in sub-templates <% end %>
<!-- main template that uses the two previous ones and that is called from the C++ code -->
<% template render() %>
<html>
<head>
<style>
body {
background-color: azure;
}
...
</style>
</head>
<body>
<!-- adds the "title" template defined above -->
<h1><% include title() %></h1>
<!-- adds the "page_content" template defined above -->
<div> <% include page_content() %> </div>
</body>
</html>
<% end template %>
<% end view %>
<% end skin %>
Le fichier animals-cppcms/src/about.tmpl
définit la vue AboutView
:
<% skin myskin %>
<!-- inherits from MasterView -->
<% view AboutView uses content::MasterContent extends MasterView %>
<!-- defines "page_content", that is used in MasterView -->
<% template page_content() %>
<p>Generated by <a href='http://cppcms.com/wikipp/en/page/main'>Cppcms</a></p>
<p><a href='<% url "/" %>'>Home</a></p>
<% end template %>
<% end view %>
<% end skin %>
Le fichier animals-cppcms/src/about.tmpl
définit la vue HomeView
:
<% skin myskin %>
<!-- inherits from MasterView -->
<% view HomeView uses content::HomeContent extends MasterView %>
<!-- defines "page_content", that is used in MasterView -->
<% template page_content() %>
<!-- creates a form using the "info" attribute defined in HomeContent -->
<form method="get" action="" >
<% form as_p info %>
</form>
<!-- add the HTML elements corresponding to the "animals" attribute defined in HomeContent -->
<% foreach animal in animals %>
<% item %>
<a class="aCss" href="img/<%= animal.image %>" >
<div class="divCss">
<p><%= animal.name %></p>
<img class="imgCss" src="img/<%= animal.image %>" />
</div>
</a>
<% end %>
<% end foreach %>
<p style="clear:both"><a href='<% url "/about" %>'>About</a></p>
<% 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
:
// 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<App>());
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 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
définit une application qui affiche une image dont le nom est passé en paramètre :
<%args>
filename;
</%args>
<html>
<body>
<img src="static/img/<$filename$>" />
</body>
</html>
De même, le fichier animals-tntent/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.) :
<%args>
myquery;
</%args>
<%pre>
#include "Animal.hpp"
</%pre>
<html>
<head>
<link rel="stylesheet" type="text/css" href="static/style.css">
</head>
<body>
<h1>Animals (Tntnet)</h1>
<form>
<p> <input type="text" name="myquery" value="<$myquery$>"> </p>
</form>
<%cpp> for (const Animal & animal : getAnimals(myquery)) { </%cpp>
<a href="myimg?filename=<$animal.image$>">
<div class="divCss">
<p> <$animal.name$> </p>
<img class="imgCss" src="static/img/<$animal.image$>" />
</div>
</a>
<%cpp> } </%cpp>
<p style="clear: both"><a href="/about">About</a></p>
</body>
</html>
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
) :
#include <tnt/tntnet.h>
// 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 ou gtkmm, 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. 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) :
…
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<AboutApp>(env); },
"/about");
// route the url "/" to an application "HomeApp"
server.addEntryPoint(EntryPointType::Application,
[=](const WEnvironment & env)
{ return make_unique<HomeApp>(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
) :
...
// Application class implementing the about page
class AboutApp : public Wt::WApplication {
private:
// main HTML template of the application
const std::string _app_template = R"(
<h1>About (Wt)</h1>
<p>Generated by <a href="https://www.webtoolkit.eu/wt">Wt</a></p>
<p><a href="/">Home</a></p>
)";
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<Wt::WTemplate>(_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
) :
…
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<char>(), std::equal_to<char>());
}
// 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<Wt::WContainerWidget>());
cAnimal->setStyleClass("divCss");
// create a text widget, inside the container
auto cText = cAnimal->addWidget(std::make_unique<Wt::WContainerWidget>());
cText->setPadding(Wt::WLength("1em"));
_animalName = cText->addWidget(std::make_unique<Wt::WText>(animal.name));
// create an image widget, inside the container
auto img = cAnimal->addWidget(std::make_unique<Wt::WImage>(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<AnimalWidget*> _animalWidgets;
// main HTML template of the application
const std::string _app_template = R"(
<h1>Animals (Wt)</h1>
<p>${myquery}</p>
${animals}
<p style="clear: both"><a href="/about">About</a></p>
)";
// 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<Wt::WTemplate>(_app_template));
// create the remaining widgets and bind them to the template placeholders
_myquery = r->bindWidget("myquery", std::make_unique<Wt::WLineEdit>());
// 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<Wt::WContainerWidget>());
// open the database
Wt::Dbo::Session session;
session.setConnection(std::make_unique<Wt::Dbo::backend::Sqlite3>("animals.db"));
session.mapClass<AnimalDb>("animals");
// query the database
Wt::Dbo::Transaction transaction(session);
Wt::Dbo::collection<Wt::Dbo::ptr<AnimalDb>> dboAnimals = session.find<AnimalDb>();
for (const Wt::Dbo::ptr<AnimalDb> & dboAnimal : dboAnimals) {
// add a widget
auto aw = w->addWidget(std::make_unique<AnimalWidget>(*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. 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
) :
{ 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
) :
with import <nixpkgs> {};
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 :
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 | cppcms (cadriciel MVC) | cppcms (système de patrons) | cppcms (connecteur SQL) |
animals-cpprestsdk | cpprestsdk (cadriciel réseau asynchrone) | ctml (générateur de documents) | sqlite_orm (ORM) |
animals-crow | http: crow (micro‐cadriciel) | crow (système de patrons) | sqlpp11 (ORM) |
animals-nodejs (Javascript/Node.js) | express (micro‐cadriciel asynchrone) | pug (générateur de documents) | better-sqlite3 (connecteur SQL) |
animals-pistache | pistache (micro‐cadriciel asynchrone) | kainjow mustache (système de patrons) | sqlite_modern_cpp (connecteur SQL) |
animals-scotty (Haskell) | scotty (micro‐cadriciel) | lucid and clay (générateurs de documents) | sqlite-simple (connecteur SQL) |
animals-silicon | silicon (micro‐cadriciel) | aucun | silicon (connecteur SQL) |
animals-tntnet | tntnet (cadriciel basé patrons) | tntnet (système de patrons) | tntnet (connecteur SQL) |
animals-wt | wt (cadriciel basé widgets) | wt (système de widgets + patrons) | wt (ORM) |
Aller plus loin
- Quelques cadriciels Web C++ (1/2) (206 clics)
# Beast
Posté par David Demelier (site web personnel) . Évalué à 2.
J'ajouterai Boost.Beast qui est vraiment ce qu'on peut appeler un micro framework.
Je l'utilise et c'est assez sympa mais rudimentaire.
git is great because linus did it, mercurial is better because he didn't
[^] # Re: Beast
Posté par AnthonyRabine (site web personnel) . Évalué à 1.
Je trouve qu'il manque de simplicité. Il faudrait un wrapper, l'écriture est bien verbeuse.
[^] # Re: Beast
Posté par AnthonyRabine (site web personnel) . Évalué à 1.
Et j'ajouterais : il semble que beast soit en bonne voie pour devenir un standard (C++21?).
# Le titre
Posté par Ms-Mac . Évalué à 5. Dernière modification le 12 décembre 2018 à 19:20.
Cadriciels c'est bien aussi. Pour les moteurs de recherche 1/2, 2/2 c’est plus logique.
Tout le monde a un cerveau. Mais peu de gens le savent.
[^] # Re: Le titre
Posté par Davy Defaud . Évalué à 2.
Je suis ravi que ça te plaise. Je suis à l’initiative du remplacement de framework par cadriciel, l’auteur n’est donc pas fautif de cette incohérence entre les deux parties. Je viens de faire ma passe de correction sur la seconde, c’est à présent harmonisé.
# Sécurité
Posté par mpurple . Évalué à 1.
Je me pose une question sur la sécurité de tous ces frameworks.
Sur un système qui doit offrir des websocket, une API Rest et des fichiers statiques, comment protège-t-on tout ce petit monde ?
Il faut que chacun est un support de TLS (je pense que cela est fait), mais l'authentification ? Comment fait-on ? Faut-il implémenter un système d'authentification sur chacun d'eux ?
Cela oblige aussi à utiliser des ports différents pour chaque sous-système. Il faut donc vérifier l'ouverture des ports sur les différents proxy.
[^] # Re: Sécurité
Posté par barmic . Évalué à 3.
Généralement tu utilise un reverse proxy qui va au moins permettre de n'avoir que le port 443 d'ouvert.
Ensuite tu peut faire du gateway offloading et laisser à ce reverse proxy la gestion de la TLS. Ça permet de ne pas avoir à se poser la question sur comment configurer chaque service (mise en place du certificat, mais aussi choix des ciphers par exemple).
Enfin pour l'authentification ça dépend de ton authentification et selon le choix fait tu va l'implémenter sur chaque service ou sur le reverse proxy. Quand tu le fais sur le reverse proxy il va ajouter des entêtes à la requête pour permettre au service suivant de gérer les droit (le reverse proxy s'assure que la requête est bien celle de tel utilisateur, chaque service vérifient que l'utilisateur en question a le droit de faire cette requête).
# Cutelyst
Posté par Alex . Évalué à 2.
Cutelyst (que je n'ai toujours pas testé) qui à le bon gout de permettre de partager ton code Qt (donc qui doit en théorie permettre simultanément d'écrire une appli web et une appli desktop)
[^] # Re: Cutelyst
Posté par AnthonyRabine (site web personnel) . Évalué à 1.
Pas compris ta phrase.
# isPrefix
Posté par akimd . Évalué à 1.
Implantation astucieuse de isPrefix, mais le logical_and est plutôt sur bool que sur char, et cette implémentation ne s'arrête pas dès que possible. Je serai plutôt parti sur std::mismatch.
[^] # Re: isPrefix
Posté par nokomprendo (site web personnel) . Évalué à 1. Dernière modification le 02 janvier 2019 à 15:54.
Bien vu, merci.
Apparemment il y a
starts_with
en C++20 qui devrait encore mieux convenir.# Commentaire supprimé
Posté par rania_d . Évalué à 0. Dernière modification le 29 janvier 2019 à 20:39.
Ce commentaire a été supprimé par l’équipe de modération.
[^] # Commentaire supprimé
Posté par rania_d . Évalué à 0. Dernière modification le 29 janvier 2019 à 20:39.
Ce commentaire a été supprimé par l’équipe de modération.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.