Interessante! Ma Qt può anche essere utilizzato per la realizzazione di siti web? Sostituisce PHP e JAVA?
Interessante! Ma Qt può anche essere utilizzato per la realizzazione di siti web? Sostituisce PHP e JAVA?
Più pratica in futuro...
Per C++ esistono alcuni framework per il web (Cutelyst, basato su Qt, CppCMS, basato su libreria standard e poco altro), ma sono pochi che li usano per un motivo molto semplice: in programmazione web nella maggior parte dei casi non interessano così tanto le performance "pure" del codice applicazione (in genere il bottleneck è il DB e/o la banda, sul codice applicativo quasi sempre si può scalare "in orizzontale" aggiungendo hardware), e viceversa sei costantemente sottoposto a input "malicious" (da parte di script o proprio hacker), per cui un linguaggio "senza reti" (nessun check sui boundary degli array, undefined behavior in numerosi casi) non è particolarmente indicato.
In generale mi sembra che non stiate considerando numerosi "dark side" del C++, quali:
- undefined behavior sempre dietro l'angolo; ti dimentichi di inizializzare una variabile? Il suo valore è spazzatura qualunque; magari in sviluppo "per caso" è sempre un valore che non dà problemi, vai in produzione e non capisci perché non funziona più niente; overflow di interi con segno? se ti va bene fa wraparound, se no iniziano a succedere cose "impossibili" perché l'ottimizzatore da specifiche è autorizzato ad assumere che l'overflow non capiti mai; questa serie di post è abbastanza illuminante in proposito;
- nessuna rete di sicurezza nella gestione di array e puntatori; sfori da un array? stai sovrascrivendo memoria a caso. Se sei fortunato hai un crash subito, se sei sfortunato stai corrompendo un blocco di memoria usato da un pezzo della tua applicazione che non c'entra niente, che inizierà a dare i numeri tra un milione di righe di codice; ci sono tool per cercare di arginare questi problemi (valgrind, opzioni di build speciali per instrumentare il codice - address sanitizer - o i container STL - STL di debug), ma non sono affidabili al 100% (dato che ogni accesso sullo stack è potenzialmente valido) e hanno un forte impatto sulle prestazioni (valgrind in particolare, per cui se ci sono anche problemi di race condition diventano complicati da riprodurre);
- nessuna gestione automatica della memoria; gli smart pointer possono aiutare, ma non coprono tutti i casi, e in ogni caso la curva di apprendimento è abbastanza ripida. Questo è causa classica di bug come memory leak (ti sei dimenticato di deallocare la memoria per qualche oggetto; se questa cosa sta in un qualche loop, rapidamente la tua applicazione inizierà ad usare sempre più memoria) o use after free (hai cancellato un oggetto, ma hai ancora un puntatore ad esso; se provi ad usarlo, anche qui è undefined behavior - se sei fortunato hai un crash, se sei sfortunato comportamenti senza senso). Anche in linguaggi con gestione automatica della memoria puoi avere memory leak (sotto forma di object leak - continui a tenere riferimenti a oggetti che ormai non ti servono più), ma è più raro e soprattutto non esiste il rischio delle use-after-free.
- nessuna rete di sicurezza di default sul casting di puntatori; se faccio un cast "secco" (non dynamic_cast) di un puntatore a classe base in un puntatore a classe derivata non c'è nessun controllo sul fatto che il puntatore alla classe base fosse effettivamente del tipo della classe derivata; anche qui, crash se sei fortunato, cose senza senso altrimenti;
- in generale, il fatto che il linguaggio sia pieno di undefined behavior e implementation defined behavior vuol dire che non puoi impararlo provando e vedendo se funziona; il fatto che il tuo codice sulla tua macchina, con il tuo compilatore funzioni può tranquillamente essere un caso; hai fatto shift a sinistra più grande delle dimensioni del tipo che stai usando? su x86 funziona in una maniera, su ARM in un'altra (e se abiliti le ottimizzazioni magari funziona in un'altra maniera ancora); per quel che riguarda la specifica, è undefined behavior, quindi vale tutto;
- tempi di compilazione biblici; la grammatica C++ è estremamente incasinata, il sistema degli include è demenziale e richiede la rilettura di quantità assurde di codice per ogni translation unit, ogni istanziazione template ha un costo non indifferente, e anche l'ottimizzatore si porta via il suo tempo; il linking a sua volta non è banale ed è per la maggior parte single threaded. Dimenticatevi dei tempi di compilazioni rapidi di Java o C# (premo build, meno di un secondo dopo ho il mio eseguibile), in C++ per progetti medio-grossi si fa a tempo ad andarsi a prendere un caffè;
- la libreria standard, anche se in tempi recenti si sta espandendo, è minimalistica, spesso non particolarmente comoda da usare (l'idea è di fornire strumenti il più generale possibile da usare, non necessariamente pratici - vedi l'incubo che è std::chrono), e in molti punti ha chiari problemi di anzianità (molte funzioni di libreria ereditate dal C, specie quelle non rientranti) o semplicemente di cattiva progettazione (iostream, tutta la parte dei locale); la gestione di Unicode è un incubo;
- nessun supporto all'introspezione; vuoi scrivere un serializzatore? preparati a scrivere un bel po' di codice rinondante, un generatore di codice e/o una buona quantità di macro. Non è un caso che quelli di Qt si sono dovuti scrivere il MOC.
- in generale, la complessità del linguaggio è assurda rispetto a quello che fa; a livello basso, la complessità e l'ambiguità della grammatica rendono il parsing lento, la costruzione di tool che lavorino su file C++ sostanzialmente impossibile (un autocomplete ben funzionante è fattibile di fatto solo avendo dietro un compilatore C++ "vero", motivo per cui praticamente tutti ora usano libclang - con il risultato che è affidabile ma lento) e la diagnostica degli errori pessima (un grande classico è un punto e virgola mancante che genera paginate di errori incomprensibili riguardo a codice che non c'entra niente svariate righe dopo). Dall'altro canto, la complessità logica di diverse zone del linguaggio è assurda quanto inutile (l'interazione mortale tra risoluzione degli overload, conversioni implicite e template è all'origine di paginate di errori incomprensibili);
- le eccezioni sono supportate e funzionano correttamente, ma il linguaggio "core" non le usa, così pure come il grosso della libreria standard; gestire molti errori (come il division by zero o l'access violation) richiede codice platform-specific, e in ogni caso non è consigliato; in generale le eccezioni non contengono stack trace, che comunque una volta fatto l'unwinding dello stack non è più recuperabile; anche qui, l'unica è usare metodi platform-specific per ottenere lo stack trace e metterselo da parte in qualche maniera;
- non c'è un build system "prevalente", e in ogni caso fanno più o meno tutti schifo; se vuoi aggiungere una libreria di terze parti preparati a litigare con include path, errori di linking e incompatibilità a livello di libreria standard;
- sempre a questo proposito, non esiste una ABI C++ standard, per cui non è possibile distribuire librerie statiche in forma già compilata che funzionino su più compilatori; per quanto riguarda i container STL addirittura il layout binario cambia a seconda delle opzioni di compilazione, per cui o distribuisci la tua libreria sotto forma di sorgenti, o eviti tipi C++ sui boundary di interfaccia;
- ancora sulle librerie: non esiste nulla di standard che riguardi le librerie dinamiche, per cui ci sono grosse differenze tra piattaforma e piattaforma; anche qui, o si compila tutto con lo stesso compilatore, oppure bisogna stare attentissimi (1) a non usare tipi C++ sui boundary di interfaccia e (2) a non passare dati da deallocare tra client e libreria e viceversa, dato che l'heap dei due "lati" non è detto che sia lo stesso.
Potrei andare avanti ancora per un pezzo; in generale, il punto è che non si può considerare come uniche prerogative per la scelta di un linguaggio le performance e il fatto che sia multipiattaforma (qualunque cosa questo voglia dire; il C++ "vanilla" è multipiattaforma, ma l'unico IO che fornisce è su console o su file).
Amaro C++, il gusto pieno dell'undefined behavior.