Visual C++

Exploración de nuevas características de C++ y MFC en Visual Studio 2010

Sumit Kumar

Visual Studio 2010 presenta grandes beneficios para desarrolladores de C++. Desde la capacidad de emplear las nuevas características ofrecidas por Windows 7 hasta las características de productividad mejoradas para trabajar con grandes bases de códigos, hay algo nuevo y mejorado para cada desarrollador de C++.

En este artículo, explicaré la manera en que Microsoft ha abordado algunos de los numerosos problemas a los que se enfrentan los desarrolladores de C++. Específicamente, Visual Studio 2010 permite un modelo de programación más moderno al agregar características de lenguaje básico del próximo estándar C++0x y al renovar la biblioteca estándar para aprovechar las nuevas características de lenguaje. Existen nuevas bibliotecas de programación paralela y herramientas para simplificar la creación de programas paralelos. También encontrará un rendimiento general y una productividad de desarrollador mejorados gracias a IntelliSense y a características de comprensión de códigos que escalan a grandes bases de códigos. También se beneficiará del rendimiento mejorado de las bibliotecas y otras características en el tiempo de diseño, el tiempo de creación, el tiempo de compilación y el tiempo de vinculación.

Visual Studio 2010 migra el sistema de creación a MSBuild para hacerlo más personalizable y admitir compatibilidad con múltiples versiones (multi-targeting) nativa. Las mejoras en la biblioteca de MFC aprovechan la eficiencia de las nuevas API de Windows 7, lo que permite escribir excelentes aplicaciones de Windows 7.

Observemos más detenidamente estos avances centrados en C++ en Visual Studio 2010.

Características de lenguaje básico C++0x

El próximo estándar C++ avanza lentamente hacia su finalización. Para ayudarlo a comenzar con las extensiones de C++0x, el compilador de Visual C++ de Visual Studio 2010 permite seis características de lenguaje básico C++0x: expresiones lambda, la palabra clave auto, referencias rvalue, static_assert, nullptr y decltype.

Las expresiones lambda definen implícitamente objetos de función no nombrados y los construyen. Las lambdas ofrecen una sintaxis ligera natural para definir objetos de función donde se usen, sin incurrir en sobrecarga de rendimiento.

Los objetos de función son una manera muy eficiente de personalizar el comportamiento de algoritmos de biblioteca de plantillas estándar (STL) y pueden encapsular código y datos (a diferencia de las funciones planas). Sin embargo, los objetos de función no son convenientes para definir, debido a la necesidad de escribir clases enteras. Además, no se definen en el lugar en su código fuente donde intenta usarlos y el hecho de que no sean locales los hace más difíciles de usar. Las bibliotecas han intentado migrar algunos de los problemas de nivel de detalle y de no localidad, pero no ofrecen mucha ayuda debido a que la sintaxis se vuelve complicada y los errores de compilador no son muy amigables. El uso de objetos de función desde bibliotecas también es menos eficiente, dado que las llamadas de función a objetos de función definidos como miembros de datos no se reemplazan por una instancia del cuerpo de la función.

Las expresiones lambda abordan estos problemas. El siguiente fragmento de código muestra una expresión lambda que se usa en un programa para eliminar enteros entre variables x e y desde un vector de enteros.

v.erase(remove_if(v.begin(),
   v.end(), [x, y](int n) { 
   return x < n && n < y; }),
   v.end());

La segunda línea muestra la expresión lambda. Corchetes, llamados iniciadores de lambda, indican la definición de una expresión lambda. Esta lambda toma un entero n como parámetro, y el objeto de función generado por lambda tiene los miembros de datos x e y. Compare eso con un objeto de función manuscrito para apreciar la conveniencia y el ahorro de tiempo que ofrecen las lambdas:

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 palabra clave auto siempre ha existido en C++, pero rara vez se usaba dado que no ofrecía ningún valor adicional. C++0x reasigna esta palabra clave para que determine automáticamente el tipo de variable desde su inicializador. Auto reduce el nivel de detalle y ayuda a que sobresalga el código importante. Evita errores de coincidencia de tipos y de truncamiento. También ayuda a hacer que el código sea más genérico, al permitir que se puedan escribir plantillas que se ocupen menos de los tipos de expresiones intermedias y que traten eficazmente con tipos no documentados como lambdas. Este código muestra la manera en que auto evita escribir el tipo de plantilla en la iteración del bucle for sobre un vector:

vector<int> v;
for (auto i = v.begin(); i != v.end(); ++i) {
// code 
}

Las referencias Rvalue son un nuevo tipo de referencia incluido en C++0x que ayuda a resolver el problema de la copia innecesaria y permite el desvío perfecto. Cuando el lado derecho de una asignación es una rvalue, el objeto del lado izquierdo puede robar recursos del objeto del lado derecho en lugar de realizar una asignación por separado, lo que permite la semántica de transferencia de recursos.

El desvío perfecto permite escribir una sola plantilla de función que tome n argumentos arbitrarios y los desvíe en forma transparente a otra función arbitraria. La naturaleza del argumento (modifiable, const, lvalue o rvalue) se conserva en este proceso de desvío.

template <typename T1, typename T2> void functionA(T1&& t1, T2&& t2) {
  functionB(std::forward<T1>(t1), std::forward<T2>(t2));
}

Una explicación detallada de las referencias rvalue se encuentra fuera del ámbito de este artículo, pero puede consultar la documentación de MSDN en msdn.microsoft.com/library/dd293668(VS.100) para obtener más información.

Static_assert permite probar aserciones en tiempo de compilación en lugar de en tiempo de ejecución. Permite desencadenar errores de compilador con mensajes de error personalizados que sean fáciles de leer. Static_assert es especialmente útil para validar parámetros de plantillas. Por ejemplo, la compilación del siguiente código devolverá el error “error 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 agrega seguridad de tipo a punteros null y está estrechamente relacionada con referencias rvalue. La macro NULL (definida como 0) y el literal 0 se usan comúnmente como el puntero null. Hasta ahora, eso no ha sido problema, pero no funcionan bien en C++0x debido a posibles problemas en el desvío perfecto. De esta forma, se ha incluido la palabra clave nullptr particularmente para evitar fallas misteriosas en funciones de desvío perfecto.

Nullptr es una constante de tipo nullptr_t, que puede convertirse a cualquier tipo de puntero, pero no a otros tipos como int o char. Además de su uso en funciones de desvío perfecto, nullptr se puede usar en cualquier lugar en que se haya usado la macro NULL como puntero null.

Sin embargo, se necesario tener precaución: el compilador sigue admitiendo NULL y aún no la ha remplazado por nullptr. Esto es principalmente para evitar romper el código existente debido al uso dominante y, con frecuencia, incorrecto de NULL. En el futuro, sin embargo, nullptr deberá usarse siempre que se haya usado NULL, y NULL se deberá tratar como una característica destinada a admitir compatibilidad con versiones anteriores.

Por último, decltype permite que el compilador infiera el tipo de retorno de una función basada en una expresión arbitraria y hace que el desvío perfecto sea más genérico. En versiones anteriores, para dos tipos arbitrarios T1 y T2, no había manera de deducir el tipo de expresión que usó estos dos tipos. La característica decltype permite establecer, por ejemplo, que una expresión que tiene argumentos de plantilla, como sum<T1, T2>(), tiene el tipo T1+T2.

Mejoras de la biblioteca estándar

Partes sustanciales de la biblioteca C++ estándar se han reescrito para aprovechar las nuevas características del lenguaje C++0x y aumentar el rendimiento. Además, se han incluido varios algoritmos nuevos.

La biblioteca estándar aprovecha plenamente las referencias rvalue para mejorar el rendimiento. Tipos tales como vector y list ahora tienen más constructores y operadores de asignación de transferencia propios. Las reasignaciones de vectores aprovechan la semántica de transferencia de recursos al elegir constructores de transferencia, de modo que si sus tipos tienen constructores de transferencia y operadores de asignación de transferencia, la biblioteca los elige automáticamente.

Ahora puede crear un puntero shared para un objeto al mismo tiempo que construye el objeto con la ayuda de la nueva plantilla de función C++0x make_shared<T>:

auto sp = 
  make_shared<map<string,vector>>
  (args);

En Visual Studio 2008, debería escribir lo siguiente para obtener la misma funcionalidad:

shared_ptr<map<string,vector>> 
  sp(new map<string,vector>(args));

El uso de make_shared<T> es más conveniente (tendrá que escribir el nombre de tipo menos veces), más sólido (evita la pérdida clásica no nombrada shared_ptr, dado que el puntero y el objeto se crean simultáneamente) y más eficiente (realiza una asignación de memoria dinámica en lugar de dos). 

La biblioteca ahora contiene un tipo de puntero inteligente nuevo y más seguro, unique_ptr (activado por las referencias rvalue). Como consecuencia, auto_ptr ha caído en desuso; unique_ptr evita los escollos de auto_ptr al ser más transferible, pero no se puede copiar. Permite implementar una semántica de propiedad estricta sin afectar la seguridad. También funciona bien con contenedores de Visual C++ 2010 compatibles con referencias rvalue.

Los contenedores ahora tienen nuevas funciones de miembros, cbegin y cend, que ofrecen una manera de usar const_iterator para inspección independientemente del tipo de contenedor:

vector<int> v;
 
for (auto i = v.cbegin(); i != v.cend(); ++i) {
  // i is vector<int>::const_iterator
}

Visual Studio 2010 agrega a la biblioteca estándar la mayoría de los algoritmos propuestos en diversas notas de C++0x. Un subconjunto de la biblioteca de conversiones Dinkumware ya está disponible en la biblioteca estándar, de modo que ahora puede realizar fácilmente conversiones como UTF-8 a UTF-16. La biblioteca estándar permite propagación de excepciones a través de exception_ptr. Se han realizado muchas actualizaciones en el encabezado <random>. Existe una lista vinculada individualmente llamada forward_list en esta versión. La biblioteca tiene un encabezado <system_error> para mejorar el diagnóstico. Además, muchas de las características TR1 que existían en el espacio de nombres std::tr1 en la versión anterior (como shared_ptr y regex) ahora son parte de la biblioteca estándar bajo el espacio de nombres std.

Mejoras de programación simultánea

Visual Studio 2010 presenta Parallel Computing Platform, que ayuda a escribir código paralelo de alto rendimiento rápidamente, a la vez que se evita errores sutiles de simultaneidad. Esto permite eludir algunos de los problemas clásicos relacionados con la simultaneidad.

Parallel Computing Platform tiene cuatro partes principales: runtime de simultaneidad (ConcRT), biblioteca de modelos de procesamiento paralelo (PPL), biblioteca de agentes asincrónicos y depuración y creación de perfiles paralelos.

ConcRT es la capa más baja de software que interactúa con el sistema operativo y arbitra entre varios componentes simultáneos que compiten por recursos. Debido a que es un proceso de modo de usuario, puede reclamar recursos cuando se usan sus mecanismos de bloqueo cooperativo. ConcRT es compatible con localidad y evita cambiar tareas entre diferentes procesadores. También utiliza programación en modo de usuario (UMS) de Windows 7, de modo que puede aumentar el rendimiento aunque no se use el mecanismo de bloqueo cooperativo.

La PPL proporciona los patrones para escribir código paralelo. Si un cálculo se puede descomponer en subcálculos que se pueden representar mediante funciones u objetos de función, cada uno de estos subcálculos se puede representar con una tarea. El concepto de tarea se encuentra mucho más cerca del dominio de problema, a diferencia de los subprocesos que se alejan del dominio de problema para dirigir su atención hacia el hardware, el sistema operativo, las secciones críticas, etc. Una tarea se puede ejecutar de manera simultánea con otras tareas, independientemente de lo que hagan las otras tareas. Por ejemplo, dos tareas diferentes pueden realizar la ordenación de dos mitades diferentes de una matriz simultáneamente.

La PPL incluye clases paralelas (task_handle, task_group y structured_task_group), algoritmos paralelos (parallel_invoke, parallel_for y parallel_for_each), contenedores paralelos (combinable, concurrent_queue y concurrent_vector) y primitivos de sincronización compatibles con ConcRT (critical_section, event y reader_writer_lock), todos los cuales tratan a las tareas como conceptos de primera clase. Todos los componentes de la PPL habitan en el espacio de nombres concurrency.

Los grupos de tareas permiten ejecutar un conjunto de tareas y esperar a que terminen. De esta forma, en el ejemplo de ordenación, las tareas que administran dos mitades de la matriz pueden realizar un grupo de tareas. Usted tiene la garantía de que estas dos tareas estarán terminadas al final de la función de llamada de miembro de espera, como se muestra en el ejemplo de código de una ordenación rápida recursiva escrita mediante tareas paralelas y lambdas:

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();
}

Esto se puede mejorar aun más mediante el uso de un grupo de tareas estructurado habilitado por el algoritmo parallel_invoke. Usa de dos a 10 objetos de función, los ejecuta todos en paralelo usando tantos núcleos como ofrezca ConcRT y espera a que terminen:

parallel_invoke(
  [=] { quicksort(first, mid1); },
  [=] { quicksort(mid2, last); } );

parallel_invoke(
  [=] { quicksort(first, mid1); },
  [=] { quicksort(mid2, last); } );

Puede haber varias subtareas creadas para cada una de estas tareas. ConcRT administra la asignación entre tareas y subprocesos de ejecución (y asegura que todos los núcleos se utilicen óptimamente). De esta forma, descomponer un cálculo en tantas tareas como sea posible ayuda a aprovechar todos los núcleos disponibles.

Otro algoritmo paralelo útil es parallel_for, que se puede usar para iterar sobre índices de manera simultánea:

parallel_for(first, last, functor);
parallel_for(first, last, step, functor);

Esto llama simultáneamente a objetos de función con cada índice, empezando por el primero y terminando en el último.

La biblioteca de agentes asincrónicos proporciona un modelo de programación basado en flujo de datos en que los cálculos dependen de los datos necesarios que se encuentran disponibles. La biblioteca se basa en los conceptos de agentes, los bloques de mensajes y las funciones de traspaso de mensajes. Un agente es un componente de una aplicación que realiza ciertos cálculos y se comunica de manera asincrónica con otros agentes para resolver un problema de cálculo mayor. Esta comunicación entre agentes se logra a través de funciones de traspaso de mensajes y bloques de mensajes.

Los agentes tienen un ciclo de vida observable que pasa por diversas etapas. No están destinados a usarse para el paralelismo específico que se logra mediante tareas de PPL. Los agentes se compilan en los componentes de programación y administración de recursos de ConcRT, y ayudan a evitar los problemas que surgen del uso de memoria compartida en aplicaciones simultáneas.

No es necesario vincular ningún componente adicional ni redistribuirlo para aprovechar estos patrones. ConcRT, PPL y la biblioteca de agentes asincrónicos se han implementado dentro de msvcr100.dll, msvcp100.dll y libcmt.lib/libcpmt.lib junto con la biblioteca estándar. La PPL y la biblioteca de agentes asincrónicos son principalmente implementaciones de sólo encabezado.

El depurador de Visual Studio ahora es compatible con ConcRT y facilita la depuración de problemas de simultaneidad, a diferencia de Visual Studio 2008, que no era compatible con conceptos paralelos de mayor nivel. Visual Studio 2010 posee un generador de perfiles de simultaneidad que permite visualizar el comportamiento de aplicaciones paralelas. El depurador posee nuevas ventanas que visualizan el estado de todas las tareas de una aplicación y sus pilas de llamadas. La figura 1 muestra las ventanas Parallel Tasks y Parallel Stacks.

Figura 1 Ventanas de depuración Parallel Stacks y Parallel Tasks
Figura 1 Ventanas de depuración Parallel Stacks y Parallel Tasks

IntelliSense y productividad de tiempo de diseño

En Visual Studio 2010 se incluye una infraestructura de IntelliSense y de exploración completamente nueva. Además de ayudar con la escala y la capacidad de respuesta en proyectos con grandes bases de códigos, las mejoras de infraestructura han habilitado algunas nuevas características de productividad de tiempo de diseño.

Características de IntelliSense como el informe de errores en vivo y las informaciones rápidas se basan en un nuevo front end de compilador, que analiza la unidad de traducción completa para ofrecer información enriquecida y precisa acerca de la semántica del código, aunque se modifiquen los archivos de código.

Todas las características de exploración de código, como la vista de clases y la jerarquía de clases, ahora usan información de código fuente almacenada en una base de datos de SQL que permite indización y tiene una superficie de memoria fija. A diferencia de versiones anteriores, el IDE de Visual Studio 2010 siempre responde y no es necesario esperar mientras las unidades de compilación se vuelven a analizar en respuesta a cambios en el archivo de encabezado.

El informe de errores en vivo de IntelliSense (los subrayados ondulados rojos conocidos) muestra sintaxis de calidad de compilador y errores de semántica durante la exploración y la edición de un código. Si se mantiene el mouse sobre el error, aparece el mensaje de error (consulte la figura 2). La ventana Error List también muestra el error desde el archivo que se está revisando actualmente, así como los errores de IntelliSense de otros lugares de la unidad de compilación. Toda esta información está a su disposición sin tener que hacer una compilación.

Figura 2 Informe de errores en vivo con errores de IntelliSense
Figura 2 Informe de errores en vivo con errores de IntelliSense

Además, una lista de archivos de include relevantes aparece en un menú desplegable mientras se escribe #include y la lista se refina a medida que usted escribe.

La nueva característica Navigate To (Edit | Navigate To o Ctrl+coma) ayuda a ser más productivo en la búsqueda de archivos o símbolos. Esta característica proporciona resultados de búsqueda en tiempo real, basados en subcadenas a medida que escribe, que hace coincidir sus cadenas de entrada con símbolos y archivos de cualquier proyecto (consulte la figura 3). Esta característica también funciona para archivos en C# y Visual Basic, y es extensible.

Figura 3 Uso de la característica Navigate To
Figura 3 Uso de la característica Navigate To

La jerarquía de llamadas (que se invoca mediante Ctrl+K, Ctrl+T o en el menú contextual) permite navegar a todas las funciones llamadas desde una determinada función y desde todas las funciones que realizan llamadas a una función en particular. Esta es una versión mejorada de la característica Explorador de llamadas que existía en versiones anteriores de Visual Studio. La ventana Call Hierarchy está mucho mejor organizada y ofrece árboles desde y hacia llamadas para cualquier función que aparezca en la misma ventana.

Tenga en cuenta que aunque todas las características de exploración de códigos están disponibles tanto para C++ como para C++/CLI, las características relacionadas con IntelliSense, tales como informe de errores en vivo e Información rápida, no estarán disponibles para C++/CLI en la versión final de Visual Studio 2010. 

En esta versión también se mejoran otras características del editor como herramienta necesaria. Por ejemplo, la popular característica Find All References que se usa para buscar referencias a elementos de código (clases, miembros de clase, funciones, etc.) dentro de toda la solución ahora es más flexible. Los resultados de la búsqueda se pueden refinar más mediante la opción Resolve Results del menú contextual.

El código inactivo ahora conserva su información semántica al mantener sus colores (en lugar de tornarse gris). La figura 4 muestra como el código inactivo se atenúa, pero sigue mostrando colores diferentes para expresar su información semántica.

Figura 4 Los bloques de código inactivo mantienen sus colores
Figura 4 Los bloques de código inactivo mantienen sus colores

Además de las características ya descritas, se mejoró la experiencia de editor general en Visual Studio 2010. El nuevo IDE basado en Windows Presentation Foundation (WPF) se ha rediseñado para eliminar confusiones y mejorar la legibilidad. Ventanas de documento, tales como el editor de código y la vista Design, ahora pueden flotar fuera de la ventana principal del IDE y pueden aparecer en varios monitores. Es más fácil realizar acercamientos o alejarse de la ventana de editor de código mediante la tecla Ctrl y la rueda del mouse. El IDE también ha mejorado su compatibilidad con la extensibilidad.

Sistemas de compilación y proyecto

Visual Studio 2010 también cuenta con mejoras sustanciales en el sistema de compilación y en el sistema de proyecto para proyectos C++.

El cambio más importante es que MSBuild ahora se usa para compilar proyectos C++. MSBuild es un motor de orquestación de compilación extensible y basado en XML, que se ha usado para proyectos C# y Visual Basic en versiones anteriores de Visual Studio. MSBuild es ahora el sistema de compilación común de Microsoft para todos los lenguajes. Se puede usar tanto en el laboratorio de compilación como en equipos de desarrollador individuales.

Los procesos de compilación en C++ ahora se definen en términos de archivos y tareas de destino de MSBuild y brindan un amplio grado de personalización, control y transparencia.

El tipo de proyecto C++ tiene una nueva extensión: .vcxproj. Visual Studio actualizará automáticamente antiguos archivos y soluciones .vcproj al nuevo formato. También existe una herramienta de línea de comandos, vcupgrade.exe, para actualizar proyectos individuales desde la línea de comandos.

En el pasado, sólo se podía usar el conjunto de herramientas (compilador, bibliotecas, etc.) proporcionado con la versión actual de Visual Studio. Era necesario que esperara hasta estar listo para migrar al nuevo conjunto de herramientas antes de poder comenzar a usar el nuevo IDE. Visual Studio 2010 resuelve este problema al permitir conjuntos de herramientas compatibles con múltiples versiones para compilación. Por ejemplo, puede admitir el compilador y las bibliotecas Visual C++ 9.0 mientras trabaja en Visual Studio 2010. La figura 5 muestra la configuración de compatibilidad con múltiples versiones (multi-targeting) nativa en la página de propiedades.

5 Conjuntos de herramientas de plataformas compatibles con múltiples versiones
Figura 5 Conjuntos de herramientas de plataformas compatibles con múltiples versiones

El uso de MSBuild hace que el sistema de compilación en C++ sea mucho más extensible. Cuando el sistema de compilación predeterminado no es suficiente para satisfacer sus necesidades, puede extenderlo agregando su propia herramienta u otro paso de compilación. MSBuild usa tareas como unidades reutilizables de código ejecutable para realizar las operaciones de compilación. Puede crear sus propias tareas y extender el sistema de compilación definiéndolos en un archivo XML. MSBuild genera las tareas a partir de estos archivos XML sobre la marcha.

Las plataformas y los conjuntos de herramientas existentes se pueden ampliar al agregar archivos .props y .targets, para pasos adicionales en las carpetas ImportBefore e ImportAfter. Esto es especialmente útil para proveedores de bibliotecas y herramientas que desean extender sus sistemas de compilación existentes. También puede definir su propio conjunto de herramientas de plataforma. Además, MSBuild ofrece mejor información de diagnóstico para facilitar la depuración de problemas de compilación, que también hacen más confiables las compilaciones incrementales. También puede crear sistemas de compilación que estén relacionados más estrechamente con control de código fuente y el laboratorio de compilación, y que dependan menos de la configuración del equipo del desarrollador.

El sistema de proyecto que se ubica encima del sistema de compilación también aprovecha la flexibilidad y extensibilidad proporcionadas por MSBuild. El sistema de proyecto entiende los procesos de MSBuild y permite que Visual Studio muestre en forma transparente información dispuesta por MSBuild.

Las personalizaciones son visibles y se pueden configurar a través de las páginas de propiedades. Puede configurar su sistema de proyecto para usar su propia plataforma (como las plataformas x86 o x64 existentes) en su propio depurador. Las páginas de propiedades permiten escribir e integrar componentes que actualizan dinámicamente el valor de las propiedades que dependen del contexto. El sistema de proyecto de Visual Studio 2010 incluso permite escribir su propia UI personalizada para leer y escribir propiedades en lugar de usar páginas de propiedades.

Compilación más rápida y mejor rendimiento

Además de las mejoras en la experiencia del tiempo de diseño descritas hasta ahora, Visual Studio 2010 también mejora la velocidad de la compilación, la calidad y el rendimiento de aplicaciones creadas con el compilador Visual C++, como resultado de las mejoras en la generación de códigos múltiples realizadas al back-end del compilador.

El rendimiento de ciertas aplicaciones depende del espacio de trabajo. El tamaño del código de la arquitectura x64 se ha reducido en un rango del 3% al 10%, a través de la realización de varias optimizaciones en esta versión, que llevó a una mejora de rendimiento de dichas aplicaciones.

La generación de código de Single Instruction Multiple Data (SIMD) (que es importante para desarrolladores de juegos, audio, vídeo y gráficos) se ha optimizado para un mejor rendimiento y una mejor calidad de código. Las mejoras incluyen la eliminación de dependencia falsas, vectorización de inicializaciones de vector constantes y mejor asignación de registros de XMM para eliminar las cargas, los almacenamientos y los traspasos redundantes. Además, se ha optimizado la familia intrinsic __mm_set_**, __mm_setr_** y __mm_set1_**.

