Noticias Weblogs Foros Wiki Código
 

Un primer vistazo al C++/CLI

Por Nishant Sivakumar

Traducción al español de RFOG

Un breve vistazo a la nueva sintaxis del C++/CLI y cómo mejora la vieja del MC++.

Introducción

Cuando Microsoft presentó las Extensiones Administradas de C++ con VS.NET 7, los programadores de C++ las aceptaron con variadas reacciones. Mientras que mucha gente se mostró contenta de poder seguir utilizando C++, la gran mayoría se mostró insatisfecha con la sintaxis tan fea y distorsionada que definía al C++ Administrado. Obviamente, Microsoft se tomó la reacción con mucha seriedad y decidió que la sintaxis del MC++ no iba a triunfar mucho.

El 6 de octubre de 2003, ECMA anunció la creación de un nuevo grupo para supervisar el desarrollo de una serie de extensiones para enlazar el estándar ISO del lenguaje de programación C++ con la Infraestructura de Lenguajes Comunes (CLI), lo que llevó a que a estas extensiones fueran conocidas como el estándar C++/CLI, que será soportado por el compilador VC++ desde la salida de Whidbey (VS.NET 2005).

Problemas de la vieja sintaxis

  • Sintaxis fea y distorsionada. – Esos dobles subrayados no son precisamente agradables a la vista.
  • Soporte de segunda clase al CLI. – Comparado con C# y VB.NET, MC++ utiliza rodeos retorcidos para ajustarse al CLI, como, por ejemplo, la inexistencia de una estructura “for each” para enumerar las colecciones .NET.
  • Pobre integración entre C++ y .NET. – No se pueden mezclar características como plantillas de C++ sobre tipos de .NET, y tampoco se pueden usar elementos del CLI como el recolector de basura en los tipos de C++.
  • El uso de punteros es tendente a la confusión. Tanto los punteros de C++ nativos como los administrados utilizan la misma sintaxis basada en *, lo que resulta un tanto confuso, dado que los punteros gc son algo totalmente diferente a los nativos tanto en naturaleza como en comportamiento.
  • El compilador MC++ no puede producir código verificable.

¿Qué proporciona el C++/CLI?

  • Sintaxis y gramática elegantes. – Esto suministra una forma natural a los programadores de C++ para escribir código administrado y permite una transición suave desde el código no administrado al administrado. Esos feos subrayados dobles han desaparecido.
  • Soporte de primer nivel al CLI. – Características como propiedades, recolección de basura y genéricos se soportan directamente. Y lo que es más interesante, el C++/CLI permite el uso de estas caracteríscias dentro de las clases no manejadas nativas.
  • Soporte de primer nivel para el C++. – Elementos del C++ como plantillas y destructores determinísticos operan tanto en clases administradas como en las que no lo son. De hecho, el único lenguaje del .NET que “aparentemente” permite la creación de un tipo .NET en la pila o en el heap nativo es el C++/CLI.
  • Enlaza el .NET con el C++. Ahora, a los programadores de C++ no les parecerá estar pescando fuera del agua cuando trabajen con la BCL.
  • El ejecutable generado por el compilador de C++/CLI es completamente verificable.

“Hola, mundo”

using namespace System;
 
void _tmain()
{
    Console::WriteLine("Hello World");
}

Bueno, realmente no parece muy diferente de la vieja sintaxis, salvo que no es necesario realizar una referencia a mscorlib.dll, dado que el compilador de Whidbey lo referencia implícitamente siempre que se compile con /clr (lo que ahora implica por defecto /clr:newSyntax).

Handles

Una de las principales confusiones en la antigua sintaxis consistía en el uso del * tanto con punteros no administrados como con referencias administradas. En C++/CLI Microsoft introduce el concepto de manejadores (handles).

void _tmain()
{
    //El signo de puntuación ^ representa un handle
    String^ str = "Hello World";
    Console::WriteLine(str);
}

