Ieri, in occasione dell’Italian Agile Day 2013, ++it (rappresentata da Marco Arena) ha presentato con Paolo Polce un talk dal titolo “Effective Code Transformations in C++“. Obiettivo della sessione è stato quello di mostrare il nuovo C++ anche ad una platea di sviluppatori provenienti da tecnologie diverse dal nativo. Con piacevole sorpresa la platea era composta anche (e in buona parte) da programmatori C++! Quindi il talk è stato molto più interattivo e divertente del previsto, arricchito da diverse domande interessanti alle quali abbiamo risposto durante la sessione e che riporteremo alla fine di questo post.
Intanto grazie all’Agile Day e tutti i suoi organizzatori per averci dato la possibilità di raccontare, seppur in piccolo, il nuovo C++. Grazie a tutti i partecipanti alla nostra sessione. E grazie a tutti coloro che si uniranno alla nostra giovane comunità!
In questo articolo vorremmo fare un wrap-up dei contenuti, nonché segnalarvi dove poter scaricare slide e la solution che abbiamo utilizzato per mostrare alcune demo live. Prima di iniziare rinnovo la richiesta di feedback a tutti i partecipanti, preziosissima per migliorare! Potete contribuire su joind.in. Grazie!
Slides su SlideShare
Live-demo:
https://ilpropheta@bitbucket.org/ilpropheta/iad2013.git
Note delle live-demo:
- abbiamo usato Visual Studio 2013 Preview;
- abbiamo mostrato solo i file main.cpp e PPLSesame.cpp;
- GMOCK è già nella solution, in un folder apposito, quindi basta clonare tutto il repo per essere pronti a compilare ed eseguire!
Contenuti del talk
Essendo ospitati da una conferenza multi-tecnologica, non potevamo dare per scontato che la platea conoscesse perfettamente il linguaggio e le novità. Per questo abbiamo puntato su un contenuto “aperto a tutti”, soprattutto a chi non aveva un background C++. Il talk è stato una sorta di intervista da parte di Paolo (che programma in C++ dai primi anni ’90) a Marco (“rappresentante” delle novità).
Al centro del talk le trasformazioni efficaci, ovvero mutazioni che rendono il codice più leggibile, manutenibile, modificabile e compatto. Le trasformazioni affrontate sono state di due categorie: codice old-style C++98 portato in C++11/14 e codice multi-thread C++11 migliorato con costrutti più appropriati. Il talk è stato diviso quindi in tre parti:
- Esempi di codice C++98 trasformati in C++11/14. Il risultato è stato poi confrontato con C#, trovando grandi somiglianze.
- Patterns: (1) come scrivere una Factory in C++11/14, (2) RAII e DEFER.
- Multi-threaded C++ con tre trasformazioni C++11 (thread – future – future/promise) e tre trasformazioni con task PPL.
Chiaramente seguire solo con le slide non rende come quello che abbiamo detto a voce, però è una buona traccia. Gli argomenti scelti per la presentazione sono stati pochi (per ragioni di tempo) ma significativi.
Contenuti delle live-demo
La parte 3 (multithreading) è stata interamente raccontata al PC con un Visual Studio 2013 Preview davanti agli occhi. Ecco riassunto quello che abbiamo mostrato:
- Un banale esempio di thread C++11 che calcola la media di un vettore mentre nel main viene calcolato in parallelo il massimo.
- Prima trasformazione: usiamo un future (un contenitore asincrono di un risultato) creato con async.
- Seconda trasformazione: e se vogliamo gestire il thread a mano (o abbiamo bisogno di fare altro dopo che il risultato del future è stato calcolato)? Mostriamo le promise (provider asincrono del risultato di un future).
- Introduciamo la Parallel Patterns Library (PPL) di Microsoft. Disponibile giù da Visual Studio 2010 e poi migliorata ancora nel 2012 con enfasi su task composition e async. Mostriamo quindi un primo esempio completamente sincrono (non c’è concorrenza) dove: prima si finge di leggere un file, poi questo viene decorato con tag HTML, poi si simulano alcune operazioni sul main e infine si visualizza il file decorato a console.
- Prima trasformazione con un task PPL, dove almeno la lettura è parallela ad alcune operazioni sul main. Ancora bloccante la decorazione e le successive operazioni sul main.
- Ultima trasformazione con task.then (continuation), dove lettura, decorazione e operazioni sul main sono completamente asincrone. In particolare dopo la lettura (asincrona) del file, il nuovo task (ancora asincono) diventa la decorazione. La continuation ha diversi benefici, come composizione dei task e propagazione delle eccezioni.
Le vostre domande
Due premesse:
- Hai seguito il talk e hai altre domande? Scrivile qui in un commento oppure apri una discussione specifica sul forum!
- Vuoi contribuire migliorando le risposte alle domande che abbiamo dato durante il talk? Commenta l’articolo!
Ecco alcune domande che ci avete fatto (perdonate se ne dimentichiamo qualcuna) e la sintesi delle nostre risposte:
- La Factory ritorna unique_ptr<IWriter> ma perché è possibile ritornare uno unique_ptr<CoutWriter> (con CoutWriter che deriva da IWriter)?
R: Perché CoutWriter è convertibile in un IWriter. Il move-constructor dello unique_ptr è generico.
- Posso castare lo unique_ptr<IWriter> ad un IWriter*?
R: Sì, ma esplicitamente. Puoi ottenere l’IWriter* che lo unique_ptr sta gestendo usando .get().
- Cosa succede se usando async, la lambda tira eccezione?
R: Viene propagata al .get() del future.
- Posso creare un thread che non parte subito?
R: Puoi creare un thread “vuoto”, cioè non associato a nessun flusso di esecuzione e poi assegnarlo in un secondo momento. Però, che noi sappiamo, non puoi evitare che il thread parta se l’hai costruito con un callable-object (e.g lambda).
- Puoi fare .get() su un future ma solo per un certo periodo di tempo?
R: Sì, puoi usare .wait_for()/wait_until() passando opportune unità temporali di std::chrono e ottenere uno future_status che ti dice se il risultato è pronto.
- Puoi chiamare .get() sul future più di una volta?
R: No, è come se stessi “prelevando” il risultato dal contenitore. Puoi usare uno shared_future per leggere il risultato più volte e da più threads. Nota a posteriori: “prelevando” vuol dire “muovendo”, ma non avendo parlato di move-semantics non potevamo spiegare il concetto in questi termini.
- E’ possibile fare in modo che il thread associato al future parta solo quando fai .get()? Una specie di lazy evaluation?
R: Sì, puoi rendere la chiamata sincrona (nello stesso thread) passando come primo parametro di std::async una politica di lancio deferred.
- Quali compilatori supportano le nuove features?
R: Visual Studio 2010 già conteneva diverse cose come le lambda, la gli smart pointers e auto. Visual Studio 2012 ha quasi tutto il supporto della concorrenza più altre cose. Il 2013 va ancora più avanti ma non è ancora completo. Clang è invece 100% compliant al C++11 e la prossima release lo sarà anche per il C++14. Anche GCC è full compliant al C++11.
- Ma il C++14 è già standard ISO?
R: Non è ancora stato ufficialmente “rilasciato” ma tutto quello che ci deve finire dentro è stato deciso, quindi è come se ci fosse un “bollo papale” 🙂
- Cosa consigliate per provare le nuove features?
R: La cosa più semplice che puoi fare è utilizzare qualche compilatore online. Ne abbiamo uno anche noi ed è qui. Chiaramente per approfondire ti conviene scegliere un ambiente, studiare e provare!
Con la speranza che quanto abbiamo presentato sia stato gradito, aspettiamo i vostri feedback! Grazie ancora!