Para un rendimiento mejorado, las aplicaciones se deben crear mediante generación de código en tiempo de enlace (LTCG) y Optimización guiada por perfiles (PGO).

La velocidad de compilación en plataformas x64 se ha mejorado realizando optimización en la generación de código de x64. La compilación con LTCG, recomendada para una mejor optimización, en general tarda más que la compilación sin LTCG, especialmente en aplicaciones grandes. En Visual Studio 2010, la compilación con LTCG se ha mejorado en hasta un 30%. En esta versión se ha incluido un subproceso dedicado para escribir archivos PDB, de modo que pueda haber mejoras de tiempo de vinculación al usar el conmutador /DEBUG.

Las ejecuciones de instrumentación de PGO se han acelerado al agregar compatibilidad con versiones sin bloqueo de binarios instrumentados. También existe una nueva opción POGO, PogoSafeMode, que permite especificar si se desea usar el modo seguro o el modo rápido en el momento de optimizar una aplicación. El modo rápido es el comportamiento predeterminado. El modo seguro es seguro para subprocesos, pero más lento que el modo rápido.

La calidad del código generado por compilador se ha mejorado. Ahora existe plena compatibilidad para Advanced Vector Extensions (AVX), que son muy importantes para aplicaciones con uso intensivo de puntos flotantes en procesadores AMD e Intel a través de opciones intrinsic y /arch:AVX. El cálculo de punto flotante es más preciso con la opción /fp:fast.

Creación de aplicaciones para Windows 7

Windows 7 presentó varias nuevas tecnologías y características fascinantes y agregó nuevas API, y Visual Studio 2010 ofrece acceso a todas las nuevas API de Windows. Los componentes del SDK de Windows necesarios para escribir código para API nativas de Windows se incluyen en Visual Studio 2010. Puede aprovechar innovaciones como Direct3D 11, DirectWrite, Direct2D y API de Servicios web de Windows usando los encabezados y bibliotecas de SDK disponibles en Visual Studio 2010.

Además de poner todas las API de Windows a disposición de los desarrolladores, esta versión de Visual Studio también le facilita escribir aplicaciones para Windows con la ayuda de un MFC reforzado. Se tiene acceso a funcionalidad sustancial de Windows 7 a través de las bibliotecas de MFC sin tener que escribir directamente en API nativas. Sus aplicaciones MFC existentes se iluminarán en Windows 7 con sólo recompilar. Y sus nuevas aplicaciones pueden aprovechar plenamente las nuevas características.

MFC ahora incluye integración mejorada con el shell de Windows. La integración de su aplicación con Windows Explorer ahora puede ser mucho mejor aprovechando los controladores de archivo para vista previa (Preview), miniaturas (Thumbnails) y búsqueda (Search) que se han agregado en esta versión. Estas características se proporcionan como opciones en MFC Application Wizard, como se muestra en la figura 6. MFC genera automáticamente el proyecto ATL DLL que implementa estos controladores.

Figura 6 MFC Application Wizard con opciones de controlador de archivos
Figura 6 MFC Application Wizard con opciones de controlador de archivos

Uno de los cambios más notables de interfaz en Windows 7 es la nueva barra de tareas. MFC permite aprovechar rápidamente características como jump lists, miniaturas en pestañas, vista previa de miniaturas, barras de progreso, superposición de iconos, etc. La figura 7 muestra vistas previas de miniaturas y miniaturas en pestañas para una aplicación MDI MFC.

Figura 7 Miniaturas en pestañas y vista previa de miniaturas en una aplicación MFC
Figura 7 Miniaturas en pestañas y vista previa de miniaturas en una aplicación MFC

La UI de cinta ahora también tiene una cinta al estilo de Windows 7 y su aplicación puede intercambiar la UI sobre la marcha en cualquier momento durante el desarrollo desde varias cintas al estilo de Office hasta la cinta de Windows 7 mediante el menú desplegable de estilos, como se muestra en la figura 8.

