Visual C++
Esplorazione delle nuove funzionalità C++ e MFC in Visual Studio 2010
Sumit Kumar
Visual Studio 2010 offre enormi vantaggi agli sviluppatori C++. Dalla possibilità di utilizzare le nuove funzionalità offerte da Windows 7 alle funzionalità di produttività migliorate per l'utilizzo di codebase di grandi dimensioni, c'è qualcosa di innovativo per ogni sviluppatore C++.
In questo articolo verrà spiegato come Microsoft ha affrontato alcuni dei principali problemi tipici dello sviluppo in C++. In particolare, Visual Studio 2010 adotta un modello di programmazione più moderno aggiungendo le funzionalità principali del linguaggio del prossimo standard C++0x e rivedendo la libreria standard affinché vengano sfruttate queste nuove funzionalità. Gli strumenti e le nuove librerie di programmazione parallela semplificano la creazione di programmi paralleli. Grazie a IntelliSense e alle funzionalità di comprensione del codice adattabili a codebase di grandi dimensioni, la produttività dello sviluppatore e le prestazioni complessive risulteranno notevolmente migliorate. Si potrà quindi trarre beneficio dalle prestazioni migliorate di librerie e altre funzionalità in fase di progettazione, compilazione e collegamento.
In Visual Studio 2010 il sistema di compilazione viene migrato a MSBuild per renderlo più personalizzabile e per supportare il multitargeting nativo. Infine, i miglioramenti apportati alla libreria MFC sfruttano la potenza delle nuove API di Windows 7, rendendo possibile la scrittura di applicazioni Windows 7 complete.
Di seguito verranno esaminati in dettaglio tutti i miglioramenti apportati a C++ in Visual Studio 2010.
Funzionalità del linguaggio principali C++0x
Il rinnovato standard C++ è ormai prossimo alla finalizzazione. Per poter acquisire familiarità con le estensioni di C++0x, il compilatore Visual C++ di Visual Studio 2010 abilita sei funzionalità del linguaggio principali C++0x: espressioni Lambda, parola chiave auto, riferimenti rvalue, static_assert, nullptr e decltype.
Le espressioni Lambda definiscono e costruiscono in modo implicito oggetti funzione senza nome. Forniscono inoltre una sintassi naturale priva di complessità per definire gli oggetti funzione dove vengono utilizzati, senza sovraccarichi a livello di prestazioni.
Gli oggetti funzione rappresentano un modo altamente efficace per personalizzare il comportamento degli algoritmi STL (Standard Template Library), inoltre possono incapsulare sia codice sia dati (a differenza delle funzioni normale). Non sono tuttavia facili da definire per la necessità di scrivere intere classi. Inoltre, per il fatto di non poter essere definiti nella posizione all'interno del codice in cui si tenta di utilizzarli (non rintracciabilità), il loro utilizzo risulta ancora più difficile. Si è tentato di risolvere alcuni dei problemi legati al livello di dettaglio e alla non rintracciabilità attraverso le librerie, ma poiché la sintassi diventa più complessa e gli errori del compilatore sono di difficile interpretazione non si sono rivelate di grande utilità. L'utilizzo di oggetti funzione dalle librerie è anche meno efficiente perché gli oggetti funzione definiti come membri dati non sono inline.
Le espressioni Lambda consentono di risolvere questi problemi. Nel frammento di codice seguente viene illustrata un'espressione Lambda utilizzata in un programma per rimuovere Integer tra le variabili x e y da un vettore di Integer.
v.erase(remove_if(v.begin(),
v.end(), [x, y](int n) {
return x < n && n < y; }),
v.end());
Nella seconda riga è riportata l'espressione Lambda. Le parentesi quadre, denominate lambda-introducer, indicano la definizione di un'espressione Lambda. Questa espressione Lambda accetta l'Integer n come parametro e l'oggetto funzione generato dall'espressione Lambda contiene i membri dati x e y. Si confronti questo oggetto a un oggetto funzione equivalente scritto manualmente per avere un'idea della praticità e del risparmio di tempo ottenibili con le espressioni Lambda:
class LambdaFunctor {
public:
LambdaFunctor(int a, int b) : m_a(a), m_b(b) { }
bool operator()(int n) const {
return m_a < n && n < m_b; }
private:
int m_a;
int m_b;
};
v.erase(remove_if(v.begin(), v.end(),
LambdaFunctor(x, y)), v.end());
La parola chiave auto è sempre esistita in C++, ma veniva raramente utilizzata perché non forniva alcun valore aggiunto. In C++0x questa parola chiave viene riadattata affinché determini automaticamente il tipo di una variabile a partire dal suo inizializzatore. Auto riduce il livello di dettaglio e aiuta a distinguere il codice ritenuto più importante. Evita la mancata corrispondenza tra i tipi e gli errori di troncamento. Consente inoltre di rendere più generico il codice consentendo la scrittura di modelli incentrati non tanto sui tipi di espressioni intermedie quando sui tipi non documentati come Lambda. Nel codice seguente viene illustrato come la parola chiave auto eviti all'utente di dover scrivere il tipo di modello nel ciclo for che scorre un vettore:
vector<int> v;
for (auto i = v.begin(); i != v.end(); ++i) {
// code
}
I riferimenti Rvalue sono un nuovo tipo di riferimento introdotto in C++0x che consente di risolvere il problema della copia non necessaria e di abilitare un inoltro senza problemi. Quando il lato destro di un'assegnazione è dato da un riferimento rvalue, l'oggetto di sinistra può sottrarre risorse dall'oggetto di destra anziché eseguire un'allocazione separata, abilitando così la semantica di spostamento.
L'inoltro perfetto consente di scrivere un singolo modello di funzione che accetta n argomenti arbitrari e di inoltrarli in modo trasparente a un'altra funzione arbitraria. In questo processo di inoltro, la natura dell'argomento (modificabile, const, lvalue o rvalue) viene preservata.
template <typename T1, typename T2> void functionA(T1&& t1, T2&& t2) {
functionB(std::forward<T1>(t1), std::forward<T2>(t2));
}
Poiché i riferimenti rvalue esulano dall'ambito di questo articolo, per ulteriori informazioni si consiglia di vedere la documentazione MSDN all'indirizzo msdn.microsoft.com/library/dd293668(VS.100).
Static_assert consente di testare le asserzioni in fase di compilazione anziché in fase di esecuzione. Consente di generare errori del compilatore con messaggi di errore personalizzati di facile lettura. Static_assert si rivela particolarmente utile per la convalida di parametri di modelli. Ad esempio, la compilazione del codice seguente genererà l'errore "Errore C2338: custom assert: n should be less than 5":
template <int n> struct StructA {
static_assert(n < 5, "custom assert: n should be less than 5");
};
int _tmain(int argc, _TCHAR* argv[]) {
StructA<4> s1;
StructA<6> s2;
return 0;
}
Nullptr aggiunge l'indipendenza dai tipi ai puntatori null ed è strettamente correlato ai riferimenti rvalue. La macro NULL (definita come 0) e il valore letterale 0 vengono comunemente utilizzati come puntatore null. Finora ciò non ha costituito un problema, ma in C++0x non funzionano correttamente a causa di potenziali problemi con l'inoltro perfetto. È stata quindi introdotta la parola chiave nullptr soprattutto per evitare errori incomprensibili nelle funzioni di inoltro perfetto.
Nullptr è una costante di tipo nullptr_t convertibile in qualsiasi tipo di puntatore, ma non in altri tipi come int o char. Oltre a essere utilizzato nelle funzioni di inoltro perfetto, nullptr può essere utilizzato ovunque la macro NULL sia stata utilizzata come puntatore null.
Si presti comunque attenzione perché NULL è ancora supportato dal compilatore e non è ancora stato sostituito da nullptr, questo principalmente per evitare interruzioni nel codice esistente a causa dell'utilizzo diffuso e spesso non appropriato di NULL. In futuro, nullptr potrà essere utilizzato ovunque NULL veniva utilizzato e NULL verrà considerato come una funzionalità intesa a garantire la compatibilità con le versioni precedenti.
Infine, decltype consente al compilatore di dedurre il tipo restituito di una funzione in base a un'espressione arbitraria e di rendere più generico l'inoltro perfetto. Nelle versioni precedenti, per due tipi arbitrari T1 e T2 non vi era nessuna possibilità di dedurre il tipo di un'espressione che utilizzava questi due tipi. La funzionalità decltype consente di dichiarare, ad esempio, un'espressione contenente argomenti di modello, ad esempio sum<T1, T2>() presenta il tipo T1+T2.
Miglioramenti alla libreria standard
Sono state riscritte parti significative della libreria C++ standard per trarre il massimo beneficio dalle nuove funzionalità del linguaggio C++0x e migliorare le prestazioni. Sono stati inoltre introdotti molti nuovi algoritmi.
La libreria standard utilizza al meglio i riferimenti rvalue per migliorare le prestazioni. Tipi come vector e list dispongono oggi di costruttori di spostamento e di operatori di assegnazione di spostamento specifici. Le riallocazioni di vector sfruttano i vantaggi della semantica di spostamento scegliendo i costruttori di spostamento, pertanto se i tipi dispongono di costruttori e di operatori di assegnazione di spostamento, la libreria li selezionerà automaticamente.
È ora possibile creare un puntatore condiviso a un oggetto mentre si costruisce l'oggetto utilizzando il nuovo modello di funzione di C++0x make_shared<T>:
auto sp =
make_shared<map<string,vector>>
(args);
In Visual Studio 2008 sarebbe necessario scrivere quanto segue per ottenere la stessa funzionalità:
shared_ptr<map<string,vector>>
sp(new map<string,vector>(args));
L'utilizzo di make_shared<T> è più pratico (il nome del tipo dovrà essere digitato un numero inferiore di volte), più affidabile (evita la classica perdita shared_ptr senza nome perché il puntatore e l'oggetto vengono creati simultaneamente) e più efficiente (viene eseguita un'allocazione dinamica di memoria anziché due).
La libreria contiene ora unique_ptr, un nuovo tipo di puntatore, più sicuro e "intelligente" abilitato dai riferimenti rvalue. Di conseguenza, auto_ptr è da considerarsi deprecato. unique_ptr evita gli errori di auto_ptr perché può essere spostato, ma non copiato. Consente di implementare la semantica di proprietà rigida senza incidere sulla sicurezza. Può anche essere utilizzato correttamente con i contenitori di Visual C++ 2010 che supportano i riferimenti rvalue.
I contenitori dispongono ora di nuove funzioni membro, cbegin e cend, che rappresentano un modo per utilizzare const_iterator a scopo di controllo indipendentemente dal tipo di contenitore:
vector<int> v;
for (auto i = v.cbegin(); i != v.cend(); ++i) {
// i is vector<int>::const_iterator
}
Visual Studio 2010 aggiunge alla libreria standard la maggior parte degli algoritmi proposti in vari documenti su C++0x. Un sottoinsieme della libreria di conversioni Dinkumware è ora disponibile nella libreria standard, pertanto è ora possibile eseguire conversioni da UTF-8 a UTF-16, ad esempio, senza incontrare difficoltà. La libreria standard consente la propagazione delle eccezioni tramite exception_ptr. L'intestazione <random> ha subito aggiornamenti significativi. In questa versione viene fornito un elenco collegato singolarmente denominato forward_list. L'intestazione <system_error> della libreria consente di migliorare la diagnostica. Inoltre, molte delle funzionalità TR1 presenti nello spazio dei nomi std::tr1 della versione precedente (come shared_ptr e regex) sono ora incluse nella libreria standard nello spazio dei nomi std.
Miglioramenti apportati alla programmazione concorrente
In Visual Studio 2010 viene introdotta la Parallel Computing Platform, una piattaforma per l'elaborazione parallela che consente di scrivere codice parallelo a elevate prestazioni evitando i bug di concorrenza. In questo modo sarà possibile evitare alcuni dei problemi tipici correlati alla concorrenza.
La Parallel Computing Platform consiste in quattro parti principali: Concurrency Runtime (ConcRT), Parallel Patterns Library (PPL), Asynchronous Agents Library e profiling e debug paralleli.
ConcRT è il livello software più basso che comunica con il sistema operativo e risolve i conflitti tra più componenti concorrenti che si contengono le risorse. Poiché si tratta di un processo in modalità utente, può recuperare le risorse quando vengono utilizzati i relativi meccanismi di blocco collaborativi. ConcRT supporta la rintracciabilità ed evita il passaggio delle attività da un processore a un altro. Utilizza anche la pianificazione della modalità utente di Windows 7 per ottimizzare le prestazioni anche quando non viene utilizzato il meccanismo di blocco collaborativo.
PPL fornisce i modelli per la scrittura di codice parallelo. Se è possibile scomporre un calcolo in più calcoli rappresentabili tramite funzioni o oggetti funzione, ogni calcolo secondario potrà essere rappresentato da un'attività. Il concetto di attività è molto più vicino all'ambito del problema, a differenza dei thread che allontanano dall'ambito del problema facendo pensare all'hardware, al sistema operativo, alle sezioni critiche e così via. Un'attività può essere eseguita contemporaneamente ad altre attività indipendentemente dalle operazioni svolte durante queste altre attività. Ad esempio, l'ordinamento di due metà diverse di una matrice può essere eseguito simultaneamente da due diverse attività.
PPL include classi parallele (task_handle, task_group e structured_task_group), algoritmi paralleli (parallel_invoke, parallel_for e parallel_for_each), contenitori paralleli (combinable, concurrent_queue e concurrent_vector) e le primitive di sincronizzazione che riconoscono ConcRT (critical_section, event e reader_writer_lock) e tutti considerano le attività come un concetto di prima classe. Tutti i componenti di PPL risiedono nello spazio dei nomi di concorrenza.
I gruppi di attività consentono di eseguire una serie di attività e di attendere il completamento di tutte. Tornando all'esempio dell'ordinamento, le attività che gestiscono due metà della matrice possono costituire un gruppo di attività. Si ha la garanzia che queste due attività vengano completate alla fine della chiamata della funzione membro di attesa, come illustrato nell'esempio di codice di un quicksort ricorsivo scritto utilizzando espressioni Lambda e attività parallele:
void quicksort(vector<int>::iterator first,
vector<int>::iterator last) {
if (last - first < 2) { return; }
int pivot = *first;
auto mid1 = partition(first, last, [=](int elem) {
return elem < pivot; });
auto mid2 = partition( mid1, last, [=](int elem) {
return elem == pivot; });
task_group g;
g.run([=] { quicksort(first, mid1); });
g.run([=] { quicksort(mid2, last); });
g.wait();
}
Questo codice può essere ulteriormente migliorato utilizzando un gruppo di attività strutturato abilitato dall'algoritmo parallel_invoke. Accetta da due a 10 oggetti funzione e li esegue tutti in parallelo utilizzando tutti i core forniti da ConcRT e ne attende il completamento:
parallel_invoke(
[=] { quicksort(first, mid1); },
[=] { quicksort(mid2, last); } );
parallel_invoke(
[=] { quicksort(first, mid1); },
[=] { quicksort(mid2, last); } );
Ognuna di queste attività potrebbe creare una serie di attività secondarie. ConcRT gestisce il mapping tra le attività e i thread di esecuzione e si assicura che tutti i core vengano utilizzati in modo ottimale. La scomposizione del calcolo nel numero possibile di attività consentirà quindi di trarre beneficio da tutti i core disponibili.
Un altro algoritmo parallelo utile è parallel_for che può essere utilizzato per scorrere gli indici in modo simultaneo:
parallel_for(first, last, functor);
parallel_for(first, last, step, functor);
Gli oggetti funzione vengono chiamati simultaneamente con ciascun indice, partendo con il primo e terminando con l'ultimo.
Asynchronous Agents Library offre un modello di programmazione basato sul flusso di dati in base al quale i calcoli dipendono dal fatto che i dati necessari diventino disponibili o meno. La libreria è basata sui concetti di agenti, blocchi di messaggi e funzioni di passaggio a messaggi. Un agente è un componente di un'applicazione che esegue determinati calcoli e comunica in modo asincrono con altri agenti per risolvere un problema di calcolo più grande. Questa comunicazione tra agenti viene realizzata attraverso funzioni di passaggio messaggi e blocchi di messaggio.
Gli agenti presentano un ciclo di vita osservabile che passa attraverso diverse fasi. Non sono stati progettati per essere utilizzati per il parallelismo specifico che si ottiene tramite attività PPL. Gli agenti vengono compilati sui componenti di pianificazione e gestione delle risorse di ConcRT e consentono di evitare i problemi derivanti dall'utilizzo di memoria condivisa nelle applicazioni concorrenti.
Per utilizzare al meglio questi modelli, non è necessario collegare o ridistribuire altri componenti aggiuntivi. ConcRT, PPL e Asynchronous Agents Library sono stati implementati in msvcr100.dll, msvcp100.dll e libcmt.lib/libcpmt.lib insieme alla libreria standard. PPL e Asynchronous Agents Library sono in prevalenza implementazioni con sola intestazione.
Il debugger di Visual Studio supporta ConcRT, per questo motivo è ora più facile eseguire il debug dei problemi di concorrenza. In Visual Studio 2008, invece, il concetto di parallelismo di livello superiore non era riconosciuto. In Visual Studio 2010 è disponibile un profiler di concorrenza che consente di visualizzare il comportamento delle applicazioni parallele. Il debugger dispone di nuove finestre in cui viene visualizzato lo stato di tutte le attività in un'applicazione e i relativi stack di chiamate. Nella Figura 1 vengono illustrate le finestre Attività in parallelo e Stack in parallelo.
Figura 1 Finestre di debug Stack in parallelo e Attività in parallelo
IntelliSense e produttività in fase di progettazione
Visual Studio 2010 include una nuova infrastruttura di esplorazione e IntelliSense. Oltre ad agevolare la scalabilità e la capacità di risposta per progetti che prevedono codebase di grandi dimensioni, i miglioramenti all'infrastruttura hanno reso possibili alcun funzionalità di produttività in fase di progettazione.
Le funzionalità di IntelliSense come la segnalazione degli errori in tempo reale e le descrizioni comandi di Informazioni rapide sono basate su un nuovo front-end del compilatore che analizza l'unità di conversione completa per fornire informazioni complete e precise sulla semantica del codice, anche quando i file di codice vengono modificati.
Tutte le funzionalità di esplorazione del codice, come la visualizzazione e la gerarchia di classi, utilizzano ora le informazioni sul codice sorgente archiviate in un database SQL che consente l'indicizzazione e prevede un footprint di memoria fisso. A differenza delle versioni precedenti, l'IDE di Visual Studio 2010 mantiene costantemente attiva la sua capacità di risposta, di conseguenza non è più necessario attendere che le unità di compilazione vengano rianalizzate in risposta alla modifica del file di intestazione.
La segnalazione degli errori in tempo reale (i noti segni rossi) di IntelliSense visualizza gli errori di semantica e di sintassi del compilatore durante l'esplorazione e la modifica del codice. Passando il puntatore del mouse sull'errore si visualizza il messaggio di errore (Figura 2). Nella finestra Elenco errori vengono inoltre visualizzati l'errore dal file attualmente visualizzato e gli errori di IntelliSense da altri punti dell'unità di compilazione. Tutte queste informazioni sono disponibili senza eseguire una compilazione.
Figura 2 Segnalazione degli errori in tempo reale: errori di IntelliSense
Viene inoltre visualizzato un elenco di file di inclusione rilevanti in un elenco a discesa mentre si digita #include e l'elenco viene perfezionato man mano che si digita.
La nuova funzionalità Passa a (Modifica | Passa a o CTRL+virgola) consentirà di migliorare la propria produttività nella ricerca di file o simboli. Questa funzionalità produce risultati di ricerca in tempo reale, in base alle sottostringhe digitate, cercando corrispondenze tra le stringhe di input e i simboli e i file nei progetti (Figura 3). Questa funzionalità può anche essere utilizzata con file C# e Visual Basic ed è estensibile.
Figura 3 Utilizzo della funzionalità Passa a
La Gerarchia chiamate, che può essere richiamata utilizzando CTRL+K, CTRL+T o tramite il menu di scelta rapida, consente di passare a tutte le funzioni chiamate da una particolare funzione e da tutte le funzioni che effettuano chiamate a una particolare funzione. Si tratta di una versione migliorata della funzionalità Visualizzatore chiamate presente nelle versioni precedenti di Visual Studio. La finestra Gerarchia chiamate è organizzata in modo più funzionale e visualizza sia le chiamate provenienti sia quelle dirette alle strutture ad albero per qualsiasi funzione visibile nella stessa finestra.
Si noti che mentre tutte le funzionalità di esplorazione del codice sono disponibili per C++ e C++/CLI, le funzionalità correlate a IntelliSense come la segnalazione degli errori in tempo reale e Informazioni rapide non saranno disponibili per C++/CLI nella versione finale di Visual Studio 2010.
In questa versione vengono anche migliorate altre funzionalità di base dell'editor. Ad esempio, la ben nota funzionalità Trova tutti i riferimenti utilizzata per cercare i riferimenti agli elementi di codice (classi, membri di classi, funzioni e così via) all'interno della soluzione è ora più flessibile. I risultati di ricerca possono essere ulteriormente definiti tramite l'opzione Risolvi risultati nel menu di scelta rapida.
Il codice inattivo preserva ora le informazioni semantiche mantenendo la colorazione (anziché diventare grigio). Nella Figura 4 viene illustrato come il codice inattivo risulti disattivato pur preservando colori diversi per trasmettere le informazioni semantiche.
Figura 4 Blocchi di codice inattivo mantengono la colorazione
Oltre alle funzionalità descritte in precedenza, l'esperienza dell'editor generale è stata migliorata in Visual Studio 2010. Il nuovo IDE basato su Windows Presentation Foundation (WPF) è stato riprogettato per migliorare l'organizzazione dello spazio e la leggibilità. Le finestre di documento come l'editor di codice e la visualizzazione Progettazione possono rimanere all'esterno della finestra dell'IDE principale ed essere visualizzate su più monitor. È ora più facile applicare lo zoom avanti e indietro nella finestra dell'editor di codice utilizzando CTRL e la rotellina del mouse. Anche nell'IDE è stato migliorato il supporto per l'estensibilità.
Sistemi di compilazione e del progetto
Visual Studio 2010 vanta sostanziali miglioramenti al sistema di compilazione e al sistema del progetto per i progetti C++.
La più importante modifica è che MSBuild viene ora utilizzato per compilare progetti C++. MSBuild è un motore di orchestrazione di compilazione estensibile e basato su XML che veniva utilizzato per i progetti C# e Visual Basic nelle versioni precedenti di Visual Studio. MSBuild è ora il sistema di compilazione Microsoft comune a tutti i linguaggi. Può essere utilizzato sia nel laboratorio di compilazione sia nei singoli computer di sviluppo.
I processi di compilazione di C++ vengono ora definiti in termini di file di attività e destinazione di MSBuild e consentono un livello superiore di personalizzazione, controllo e trasparenza.
Il tipo di progetto C++ dispone di una nuova estensione: .vcxproj. Visual Studio aggiornerà automaticamente le soluzioni e i file vcproj precedenti nel nuovo formato. È inoltre disponibile uno strumento da riga di comando, vcupgrade.exe, per aggiornare singoli progetti dalla riga di comando.
In passato, era possibile utilizzare unicamente il set di strumenti (compilatore, librerie e così via) fornito con la versione corrente di Visual Studio. Era necessario attendere finché non si era pronti a eseguire la migrazione al nuovo set di strumenti prima di poter utilizzare il nuovo IDE. Visual Studio 2010 risolve questo problema consentendo l'utilizzo di più versioni del set di strumenti per la compilazione. È ad esempio possibile utilizzare il compilatore e le librerie di Visual C++ 9.0 anche se si lavora in Visual Studio 2010. Nella Figura 5 vengono illustrate le impostazioni di multitargeting nella pagina delle proprietà.
Figura 5 Targeting di più set di strumenti della piattaforma
L'utilizzo di MSBuild rende ancora più estensibile il sistema di compilazione di C++. Quando il sistema di compilazione predefinito non è sufficiente a soddisfare le proprie esigenze, è possibile estenderlo aggiungendo un proprio strumento o qualsiasi altro passaggio di compilazione. MSBuild utilizza le attività come unità riutilizzabili di codice eseguibile per eseguire le operazioni di compilazione. È possibile creare attività personalizzate ed estendere il sistema di compilazione definendole in un file XML. MSBuild genera le attività da questi file XML in modo istantaneo.
I set di strumenti e le piattaforme esistenti possono essere estesi aggiungendo file props e targets per passaggi aggiuntivi nelle cartelle ImportBefore e ImportAfter. Questa operazione è particolarmente utile per i provider di librerie e strumenti che vorrebbero estendere i loro sistemi di compilazione esistenti. È inoltre possibile definire un set di strumenti della piattaforma personalizzato. MSBuild fornisce informazioni di diagnostica più accurate che agevolano l'attività di debug in fase di compilazione e rendono più affidabili le compilazioni incrementali. È inoltre possibile creare sistemi di compilazione maggiormente correlati al controllo del codice sorgente e al laboratorio di compilazione e meno dipendenti dalla configurazione del computer di sviluppo.
Il sistema del progetto che poggia sul sistema di compilazione trae anch'esso notevoli vantaggi dalla flessibilità e dall'estensibilità di MSBuild. Il sistema del progetto comprende i processi MSBuild e consente a Visual Studio di visualizzare in modo trasparente le informazioni rese disponibili da MSBuild.
Le personalizzazioni sono visibili e possono essere configurate tramite le pagine delle proprietà. È possibile configurare il sistema del progetto per l'utilizzo di una propria piattaforma (piattaforme x86 o x64 esistenti) o di un proprio debugger. Le pagine delle proprietà consentono di scrivere e integrare componenti in grado di aggiornare in modo dinamico il valore di proprietà che dipendono dal contesto. Il sistema del progetto di Visual Studio 2010 consente inoltre di scrivere un'interfaccia utente personalizzata per leggere e scrivere le proprietà anziché utilizzare le relative pagine.
Compilazione più rapida e prestazioni migliorate
Oltre ai miglioramenti apportati all'esperienza utente in fase di progettazione descritti finora, Visual Studio 2010 migliora la velocità di compilazione, la qualità e le prestazioni delle applicazioni compilate con il compilatore di Visual C++, in seguito ai miglioramenti apportati a livello di generazione di codice al back-end del compilatore.
Le prestazioni di alcune applicazioni dipendono dal working set. Le dimensioni del codice per l'architettura x64 sono state ridotte dal 3 al 10 percento grazie a numerose ottimizzazioni in questa versione, con conseguente miglioramento delle prestazioni di tali applicazioni.
La generazione di codice SIMD (Single Instruction Multiple Data), importante per gli sviluppatori di giochi, audio, video e grafica, è stata ottimizzata per migliorare le prestazioni e la qualità del codice. I miglioramenti includono la rottura di dipendenze false, la vettorizzazione di inizializzazioni di vettori costanti e la migliore allocazione dei registri XMM per rimuovere carichi ridondanti, archivi e spostamenti. È inoltre stata ottimizzata la famiglia intrinseca di __mm_set_**, __mm_setr_** e __mm_set1_**.
Per prestazioni migliori, le applicazioni devono essere compilate utilizzando Generazione codice in fase di collegamento (LTCG, Link Time Code Generation) e Ottimizzazione PGO (PGO, Profile Guided Optimization).
La velocità di compilazione sulle piattaforme x64 è stata migliorata ottimizzando la generazione di codice x64. La compilazione con LTCG, consigliata per una migliore ottimizzazione, è in genere più lenta della compilazione non LTCG nelle applicazioni di grandi dimensioni. In Visual Studio 2010, la compilazione LTCG è stata migliorata del 30 percento. Un thread dedicato alla scrittura di file PDB è stato introdotto in questa versione, pertanto si noteranno miglioramenti in fase di collegamento quando si utilizzerà l'opzione /DEBUG.
Le esecuzioni della strumentazione PGO sono state accelerate aggiungendo il supporto per versioni senza blocco dei binari instrumentati. È inoltre disponibile una nuova opzione POGO, PogoSafeMode, che consente di specificare se utilizzare la modalità sicura o la modalità rapida per l'ottimizzazione di un'applicazione. Il comportamento predefinito è dato dalla modalità rapida. La modalità sicura è thread-safe, ma più lenta della modalità rapida.
La qualità del codice generato dal compilatore è stata migliorata. È ora presente il supporto completo per Advanced Vector Extensions (AVX), molto importante per le applicazioni che fanno un uso cospicuo della virgola mobile nei processori AMD e Intel tramite opzioni intrinseche e /archAVX:. Il calcolo dei numeri a virgola mobile è più preciso con l'opzione /fp:fast.
Creazione di applicazioni per Windows 7
Windows 7 ha introdotto diverse nuove tecnologie e funzionalità e ha aggiunto nuove API. Visual Studio 2010 consente l'accesso a tutte le nuove API di Windows. I componenti di Windows SDK necessari per scrivere codice per API Windows native sono inclusi in Visual Studio 2010. È possibile usufruire di innovazioni quali Direct3D 11, DirectWrite, Direct2D e le API del Servizio Web di Windows utilizzando le librerie e le intestazioni SDK disponibili in Visual Studio 2010.
Oltre a rendere disponibili tutte le API Windows agli sviluppatori, questa versione di Visual Studio semplifica la scrittura di applicazioni per Windows tramite MFC. È possibile accedere a numerose funzionalità di Windows 7 tramite le librerie MFC senza dovere scrivere direttamente nelle API native. Basterà ricompilarle per rendere le applicazioni MFC compatibili con Windows 7. E le nuove applicazioni potranno sfruttare completamente tutte le nuove funzionalità.
MFC offre un'integrazione migliorata con la shell di Windows. L'integrazione dell'applicazione con Esplora risorse risulta alquanto migliorata grazie all'utilizzo dei gestori di file per l'anteprima e la ricerca che sono stati aggiunti in questa versione. Queste funzionalità vengono fornite come opzioni nella Creazione guidata applicazione MFC, come illustrato nella Figura 6. MFC genererà automaticamente il progetto DLL ATL che implementa questi gestori.
Figura 6 Creazione guidata applicazione MFC con opzioni di gestori file
Una delle principali modifiche apportate all'interfaccia utente di Windows 7 è la nuova barra delle applicazioni. MFC consente di utilizzare rapidamente funzionalità quali jump list, miniature a schede, anteprime, indicatori di stato, immagini sovrapposte alle icone e così via. Nella Figura 7 vengono illustrate anteprime e miniature a schede per un'applicazione MFC MDI a schede.
Figura 7 Anteprima e miniature a schede in un'applicazione MFC
L'interfaccia utente della barra multifunzione è in puro stile Windows 7 e l'applicazione può passare all'istante da diverse barre multifunzione in stile Office alla barra multifunzione di Windows 7 durante lo sviluppo tramite l'elenco a discesa di stili, come illustrato nella Figura 8.
Figura 8 Elenco a discesa di stili della barra multifunzione in un'applicazione MFC
MFC consente di predisporre le applicazioni per il multitocco e richiede messaggi specifici da gestire in base ai vari eventi che possono verificarsi. La registrazione per gli eventi di tocco e di movimento indirizzerà tali eventi per l'applicazione. Grazie a MFC le applicazioni supportano per impostazione predefinita valori DPI elevati, pertanto sono facilmente adattabili a schermi di questo tipo senza apparire disturbate. MFC ridimensiona e modifica i tipi di carattere e altri elementi per assicurarsi che l'interfaccia utente continui ad apparire nitida sugli schermi a DPI elevato.
Oltre alle nuove funzionalità di Windows 7, altre caratteristiche esistenti fin da Windows Vista ma non ancora incluse nelle versioni precedenti di MFC sono finalmente state inserite. Ad esempio, Gestione riavvio è una funzionalità utile introdotta in Windows Vista che consente a un'applicazione di eseguire un salvataggio prima della chiusura. L'applicazione può richiamare questa funzionalità e ripristinarne lo stato in caso di riavvio. È ora possibile utilizzare Gestione riavvio nell'applicazione MFC per gestire gli arresti anomali e riavviare in modo più corretto. Aggiungere semplicemente una riga di codice per abilitare il riavvio e il ripristino nell'applicazione esistente:
CMyApp::CMyApp() {
m_dwRestartManagerSupportFlags =
AFX_RESTART_MANAGER_SUPPORT_RESTART;
// other lines of code ...
}
Le nuove applicazioni MFC ottengono automaticamente questa funzionalità tramite la Creazione guidata applicazione MFC. Il meccanismo di salvataggio automatico è disponibile alle applicazioni che salvano documenti e il relativo intervallo può essere definito dall'utente. Le applicazioni possono scegliere solo il supporto del riavvio o l'avvio del ripristino dell'applicazione (applicabile alle applicazioni di tipo Doc/Visualizzazione) nella Creazione guidata applicazione MFC.
Un'altra aggiunta è la finestra di dialogo Attività di Windows che è un tipo migliorato di finestra di messaggio (Figura 9). MFC dispone ora di un wrapper per la finestra di dialogo Attività che è possibile utilizzare nelle applicazioni.
Figura 9 Finestra di dialogo Attività
La Creazione guidata classe MFC è tornata
Non solo sono state aggiunte nuove funzionalità alla libreria MFC, ma è ora più facile utilizzare MFC nell'IDE di Visual Studio. Una delle funzionalità più richieste, la Creazione guidata classe MFC (Figura 10) è stata reintrodotta e migliorata. È ora possibile aggiungere classi, gestori eventi e altri elementi all'applicazione utilizzando la Creazione guidata classe MFC.
Figura 10 Creazione guidata classe MFC
Un'altra aggiunta è la finestra di progettazione della barra multifunzione che consente di progettare graficamente l'interfaccia utente della barra multifunzione (anziché definirla nel codice come in Visual Studio 2008) e archiviarla come risorsa XML. Questa finestra di progettazione è evidentemente utile per la creazione di nuove applicazioni, ma ovviamente anche le applicazioni esistenti possono utilizzarla per aggiornare le loro interfacce utente. È possibile creare la definizione XML aggiungendo temporaneamente una riga di codice alla definizione del codice esistente dell'interfaccia utente della barra multifunzione:
m_wndRibbonBar.SaveToXMLFile(L"YourRibbon.mfcribbon-ms");
Il file XML risultante potrà essere utilizzato come file di risorsa e ulteriori modifiche potranno essere effettuate tramite la finestra di progettazione della barra multifunzione.
Conclusioni
Visual Studio 2010 è una tappa fondamentale nell'evoluzione di Visual C++ in grado di semplificare la vita degli sviluppatori per diversi aspetti. In questo articolo i vari miglioramenti apportati a C++ sono stati solo annunciati. Per un approfondimento di tutte queste funzionalità, fare riferimento alla documentazione MSDN e al blog del team di Visual C++ all'indirizzo blogs.msdn.com/vcblogche è anche stato utilizzato come base per alcune delle sezioni di questo articolo.
Sumit Kumarè un program manager nel team dell'IDE di Visual C++. Possiede una laurea specialistica in Informatica dell'Università del Texas a Dallas.
Si ringraziano i seguenti esperti tecnici:Stephan T. Lavavej, Marian Luparu e Tarek Madkour