El signo de puntuación ^ (pronunciado como “tejado” –y reto a quien quiera a darle un nombre mejor. Nota del Traductor) representa un handle a un objeto manejado. De acuerdo con la especificación de CLI, un handle es una referencia a un objeto manejado. Los handles en la nueva sintaxis son los equivalentes a los punteros tipo __gc de la sintaxis MC++. Ahora, los handles ya no se confunden con los punteros y son asímismo de naturaleza totalmente diferente.

En qué se diferencian los handles de los punteros

  • Los punteros se indican mediante el signo *, mientras que los handles se denotan con el símbolo ^.
  • Los handles son referencias administradas a objetos situados en el montículo (heap) administrado, y los punteros simplemente apuntan a una dirección de memoria.
  • Los punteros son estables y los ciclos del GC no les afectan, mientras que los handles pueden apuntar a diferentes direcciones en base a las actuaciones del GC y a las compactaciones de memoria.
  • En el caso de los punteros, es el programador el que ha de liberar la memoria mediante delete, o sufrirá fugas de memoria.
  • Para los handles el uso de delete es opcional.
  • Los handles son tipos seguros mientras que los punteros definitivamente no lo son. No se puede moldear un handle a un void^.
  • De este modo, new devuelve un puntero, y gcnew un handle.

Instanciando objetos CLR

void _tmain()
{
    String^ str = gcnew String("Hello World");
    Object^ o1 = gcnew Object();
    Console::WriteLine(str);
}

La palabra reservada gcnew se utiliza para instanciar elementos CLR y devuelve un handle al objeto instanciado en el montículo CLR. La ventaja acerca de gcnew consiste en que permite diferenciar muy fácilmente entre las instancias administradas de las que no lo son.

Básicamente, lo único necesario para acceder a la BCL es la palabra reservada gcnew y el operador ^.

Declarando tipos

Los tipos CLR tienen un prefijo como adjetivo que describe la clase del tipo que es. Los siguientes son ejemplos de declaraciones en C++/CLI:

  • Tipos CLR
    • Tipos por referencia
      • ref class RefClass{…};
      • ref struct RefClass{…};
    • Tipos por valor
      • value class ValClass{…};
      • value struct ValClass{…};
    • Interfaces
      • interface class IType{…};
      • interface struct IType{…};
    • Enumeraciones
      • enum class Color{…};
      • enum struct Color{…};
  • Tipos nativos
    • class Native{…};
    • struct Native{…};
using namespace System;
 
interface class IDog
{
    void Bark();
};
 
ref class Dog : IDog
{
public:
    void Bark()
    {
        Console::WriteLine("Bow wow wow");
    }
};
 
void _tmain()
{
    Dog^ d = gcnew Dog();
    d->Bark();
}

Aquí, la sintaxis queda mucho más clara y fácil de ver que la antigua, en la que el código estaba constelado con palabras reservadas con doble subrayado como __gc e __interface.

Boxing/Unboxing

Boxing es ahora implícito (¡bien!) y seguro respecto a tipos. Se realiza una copia bit a bit al crear un nuevo Object en el montículo CLR. Unboxing también es implicito - simplemente hay que realizar un reinterpret_cast y después desreferenciar.

void _tmain()
{
    int z = 44;
    Object^ o = z; //boxing implícito
 
    int y = *reinterpret_cast<int^>(o); //unboxing
 
    Console::WriteLine("{0} {1} {2}",o,z,y);
 
    z = 66; 
    Console::WriteLine("{0} {1} {2}",o,z,y);
}
 
// Salida
// 44 44 44
// 44 66 44

El Object o es una copia boxed y no se refiere al tipo por valor int, lo que resulta obvio de la salida del segundo Console::WriteLine.

Cuando se encaja (box) un tipo por valor, el objeto devuelto mantiene el valor del tipo original.

void _tmain()
{
    int z = 44;
    float f = 33.567;
 
    Object^ o1 = z; 
    Object^ o2 = f; 
 
    Console::WriteLine(o1->GetType());
    Console::WriteLine(o2->GetType());    
}
 
// Salida
// System.Int32
// System.Single

Por lo tanto, no se puede intentar desencajar (unbox) a un tipo diferente.