Figura 8 Menú desplegable de estilos de cinta en una aplicación MFC
Figura 8 Menú desplegable de estilos de cinta en una aplicación MFC

MFC permite que sus aplicaciones tengan compatibilidad con multitoque y llama mensajes correctos para que usted los administra cuando se produzcan diversos eventos de multitoque. Con sólo registrarse para eventos de toque y gesto, dichos eventos se enrutan para su aplicación. MFC también hace que las aplicaciones sean compatibles con alto PPP de manera predeterminada, para que se adapten a pantallas de alto PPP y no se vean pixeladas o difusas. MFC escala internamente y cambia fuentes y otros elementos para asegurarse de que la UI se siga viendo nítida en pantallas de alto PPP.

Además de las nuevas características de Windows 7, algunas otras características de Windows que han existido desde Windows Vista pero que no se incluyeron en versiones anteriores de MFC se han incluido ahora. Por ejemplo, el Administrador de reinicio es una característica útil incluida en Windows Vista que permite que una aplicación realice un guardado de aplicación antes de terminar. La aplicación puede invocar esta característica y luego restaurar su estado al reiniciar. Ahora puede aprovechar completamente el Administrador de reinicio en su aplicación MFC para administrar bloqueos y reinicios de manera más elegante. Sólo agregue una línea de código para habilitar el reinicio y la recuperación en su aplicación existente:

CMyApp::CMyApp() {
  m_dwRestartManagerSupportFlags = 
    AFX_RESTART_MANAGER_SUPPORT_RESTART;
// other lines of code ...
}

Las nuevas aplicaciones MFC obtienen esta funcionalidad automáticamente mediante MFC Application Wizard. El mecanismo de guardado automático está disponible para aplicaciones que guardan documentos y el intervalo de guardado automático puede definirlo en usuario. Las aplicaciones pueden elegir entre sólo soporte de reinicio o inicio de recuperación de la aplicación (aplicable a aplicaciones de tipo Doc/View) en MFC Application Wizard.

Otra adición es el diálogo de tarea de Windows, que es un tipo mejorado de cuadro de mensaje (consulte la figura 9). MFC ahora tiene un contenedor para el diálogo de tarea que puede usar en sus aplicaciones.

Figura 9 Diálogo de tarea
Figura 9 Diálogo de tarea

MFC Class Wizard está de vuelta

No sólo se ha agregado nueva funcionalidad en la biblioteca MFC, sino que esta versión también facilita trabajar con MFC en el IDE de Visual Studio. Una de las características que se pide más comúnmente, el MFC Class Wizard (Asistente para clases MFC, que se muestra en la figura 10), ha vuelto y está mejorado. Ahora puede agregar clases, controladores de eventos y otros elementos a la aplicación mediante MFC Class Wizard.

Figura 10 MFC Class Wizard
Figura 10 MFC Class Wizard

Otra adición es el diseñador de cinta, que permite diseñar gráficamente la UI de cinta (en lugar de definirla en código en Visual Studio 2008) y almacenarla como recurso XML. Este diseñador obviamente es útil para crear nuevas aplicaciones, pero aplicaciones existentes también pueden aprovechar el diseñador para actualizar sus UI. La definición de XML se puede crear simplemente agregando una línea de código en forma temporal a la definición de código existente de la UI de cinta:

m_wndRibbonBar.SaveToXMLFile(L"YourRibbon.mfcribbon-ms");

El archivo XML resultante se puede consumir como archivo de recursos y se pueden hacer más modificaciones mediante el diseñador de cinta.

Conclusión

Visual Studio 2010 es una versión importante en la evolución de Visual C++ y facilita de muchas formas la vida de los desarrolladores. Apenas he abordado superficialmente las diversas mejoras relacionadas con C++ en este artículo. Para un análisis más profundo de las diferentes características, consulte la documentación de MSDN y el blog del equipo de Visual C++ en blogs.msdn.com/vcblog, que también se usó como la base para algunas de las secciones de este artículo.   

Sumit Kumar es un administrador de programas del equipo del IDE de Visual C++. Tiene una maestría en informática de University of Texas en Dallas.

Gracias a los siguientes expertos técnicos: Stephan T. Lavavej, Marian Luparu y Tarek Madkour