void _tmain()
{
    int z = 44;
    float f = 33.567;
 
    Object^ o1 = z; 
    Object^ o2 = f;
 
    int y = *reinterpret_cast<int^>(o2);//System.InvalidCastException
    float g = *reinterpret_cast<float^>(o1);//System.InvalidCastException
}

Si se intenta, se obtiene una excepción del tipo System.InvalidCastException, ¡lo que nos demuestra la perfección en lo que respecta a la seguridad de tipos! Si se mira el código IL generado, se podrá observar a la instrucción MSIL box en acción. Por ejemplo:

void Box2()
{
    float y=45;
    Object^ o1 = y;
}

compila a:

.maxstack  1
.locals (float32 V_0, object V_1)

  ldnull
  stloc.1
  ldc.r4     45.
  stloc.0
  ldloc.0
  box   [mscorlib]System.Single
  stloc.1
  ret

De acuerdo con la documentación sobre MSIL, “La instrucción box convierte el valueType bruto (un valor unboxed) en una instancia del tipo Object (del tipo O). Esto viene acompañado por la creación de un objeto nuevo y la copia de los datos de valueType al nuevo objeto asignado.”

Siguientes lecturas (en inglés)

Conclusión

Bueno, ¿quién querría utilizar el C++/CLI si puede usar C#, J# y ese VB como se llame para escribir código .NET? Aquí hay cuatro razones que di en el DevCon 2003 de Trivandrum (Dic 2003).

  • Compilar código C++ existente a IL (la magia de /clr).
  • Destrucción determinística.
  • Soporte para interop nativo que aventaja al que cualquier otro lenguaje CLI pueda ofrecer.
  • Todos esos subrayados han desaparecido ;-)

Acerca de Nishant Sivakumar

Editor Site Builder

Nish es un tipo simpático que reside en Trivandrum, India, y que ha estado programando desde 1990, cuando tenía 13 años. Ha sido evangelista de varias tecnologías de Microsoft, como VC++, C++/CLI y el .NET Framework, por lo que está muy sorprendido de que Microsoft no le haya ofrecido el puesto de Jefe Evangelista en Redmond.

Trabaja para The Code Project y mantiene los productos de Dundas Ultimate Toolbox, Ultimate Grid y Ultimate TCP/IP que únicamente se venden en la tienda online de The Code Project. Suele frecuentar los foros de discusión de CP cuando no está programando, leyendo o escribiendo. Le gustaría visitar al menos tres docenas de países antes de que su mecanismo biológico humano deje de funcionar (eufemismo para evitar el uso de la palabra m.), y lamenta no haber visto la nieve hasta ahora. Y hay que mencionar que Nish no es muy dado a hablar sobre sí mismo.

Nish ha sido Microsoft Visual C++ MVP desde octubre de 2002. Mantiene un site sobre trucos y curiosidades de MVP en www.voidnish.com, donde se puede encontrar la lista consolidada de sus artículos, escritos e ideas sobre VC++, MFC, .NET y C++/CLI. Oh, también podrías querer echarle un vistazo a su blog sobre C++/CLI, MFC, .NET y otras muchas cosas en blog.voidnish.com

A Nish le encanta leer ciencia ficción, P. G. Wodehouse y Agatha Christie, y se imagina que es un escritor decente. Es autor de una comedia romántica, Summer Love and Some more Cricket y de un libro sobre programación, Extending MFC applications with the .NET Framework.

Pulsa aquí para ver el perfil público de Nishant Sivakumar.

RFOG

El traductor es un programador de hardware embebido, le gusta la ciencia ficción (aunque lee todo lo que le cae en las manos, hasta la lista de componentes del gel de baño mientras se ducha ¡sin gafas!), y en sus ratos libres se dedica a hacer cosas como ésta, algún que otro programa Open Source para Windows como zxFortune, responder en los foros de las news y a poner entradas en su blog.

 
traducart/a_first_look_at_c_cli.txt · Última modificación: 2008/06/18 10:23 (editor externo)
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki