MonoTouch: muy bonito pero una guarrería

Ayer estuve jugando un poco con MonoTouch, más que nada por curiosidad. Para los que no lo sepáis, es un IDE más una biblioteca que envuelve de forma conveniente todo el desarrollo para iOS, tanto iPhone como iPad y que permite a un programador de C# desarrollar para estas plataformas sin tener que aprender nada de Objective-C y demás zarandajas.

Es un decir, porque realmente no es más que un envoltorio fino a todo eso. Por lo tanto, si estás desarrollando para Windows o Silverlight, no esperes sentarte y ponerte a ello porque aparte de usar la sintaxis de C#, todo lo demás es iOS. Incluso se usa el IDE de xCode para crear los elementos gráficos y su interactuación, como son los outlets (propiedades) y las actions (los eventos).

La ventaja es que al ser una capa fina apenas introduce sobrecarga y todo es nativo. El inconvenientes es que tienes que aprenderte todas las idiosincrasias del iOS.

Y otra cosa que no me creo mucho es el tema de la compartición de código. No esperes tener el mismo código fuente de tu aplicación y compilar para Android, iOS, OS X y Windows. Podrás compartir bloques de código, pero nada más.

***

Otra cosa es que estas herramientas son de pago. Desde 400$ la más barata hasta viarios miles de dólares. Depende de qué quieras y cómo. Las versiones gratuitas para Android e iOS no caducan pero sólo permiten ejecutar código en el simulador.

Las versiones para escritorio son completamente gratuitas.

Se me olvidaba decir 

***

Ahora bien, como decidas desinstalar algo… arreglado vas. Aparte de que no hay desinstalador, los procesos para hacerlo son completamente insuficientes y te dejan el sistema hecho unos zorros con miles de archivos desperdigados por todo el disco. 

Versiones anteriores traían un desinstalador embebido dentro del PKG que realizaba la instalación, pero al menos la que yo he instalado no lo trae.

Primero ejecuta los comandos descritos aquí. Eso te va a limpiar digamos que las partes públicas del entorno. Y si tienes la última versión de xCode instalada, te puedes cargar la carpeta /Developer de un plumazo sin problemas porque sólo tendrás el Mono de los cojones en ella. Pero antes de hacerlo, míralo por si acaso.

Una vez pasado ese script, si te pica la curiosidad, haz una búsqueda global con la palabra “mono”.

En mi caso he usado CleanMyMac para limpiar el ordenador de basura, pero tu puedes hacerlo a mano o con otra herramienta. Ojo con los ficheros que no son del mono.

Finalmente tenemos que desinstalar MonoDevelop. En mi caso, de nuevo con CleanMyMac.

Y creo, sólo creo, que tengo limpio de nuevo el sistema.

Hay que joderse.

C++/CX (IV). Clases parciales

Éramos pocos y parió la abuela. ¿Sabéis por qué C++/CLI (el C++ de .NET) se quedó en la cuneta en eso de ser a first class .NET language (o en cristiano: un lenguaje .NET de primera clase)? Efectivamente, la ausencia de clases parciales.

Cuando Microsoft introdujo el .NET 3.0 también cambió la forma de entender la interacción con la parte visual. Si bien antes las ventanas se construían con código, aunque de forma más o menos automatizada gracias al diseñador visual (hablamos de Windows Forms), a partir de ese momento se implementó una nueva forma que, pese a ser una idea cojonuda, peca un poco de mal implementada, como casi todo lo que hace la casa.

Los viejos lobos de mar nos hemos defendido con las plantillas de cuadro de diálogo, que definían los tales mediante una serie de palabras claves. Luego Windows se encargaba de leer la versión binaria de ese texto y nos construía nuestro cuadro de diálogo.

Pues bien, XAML es la enésima reencarnación de lo mismo, pero con esteroides. Y digo enésima porque por ejemplo Delphi, C++ Builder e incluso el ya vetusto VB6 lo hacían más o menos así.

XAML es un lenguaje (lo que me cuesta llamarlo así) XML que define la estructura de una ventana y todos sus componentes. La idea es tener algo que nos defina por completo, y de forma independiente del código, la parte visual de un programa y que encima se pueda crear tanto de forma manual como con programas… Eso sí, léete tu un fichero XAML escrito con Blend, que lo vas a flipar en colores.

Bueno, a lo que vamos, XAML tiene que poder interactuar de alguna forma con el código. Para evitarnos cosas como MFC, Microsoft permitió que se pudiera definir una misma clase en más de un fichero. Y nacieron las clase parciales. 

La idea es bien sencilla. XAML conoce una parte de la clase que define la ventana, y el programador conoce otra. Luego el compilador las une y hemos conseguido nuestro objetivo.

Pues bien, C++/CX implementa clases parciales de forma casi idéntica a C#. Es la única forma de que C++ pueda entenderse con XAML sin hacer virguerías todavía peores. 

En resumen:

No está definido en el estándar de C++11.

Sólo funciona para ref class. O en otras palabras: no vale para código C++ clásico ni para la parte clásica de una aplicación Metro escrita en C++/CX.

Se añade una nueva palabra reservada, partial, que debe ir en todas las declaraciones menos en una. 

Tomándolo del artículo en inglés en el que me baso, os pego un ejemplo:

 

// foo.private.h

#pragma once

 

partial ref class foo // <- here the partial keyword is used

{

private:

   int _id;

   Platform::String^ _name;

};

// foo.public.h

#pragma once

#include «foo.private.h»

 

ref class foo // <- partial keyword is not used here

{

public:

   int GetId();

   Platform::String^ GetName();

};

// foo.cpp

#include «pch.h»

#include «foo.public.h»

 

int foo::GetId() {return _id;}

Platform::String^ foo::GetName {return _name;}

 

Es decir, cuando declaremos una clase en varios ficheros, todos ellos deben llevar la palabra reservada partial excepto uno de ellos, que hará de concentrador. Supongo que esto facilita la tarea del compilador, que irá anotando las partes parciales y las irá añadiendo poco a poco a la otra de forma dinámica y conforme se vaya encontrando el código. Y finalmente, en uno o más CPP definimos los métodos y demás. Aquí ya no hace falta para nada la palabra partial porque eso ya lo podíamos hacer antes sin problema alguno.

Esto nos lleva al dibujo siguiente, tomado también del artículo citado:

 

xaml_cpp

 

Eso es lo que hace Visual C++ a la hora de crear una ventana, generando estos cinco ficheros. El XAML contiene la definición de la ventana y todo lo que pongamos dentro, y es idéntico a uno generado en C#. También genera otros dos con los nombres terminados en .g.h y en .g.cpp, que contienen código necesario para que el sistema pueda enlazar el archivo XAML con el código fuente que hayamos podido editar, que a su vez reside en otros dos sendos ficheros con las extensiones de .xaml.h y .xaml.cpp. 

Nuestro código debe ir en los ficheros terminados en xaml (para la definición de la parte visual), xaml.h (para la declaración de la clase que representa la ventana) y xaml.cpp para todo lo demás.

Debemos ser muy cuidadosos en no tocar los otros dos ficheros, que suelen ser generados de forma automática al vuelo por Visual Studio (y supongo que por Blend, pero eso no lo he probado todavía) y que, si hacemos algo mal, podemos tirar abajo el diseñador y luego la aplicación no se cargará bien. Digamos que dichos ficheros son los equivalentes al InitializeComponents() de WindowsForms y que, si los tocamos sin conocimiento de causa, podemos dejar inservible nuestra ventana.

Os dejo que juguéis con lo descubierto. Creaos una aplicación vacía, ponedle un botón o lo que querás y añadidle un evento.

 

 

 

 

C++/CX (III). Objetos COM

Decíamos ayer y antes de ayer que para crear aplicaciones Metro en Windows 8 podíamos usar, o bien el .NET Framework 4.5 con C# y VB.NET o bien hacerlo con el tradicional C++. Ya conocemos que Microsoft se ha dado cuenta de que la cosa manejada no deja de ser un poco juguete comparada con la nativa, y que hay muchísimas empresas que siguen, no solo con C++, sino incluso con MFC.

Independientemente de eso, lo cierto es que el API para Metro no es más que una variación de Win32, empaquetada y ofrecida mediante objetos COM nativos. La idea es cambiar Win32 y prohibirla para Metro, ofreciendo su equivalente mediante la biblioteca WRL que no es más que un encapsulado exportado mediante COM.

Aquí parece como si Microsoft matara dos pájaros de un tiro, ya que .NET utiliza COM internamente, o lo más parecido y cercano a ello. Por lo tanto, lo exportado mediante la WRL es tomado, por un lado por .NET y por el otro por C++ con su extensión CX.

Tenemos que decir que C++/CX es código nativo. Sin recolector de basura. Sin máquina virtual.  Compilado para un procesador específico. No es más que nuestro código escrito en C++ que utiliza objetos COM envueltos en una serie de clases al estilo de .NET. De hecho, la sintaxis de C++/CX es la misma que la de C++/CLI, por lo que en un principio puede llevar a confusión.

De todos modos, para aquellos que hayan leído el Rationale de Sutter (versión en español de Octavio Hernández y del que suscribe aquí), sabrán que es la única otra sintaxis posible y, o bien escribimos en C++/CLI para el .NET clásico (léase Windows Forms) o bien lo hacemos en C++/CX para Metro. Y si bien podemos hacer ambas cosas para Windows 8, las aplicaciones C++/CLI serán las tradicionales, y las C++/CX las Metro.

Con esto queremos decir que, usando C++/CX, tenemos acceso a toda la parafernalia nativa de C++ como la STL, Boost, C++11… y encima desarrollar para Metro. O dicho de otro modo: cualquier código que tengamos que no haga uso de ventanas ni de Win32 de forma directa no servirá para las aplicaciones Metro.

***

La parte CX se activa con la opción /ZW del compilador, y lo que hace es mapear los objetos COM en clases normales y corrientes. Allí donde antes teníamos que enfangarnos con IInspectable, y usar toda la parafernalia COM, ahora simplemente tenemos que usar una ref class normal y corriente con sus constructores, destructores, propiedades, delegados y métodos que recibirán los tipos de datos más estándar y que devolverán también valores estándar.

O en otras palabras: las clases que nos ofrece el API de Metro (por llamarlo de alguna manera, ya que es el API de WRL) son envoltorios a objetos COM WRL. Y de igual forma que los programadores de Windows han construido la WRL, nosotros también podremos tener objetos del mismo tipo, hechos por nosotros y ofrecidos a terceros. 

Supongamos que tenemos una clase cualquiera con un método llamado Compute que toma dos enteros y devuelve un valor de estado. Para nosotros, que usamos dicha clase, tan sólo tenemos que llamar al método y esperar el resultado.

Pero internamente, lo que está haciendo el sugar syntax de la parte CX del compilador, es algo así:

 

//Parte del “consumidor” de la case
inline int Computer::Compute(int first, int second) {
    int result;
    HRESULT hr = ___impl_Compute(this, first, second, &result);
    if (hr != 0) ___impl_throw_for_hr(hr);
    return result;

 

Y luego, el método __impl_Compute, que está en el lado del componente (si fuera una clase WRL estaría dentro de la WRL), sería algo así:

 

HRESULT __stdcall ___impl_Compute(Computer* cmp, 

  int first, int second, int* result) { 

    try { *result = cmp->Compute(first, second); } 

    catch(Platform::Exception^ e) { return e->HResult; } 

    return S_OK; 

}

 

De este modo vemos cómo podemos acceder a un método sin toda la parafernalia ni las ofuscaciones de COM. Y no queremos entrar en detalles sobre por qué COM es tan así, mayormente porque no tenemos ni repajolera idea.

No obstante, si así lo queremos por detalles de la implementación o simplemente por ser guays, tenemos acceso a la parte interna de WRL de la siguiente manera:

 

//Assuming 32-bit pointers 

Computer^ computer = ref new Computer; 

int* vtable_array = (int*)computer; 

int* icomputer_vtable = (int*)vtable_array[0]; 

int* compute_will_be_fptr = (int*)icomputer_vtable[6]; 

typedef HRESULT (__stdcall *compute_fptr_t)(Computer*, 

  int, int, int*); 

compute_fptr_t compute_fptr = (compute_fptr_t)compute_will_be_fptr; 

//…use compute_fptr freely 🙂

 

No voy a comentar nada de aquí porque simplemente me parece algo demasiado barroco y supongo que sólo será útil si te encuentras con algún bug en la parte CX del compilador… También podemos acceder a la interfaz COM pura a través del método RoActivateInstance, pero lo dejamos para los hardcore.

***

Finalmente hay una opción del compilador no documentada para que éste nos muestre una estructura detallada del componente WinRT que especifiquemos: 

 

/d1reportSingleClassLayout<CLASSNAME>

 

Y de nuevo dejamos la opción para los aventureros, que yo ya me he cansado de escribir por hoy.

 

Menuda porquería de gestión de memoria que tiene el OS X

A veces, cuando uno es un swicher de pro como yo, se encuentra con cosas que le resultan un tanto kafkianas como la que os voy a contar.

¿Tiene alguien queja de la gestión de memoria de Windows? No me refiero a si consume más o menos, sino a si funciona como debe, sin que haga cosas raras. ¿A que no? Pues en OS X (los MAC para los despistados), dicha gestión deja mucho que desear.

Hace tiempo empecé a leer en los blogs fanboys la existencia de ciertas aplicaciones que liberaban y optimizaban la memoria de los MAC. En principio algo similar a los supuestos optimizadores de memoria en Windows, pero para OS X. Y si en Windows no hacen falta y sólo consumen recursos, en OS X debería ser lo mismo o, en opinión de los expertos, todavía más inútiles que en la plataforma de Microsoft.

Yo fui uno de esos que comenzó a defender a capa y espada la gestión de memoria del OS X sin haber hecho ninguna prueba porque suponía que era una cosa que se caía por su propio peso: si un sistema operativo es incapaz de gestionar bien su memoria, poco útil va a ser…

Y de hecho, igual que pasó en su momento cuando se promocionaban ese tipo de aplicaciones para Windows, con artículos pagados y demás, se armaron en los citados blogs unas buenas trifulcas sobre el tema, con razonamientos a favor y en contra más o menos elaborados y veraces…

Una puntualización. En OS X, cuando tu cierras una aplicación, está no se cierra realmente, sino que se suspende y queda residente en memoria. Es lo que ocurre cuando le das al aspa de cerrar, o cuando presionas Command-W. Si realmente quieres retirarla de la memoria, o cerrarla ala Windows, tienes que presionar Command-Q o hacerlo a mano desde el Dock. Aquí hablamos de cerrarla realmente, que es cuando se debería liberar su memoria. No obstante, hay programas que realmente se cierran ellos solos, como el gestor de libros Calibre.

Por lo tanto, en OS X tenemos dos formas de cerrar una aplicación. En la primera realmente no se está cerrando, sino que se está ocultando y no es aplicable lo que estamos hablando aquí. Sería el equivalente de minimizar en Windows.

En OS X, la memoria se va asignando pero no liberando. Es decir, tu abres un programa, lo usas y luego lo cierras del todo. La memoria asignada no se libera, sino que queda asignada por si se vuelve a abrir la aplicación y se aprovecha la huella de memoria ya existente.

Por lo tanto, conforme vas abriendo y cerrando aplicaciones la memoria se va llenando poco a poco hasta que apenas queda libre. Hasta aquí la cosa está bien, porque de este modo mantenemos una caché que podría resultar en un aumento de rendimiento.

Y a veces es así. La segunda vez que abres ciertos programas, estos se cargan mucho más deprisa porque recuperan su imagen de la memoria física. Por ejemplo, en el caso del Opera con varios miles de mensajes de correos y de news, la diferencia es abismal. De tardar como treinta segundos o más a ser completamente instantánea.

Según la teoría, una vez que se ha ocupado toda la memoria y se necesita más, es esa caché la que se va liberando según un algoritmo que no me he preocupado en mirar. 

Eso es la teoría. La práctica es otra: se tira de swap y el rendimiento de tu MAC empieza a caer en picado y oyes cómo empiezan a rascar los discos duros. Y nadie puede negármelo porque lo he comprobado yo mismo esta mañana.

Todo ha comenzado pasándome del iMAC al Air la biblioteca del iTunes. Para ello he iniciado el AirDrop y con ambos iTunes cerrados, lo he copiado. En el Air estaba vigilando el uso de procesador del Sparrow, que me tiene mosca, así que he abierto el monitor de actividad en el iMAC. 

La primera en la frente: de los 12GB de RAM que tiene el iMAC, he visto cómo conforme si iban pasando los ficheros de uno a otro se iba quedando en cero libres. Una vez ha terminado de copiar, he abierto varios programas como el Word, el Pages y el ordenador ha comenzado a tirar de swap en lugar de liberar la memoria inactiva. Los he cerrado del todo y no ha decrecido el consumo. 

Para completar el ejercicio he lanzado una máquina virtual que consume 2GB de memoria y ni aun así se ha liberado lo ocupado, sino que los discos han empezado a rascar todavía más furiosamente y el rendimiento ha caído en picado…

Tras eso me he ido a la App Store y me he bajado un liberador de RAM. Al poco de ejecutarlo y lanzar la limpieza de la misma, mano de santo, oye. Los discos han dejado de rascar y me ha dejado algo más de 8GB de RAM libres, que han subido a 10 cuando lo he vuelto a ejecutar tras cerrar la máquina virtual.

El problema, evidentemente, no está en el consumo de RAM. Por mi como si se ocupa toda, sino en el hecho de que un consumo real de 2GB del sistema operativo más otros 2 GB de la máquina virtual, han tirado abajo el rendimiento de un iMAC con 12GB de RAM y un i7 con 8 cores…

Pensaba que Apple podía hacerlo mejor, pero al final resulta que son unos chapuceros de mucho cuidado…

Por si os interesa, he probado el FreeMemory y tras comprobar que funcionaba bien, he comprado la versión Pro por tres cuartos de euro.

ExFAT 1, XBOX 0, Microsoft -infinito

Esta tarde nos hemos puesto a ver una película en la XBOX… lo que ha supuesto toda una odisea. Me explico.

Enciendo la consola y hala, actualización. No suelo encenderla muy a menudo, pero joder, que pase como con los PC… Pero el tema no es que haya sido una, sino que han sido dos, la segunda de ellas muy muy gorda. Tanto, que supongo que se ha actualizado toda entera.

La primera en la frente: no he podido usar la consola cuando he querido, sino que me he tenido que esperar más de media hora. Vale, podría haber decidido actualizarla luego, pero cualquiera se fía de un equipo que se conecta a internet con la que está cayendo…

Vale. Entro ya en el interfaz y meto un USB normal y corriente de 16GB… No lo detecta. Lo saco y meto un segundo de 4 GB, previa copia de lo que íbamos a ver. Tampoco lo coge. De 1 GB. Ni con esas.

Vale, estaban en ExFAT y la consola ¡¡no soporta los propios formatos de Microsoft!! Está bien que no lo haga cuando salió el sistema de ficheros, pero éste lleva ya varios años en el mercado.

Entiendo que no lea NTFS, ni HPFS, ni Ext2, ni ReiserFS… Es razonable a más no poder, ¡pero que un producto de Microsoft sea incapaz de leer sus propios formatos!…

Porque entendamos que si bien NTFS es un sistema “enterprise”, ExFAT es comercial y pretende ser la sustitución del veterano FAT… ¡Joder, si hasta los MAC los leen!

Y lo peor no es eso, lo peor es que una vez que ha sido tocado por la XBOX, ese USB deja de poder aceptar nuevo contenido. Es decir, que sólo deja ver y borrar ficheros, pero no añadir.

***

Al final, mientras reformateaba el disco a FAT32, y copiaba los archivos, me dio por navegar un rato con la consola…

Ahora se pueden instalar aplicaciones. Vale. Me instalo el visor de Youtube, por hacer algo. Se instala sin problemas.

Lo lanzo segundos después ¡y me dice que tiene que actualizarse! Hay que joderse, amigo Sancho… ¡¡Pero si está recién bajado de la tienda!!

En fin, se actualiza, entro y… necesito una cuenta Gold. La mía es la normal, sin pago…

¿Por qué cojones no me dice que es necesaria una cuenta Gold antes de instalar? Pues si piensan que tras la instalación voy a pasar por caja van arreglados.

Por seguir probando (ya que todavía no se habían copiado los ficheros en el disco USB), instalo la aplicación de DailyMotion. Esta al menos me avisa de que habrá cosas del interfaz no soportadas en la consola, pero cuando lo lanzo e intento ver un vídeo… me vuelve a pedir la cuenta Gold.

***

Vamos, lo dicho, un gigantesco despropósito de cosas que me resultan completamente incomprensibles… No sé quiénes tomarán las decisiones, pero seguro que serían más útiles cavando bancales de ajos que decidiendo qué va y qué no va en los productos de Microsoft. 

Y no hablo de los problemas de conexión que tengo con el Media Center (de Microsoft) y el NAS (este no es de MS), aunque el último suele ir sensiblemente mejor que el primero.

***

Básicamente, que les den.

Culico que veo cubico que deseo

Creo que ya tengo un post con el mismo título por algún lado, pero no puedo resistirme a comentar algunas cosas sobre los últimos movimientos de Microsoft.

Hay cosas de las que de momento no puedo hablar, pero hay otras que sí, y la verdad es que resultan un poco risibles porque, al menos desde mi punto de vista, son la respuesta incorrecta a una competencia mal entendida.

Os comento.

Apple, de vez en cuando, dice cosas como que el Flash es malo y sus productos no lo llevan, o que la disquetera está obsoleta, o que simplemente se acabaron las instalaciones con unidad de disco…

Y eso primero genera polémica, y luego resulta que tenían razón…

Pues bien, Microsoft quiere hacer lo mismo, pero como ya sabemos dónde tienen el estilo, la cosa termina, seguro, en unas risas y en una l,vuelta atrás.

Por un lado, mientras todos los fabricantes de móviles hacen cada vez equipos más potentes y con más funcionalidades dentro de su sistema operativo, mientras que mantienen otras restricciones más o menos incomprensibles para los expertos, Microsoft ha decidido hacer lo mismo… pero a su manera.

En general, un producto de Microsoft siempre ha ido creciendo tanto en vertical (más necesidad de hardware) como en horizontal (un API más rico y más completo). Esto ha llevado siempre a que la siguiente versión ha necesitado más memoria y más procesador para funcionar bien. En la parte de las APIs, han preferido enmarranar y convertir algo existente en más complejo en lugar de hacer borrón y cuenta nueva o simplemente añadir un API nuevo con más funcionalidad y dejar que el anterior muera…

Tampoco vamos a hablar del hecho de que, cuando han conseguido una tecnología madura y estable, en lugar de mantenerla y ampliarla, han decidido reinventar la rueda y vuelta a empezar: nuevo de todo, nuevos bugs, etc…

Pues bien, como decía, en un intento de paralelizarse a Apple, en lugar de cambiar para bien, lo hacen para mal. Hablo ahora de Windows Phone de 256 megas de RAM, que enmierdan todavía más ya la mierdosa API de ese sistema operativo. Quitan cosas útiles y añaden nuevas inútiles (y ya hablaré de eso en otro momento). Básicamente quieren copiar, pero copian mal.

***

Apple retiró su Media Center por los motivos que fueran. Microsoft lo retira en su Windows 8. Nada, hay que hacer lo mismo.

***

Y ahora llega el colmo de los colmos. Windows 8 no podrá reproducir DVD sin programas externos porque dicen que el uso del DVD está rápido decline en los ordenadores…

En fin, lo dicho, copia, pero copia bien, joder, y no vayas haciendo este tipo de tonterías…

Ocultación de datos y paso de variables

Leo en Twitter un par de preguntas más que curiosas sobre dos cosas que se dan por supuestas en el desarrollo orientado a objetos. Aunque una de ellas está formulada al revés, yo aquí le voy a dar el sentido correcto, e intentaré responder, en la medida de mis conocimientos, a ambas.

***

La primera de ellas es “¿por qué las variables de una clase no deben ser públicas?”

Es una de las primeras cosas que nos enseñan en la POO. Extendiendo la pregunta al ámbito completo, el concepto se conoce como “ocultación de datos”, y se extiende no sólo a las variables de clase, sino también a los objetos en sí y en general al programa completo.

Por poner un caso extremo, en la mayoría de Frameworks serios, el punto de entrada de un programa cualquiera debería ser:

 

MiApp theApp;

theApp.Run();

 

Vale, es un poco extremo, pero a ese nivel es lo que necesitamos: una aplicación, y que la vamos a ejecutar. No nos hace falta nada más ni tenemos que conocer nada más a ese nivel.

Es decir, uno de los motivos de la ocultación de datos es dar a conocer únicamente lo estrictamente necesario, y de hecho no nos importa si theApp tiene una variable llamada Juanito o Pepito porque no tiene utilidad conocerlo a ese nivel.

Y de hecho no nos interesa que nadie sepa la estructura interna de MiApp porque sencillamente ahí no podemos y no debemos hacer nada más que ejecutar el objeto que es la representación en memoria de nuestra aplicación. Quizás, o bien en el constructor o bien en el método Run(), pasar los argumentos con los que ha sido llamada la aplicación, pero sólo si es pertinente y nos interesa.

La regla nos dice que si no lo necesitamos a ese nivel, no lo hagamos público. Pero claro, todavía no he explicado por qué. Simplemente he definido la regla: oculta todo lo que puedas.

Jorge Serrano, respondiendo en Twitter, lo ha dicho bastante claro: No te interesa que nadie te pueda cambiar los zapatos sin tu consentimiento. Yo diría que cada perro debe lamerse su pijo, expresión más castiza. Imagina que vas andando por el monte, y alguien te cambia tus botas de trekking por unas chanclas de playa sin tu consentimiento… O que estás en una reunión de alto copete y una chavala empieza a esto…, lamerte el tema…

Reformulado en código, y retrocediendo un poco, la idea es bastante interesante.

En la programación procedural clásica (es decir, C y similares), y tal y como lo dijo Stroustrup en no recuerdo qué lugar, un programa es un conjunto de funciones que menean un conjunto de datos.

En teoría, cualquier función podría tocar cualquier dato. Digo en teoría porque en C y similares también hay métodos, más primitivos, para ocultar datos. Quien haya desarrollado aplicaciones más o menos grandes con esta filosofía, se habrá encontrado con serios problemas:

  • Métodos que cambian datos que no deberían cambiar.
  • Datos que deberían ser diferentes pero que por un despiste tienen el mismo nombre y que son combinados por el enlazador como uno solo.

Poniéndolo en otras palabras: se pueden dar situaciones en las que la modificación de un dato genere efectos laterales que terminen en un comportamiento no deseado, o incluso generar una onda de interferencia que termine tumbando la aplicación.

Lo mismo podría pasar con dos variables que accidentalmente han sido nombradas igual. Un compilador moderno debería avisar de este último caso a nivel de enlazador, y en general, al menos con Visual C++, así ocurre. Otra cosa es que el programador novel le haga caso o se dé cuenta del aviso. Y no todos los compiladores notifican de este hecho, sobre todo los de plataforma cruzada para sistemas embebidos, que suelen ser bastante antiguos.

Volviendo al tema central, la OO intenta solucionar este problema con las clases y la ocultación de datos. Es decir, una clase es una serie de métodos que definen cómo tocar a una serie de datos de forma auto contenida. Y hasta la fecha es la mejor solución que se ha podido encontrar, y si alguien conoce alguna mejor (que no sea una paja mental), le vaticino los laureles del éxito más absoluto y arrollador.

En otras palabras: una clase es una entidad con un comportamiento definido por su código y es algo opaco a cualquier cosa que intente ver su interior excepto un interfaz público que sirva para tratar con ella.

Y hay varias razones, todas ellas muy poderosas, para que esto deba ser así, lo que lleva implícito una serie de obligaciones: ocultación de datos, interfaz pública y autocontención. Otras características como polimorfismo extienden pero no obligan, por lo que las vamos a obviar.

Volviendo al ejemplo de los zapatos, pensemos en un objeto zapato genérico. Cuando construyamos una persona, a la clase persona le añadimos dos objetos zapatos (que podrían ir en un array, pero no es imprescindible).

Y ahora viene el truco del almendruco. Atarse los cordones. Podemos hacerlo de varias formas.

La más chapucera es acceder al método miembro cordón de cada zapato y atarlo, ejecutando una serie de acciones (código) sobre dichos cordones. Para ello necesitamos que cordón sea público.

¿Pero qué pasa si tenemos varios tipos de zapatos? Por ejemplo, unos que lleven hebillas. Ups. El código para atar zapatos que está en la clase persona ya no nos sirve…

Si embargo, si definimos un zapato padre que tenga un método llamado AtarCordones() y que internamente use el dato miembro cordón para realizar la misma operación que antes hacíamos desde la clase persona, cuando heredemos de dicho zapato una nueva clase llamada sandalia, tan sólo tendremos que redefinir el método AtarCordones() que, en lugar de usar cordón, usará hebilla y una serie de acciones con ellas.

Y lo que es más importante, la clase contenedora, persona, se desentiende de qué zapato tenemos, de si tiene hebillas o cordones. Simplemente llama a zapato.AtarCordones() y la acción estará hecha.

[Hablando más seriamente, deberíamos tener una clase base virtual llamada, por ejemplo, ZapatoGenérico que defina un método virtual llamado AtatCordones(). Y luego deberíamos heredar de ahí el Zapato normal y la Sandalia. Y quizás otras variantes de calzado.]

Por lo tanto, al ocultar los datos internos y dejar una interfaz pública, ganamos muchas cosas:

  • Los objetos que manipulen nuestro objeto lo harán a través de una interfaz definida y que debe ser siempre la misma (con la salvedad de los pasos de refactorización que sean necesarios y que cambien los nombres o la propia interfaz).
  • El punto de arriba mejora la claridad del código,
  • Nos evita tener que depurar los objetos llamadores porque no ha cambiado nada en ellos,
  • Nos permite cambiar el comportamiento interno del objeto manipulado sin tener que preocuparnos de la clase manejadora,
  • Permite el efecto biblioteca (comentado por Herb Sutter en su Rationale a C++/CLI, que su vez lo toma de Stroustrup): podemos meter la clase Zapato en una biblioteca, y podremos cambiar su comportamiento interno sin tener que recompilar el código que la use,
  • Oculta la visibilidad, permitiendo un código mucho más limpio y con menos interferencias y símbolos globales, que al menos en C++ pueden lentificar la compilación en grado sumo.
  • Acelera la carga en tiempo de ejecución, porque el cargador del sistema, al tener menos símbolos públicos, tiene que inicializar y relacionar menos cosas.

Vale, creo que no se me escapa nada, y si lo hiciera, con los puntos de arriba hay suficiente.

Todo esto nos lleva a una serie de reglas de diseño con las clases que, al menos yo, llevo a rajatabla:

  • Oculta todo lo que puedas.
  • Usa el nivel más cerrado posible: private en C++ para todo lo que puedas. Luego, cuando refactorices y necesites que algo sea conocido por clases hijas, pásalo a protected, pero con sumo cuidado ya que si hay (o se quiere) efecto lateral, mejor pon un método protegido en el padre que pueda ser llamado por el hijo para acceder a ese miembro privado.
  • Define interfaces públicos, pero los menos posibles. Si un zapato no va desatar los cordones, no lo definas. Si lo necesitas, ya lo definirás. (Aquí hay que mantener cierto equilibrio, porque si sabes que lo puedes necesitar, mejor lo declaras pero no lo defines –cuerpo vacío-, ya que podría ser peor luego tener que cambiar la interfaz ya definida y usada).
  • Documenta por qué, no cómo (el cómo ya te lo dice el propio código, a no ser que sea algún algoritmo complejo, y en ese caso en el 99% de las veces seguro que se puede hacer de otra forma más sencilla).

Finalmente esto nos lleva a un problema que plantea mucha gente, y es que toda esa ocultación y esos métodos de acceso pueden volver un programa extremadamente lento.

Os lo puedo asegurar: eso fue en tiempos pasados, cuando los compiladores eran animales mitológicos y se sabía poco sobre ellos.  Ahora, cualquier compilador medio decente se va a comer tus miles de líneas de código en un santiamén y va a generar mejor código que el que tu pudieras hacer a mano saltándote todos esos pasos. Al menos en C++ y quiero creer que también en C#.

***

Bueno, ahora viene la segunda pregunta, que reformulo pues está planteada con los conceptos cambiados: “¿Alguno sabe la razón de por qué las variables se pasan por valor y los objetos por referencia?” Hablamos de C#, ya que en C++ y C++/CLI podemos pasarlos como nos salga de la pepitilla…

Para responder a esta pregunta debemos meternos un poco en cómo funcionan los compiladores.

Una variable nativa, digamos un entero, ocupa 4 bytes (por decir algo, todo depende de dónde ejecutemos). Un objeto ocupa tanto como la suma de sus datos miembro y una o varias vtable en caso de que tenga métodos virtuales y del nivel de anidación de la herencia (así como de lo bueno que sea el compilador).

[Una vtable es un array de punteros a función que, en tiempo de ejecución, determinan qué método se va a ejecutar dentro de un objeto con métodos virtuales. Un buen compilador resumirá esto en cambiar un call directo a un call indirecto en base a un índice almacenado en algún lugar.]

Cuando nosotros definimos una clase, y a partir de ella instanciamos un objeto, el compilador (y en tiempo de compilación) hace un pase de manos y agrupa todos los métodos miembro en un bloque que junta con los demás métodos miembro de las demás clases, añadiendo unas firmas especiales a los nombres de función.

Digamos que una vez compilado, un programa OO se convierte en un programa no OO que tiene una serie de funciones globales que acceden y modifican una serie de datos… ¿Os suena, verdad? Es que es la única forma de hacerlo, os lo puedo asegurar. La ventaja está en que, salvo oscurísimos errores del compilador que cada vez ocurren con menos frecuencia, a todos los efectos, el comportamiento final simula ser completamente orientado a objetos.

Bueno, cuando pasamos parámetros en una llamada a método, el sistema usa una pila, que es un área de la memoria especialmente destinada a las tareas descritas.

No vamos a entrar en detalles técnicos, pero la cosa funciona así: el compilador pone en la pila los datos a pasar, copiándolos quizá de otro punto de la pila. Entonces hace un call en ensamblador a la función que hemos llamado, y cuando entremos en ella, sabrá que en la cima de la pila tiene sus parámetros.

Cuando pasamos un parámetro por valor, estamos copiando dicho valor en la pila. Si es un entero, ocupará 4 bytes. Si es un objeto, ocupará tantos bytes como datos miembro tenga (ojo, sólo los datos, no el código), más las tablas virtuales más algún que otro elemento más que hace la función de metadatos del objeto pasado.

Si el objeto ocupa 10 bytes, se copiarán, y si ocupa 100, 1K o 100K, se tendrán que copiar absolutamente todos los bytes. Dependiendo de qué objeto, la copia podría ser onerosa en tiempo de ejecución y gasto de memoria.

Cuando pasamos un objeto por referencia, estamos pasando un puntero al dato. No importa en qué lenguaje estemos, siempre es un puntero. Todo lo demás lo hace el sugar syntax del lenguaje, que nos endulza la sintaxis y el operar con ellos.

En este caso, de media, una referencia a un objeto en C#, y si no lo han cambiado, ocupa unos 10 bytes independientemente de qué tamaño real tenga el objeto en sí.

Por lo tanto, ahora vemos por qué en C# un objeto se pasa por referencia y un tipo nativo por valor: optimización.

Esto genera una serie de idiosincrasias en C# que al menos a mi no me gustan mucho, ya que estamos obligando al programador a que lo haga al estilo del lenguaje y no como uno quiera (ya sabéis, eso de “programar en” y “programar con”).

La optimización es la adecuada para la mayoría de casos, pero a veces querríamos hacerlo de otra forma, y es cuando uno ve las limitaciones del lenguaje. En el caso que nos ocupa, un objeto no se puede pasar por valor, lo que fuerza a copiarlo a mano si vamos a modificarlo y no queremos que el original sufra cambios. Y a veces eso es difícil, ya que en algunos casos la copia es de nuevo por valor y no se produce la mutación a nuevo objeto cuando modificamos la referencia creada, y no podemos hacer nada porque el C# (y el .NET) son así.

Sin embargo sí que podemos pasar un tipo nativo por referencia, añadiendo ref al parámetro (con lo cual modificaremos su valor desde dentro de la función llamada). No obstante, la implementación es una chapuza como un castillo, ya que la cosa funciona así dadas las severas limitaciones de la máquina virtual .NET: El valor a pasar por referencia se copia al montículo (heap en inglés, que es donde se guardan los objetos y demás elementos instanciados por referencia) y entonces se pasa, en la pila, una referencia a ese valor puesto en el montículo. Cuando salimos de la función llamada, el sistema coge del montículo el valor modificado y lo vuelve a poner en la pila.

Es lo que se llama box/unbox, y viene determinado por la limitación del .NET de acceder a la pila cuando el elemento a tocar no está encima de ella. La única ventaja que obtenemos de esto es que es mucho más difícil generar una inyección de código mediante el envenenamiento de los parámetros de retorno de la pila.

***

Para aquellos que quieran tener una visión más amplia de la orientación a objetos, así como muchas explicaciones, les recomiendo el libro de Bertrand Meyer, Construcción de software orientado a objetos Segunda Edición, que es un tocho de más de mil páginas denso como él solo… Pese al proselitismo hacia Eiffel, es un gran libro. Hay edición en castellano de Prentice Hall (que es la que yo tengo), pero no sé si se podrá encontrar o no.

Otro no menor pero más práctico, es Code Complete 2 de Steven C. McConnell. Este es mucho más práctico y orientado hacia el código real que escribimos las personas normales.

Windows Phone y las petisoperías

Hay que joderse, que dijo aquél. Este podría ser el resumen de esta entrada. Ahora mismo veréis por qué.

***

Estoy empezando a jugar de forma más o menos seria con Windows Phone. Ya cuando salió, el no poder ejecutar código nativo básicamente me hizo pensar que Microsoft había perdido el tiempo y que WP iba a ser un gran y nuevo fracaso de la compañía que había perdido el norte, el sur, el este y el oeste…

Pasa el tiempo y pese a todos los intentos de MS, WP no levanta cabeza. Ni cuota de mercado ni aplicaciones, comparándolo con Android e iOS, que crecieron mucho más rápido.

Ahora sé algunas razones, imposibilidad de usar C++ incluída.

***

Windows Phone 7.0 no tiene sockets. Toma ya. Increíble. Para los legos: no hay posibilidad de crear ningún tipo de aplicación de chat, ni de comunicación remota a servidores ni nada parecido. Todo hay que hacerlo mediante HTTP, que no conserva el estado y necesita sobredimensionar el lado servidor frente al cliente…

Whidows Phone 7.5 tiene sockets… de aquella manera. Cuando la aplicación pasa a background el socket se cierra, con lo que pierdes la persistencia en la comunicación. Aquí lo puedes solucionar mediante técnicas Push (que todavía no he visto e ignoro las limitaciones y ventajas que puedan tener). Pero de momento, aplicaciones verticales que requieran una conexión permanente tampoco están permitidas.

***

Ahora llega la última versión, con dispostivos de 256 MB de memoria… Y la vuelven a cagar. Dadas las limitaciones de los mismos, dejan de funcionar las tareas periódicas y las intensivas… Y te dicen que cuando lances la aplicación mires en qué dispositivos estás ejecutando y uses secuencias if/else para cambiar el comportamiento.

En primer lugar eso es una soberana imbecilidad como una casa. ¿Tienes que codificar las cosas dos veces? Pues no, tendrás que hacerlo todo con la limitación predicha, porque si lo puedes hacer sin esos elementos, es una soberana gilipollez repetir el código. El problema vendrá cuando sin eso no puedas hacerlo.

En segundo lugar, mientras los demás fabricantes implementan mejoras y optimizan el código de sus sistemas operativos, Microsoft hace lo contrario. Apple pasa de dispositvos de 256 MB de RAM a 512 y luego a 1024. Microsof pasa de 512 a 256, llevándose cosas de en medio.

***

Recuerdo la presentación de Windows Phone: que si sólo .NET, que si sólo procesadores multicore, que si sólo dispositivos de gama alta… Al final tienen que pasar por el aro, pero en lugar de abrir el Windows CE interno al lenguaje C y/o C++ y quitar la mierda del .NET, que seguro consume 40 de los 60 MB de la huella de memoria de cada proceso, van y quitan prestaciones… porque lo de las tareas asíncronas es sólo una de tantas limitaciones impuestas, que podéis leer en la entrada que os enlazo más arriba.

***

Resumendo, que van de mal en peor. RFOG dixit.

La Oferta Interminable

No sé si reír o llorar, si partirme el culo de la risa o qué hacer. Os cuento rápidamente:

Embarcadero, actual dueño de RAD Studio, que combina Delphi, C++ Builder y otras herramientas, tiene por costumbre sacar, año sí y año también, una nueva versión de cada una de sus herramientas de desarrollo. Da igual en el estado en que estén: ellos, más o menos a finales de agosto, sacan lo que ellos llaman una nueva versión.

Digamos que es una suscripción anual encubierta, ya que la nueva soluciona algunos bugs de la anterior, y de paso añade unos cuantos más. El compilador de C++ está estirado hasta límites increíbles, y parece ser que están haciendo algo nuevo con soporte multiplataforma y mixto entre 32 y 64 bits. Ya veremos qué sacan.

Os decía que cada año hay una nueva versión del producto. En general suele ser bastante inestable y algunas veces completamente inusable en según qué escenarios. Hasta que no sacan el primer parche la cosa no empieza a ir medianamente bien. Debe ser que eso de probar antes de vender no va con ellos.

También suelen hacer algún tipo de oferta que viene a durar dos o tres meses. La más típica es la de que pagas un producto y te llevas otro igual. No obstante últimamente se ha puesto de moda primero limitar las actualizaciones de versiones demasiado viejas.

Es decir, que si tienes un C++Builder 5 no puedes, legalmente, actualizarte a C++Builder XE2 (la última) a precio de actualización, sino que tienes que pagar la versión completa. Comprarla la puedes comprar, pero cuando metes el número que te dan y tiras a activar te dice que nones, así que ándate con ojo.

Ya que estamos, y antes de entrar en el meollo de la entrada os cuento la última que han hecho. Dado el XE2 es la primera versión en la que se soportan los 64 bits para Delphi en Windows y la plataforma OS X (MAC) en 32 bits (que ya les vale), están sacando actualizaciones a bastante buen ritmo. De hecho la última que salió hace unos días fue la cuarta.

Es una actualización completa. Tienes que desinstalar el producto completo y volver a instalar la versión completa.

Pero eso no es lo único malo. Lo peor de todo es que esta nueva “actualización” rompe la compatibilidad binaria hacia atrás y, para más inri, hay algo que han hecho mal y el enlazador tiene serias limitaciones para generar programas en Firemonkey hasta el punto de que es completamente inusable si no aplicas una chapuza de las gordas…

Hay que joderse.

***

Bueno, ahora ya sí, lo que os quería contar. Os decía que primero te capan la posibilidad de actualización desde versiones antiguas. Eso deben haberlo hecho para forzar a los usuarios que todavía utilicen versiones viejas (recordemos que, al menos en el C++ Builder y para Windows, desde la versión 4 no han aportado nada realmente nuevo) a comprar una actualización o a perder definitivamente la posibilidad de hacerlo.

En cierta medida tiene su justificación, aunque si tu estás más que cómodo con tu C++Builder 4 ó 5, no tienes por qué actualizar para encontrarte con nuevos problemas y nuevas idiosincrasias impuestas por Embarcadero. Y de todos modos pienso que no es más que una técnica para intentar vender más.

No obstante el cachondeo viene ahora. La oferta comenzó en agosto de 2011, terminó en septiembre u octubre (no recuerdo exactamente cuándo), pero la ampliaron hasta diciembre. Una semana antes de caducar volvieron a ampliarla hasta no recuerdo qué fecha, y finalmente lo han vuelto a hacer hasta el 31 de marzo.

¿Sabéis lo que pienso? Que casi nadie les compra ya el producto, y con la tónica que llevan no creo que continúen teniendo mucho éxito. O se espabilan de una vez o los veo en el agujero, pese al bombazo de la multiplataforma (que por cierto ha pasado sin pena ni gloria en los medios de comunicación), que como ya os dije, vino traída de forma bastante chapucera.

Instalando las últimas versiones de QT en un MAC

Conforme va pasando el tiempo vemos cómo un producto que pudo haber sido de Microsoft y haber constituido la plataforma base nativa para Windows, va creciendo y estabilizándose poco a poco.

Hablo del framework de QT, creado por Trolltech, vendido a Nokia y, cuando ésta pasó a formar parte de Microsoft, cedido a Digia para el soporte. Resulta un producto un tanto movido, base de KDE y de muchos otros productos interesantes.

Hasta no hace mucho, utilizar QT Creator para construir este tipo de aplicaciones era un tanto cansino, porque el producto no estaba a la altura de un IDE medio decente. Tampoco es que ahora lo esté, porque sólo permite visualizar una ventana, y la depuración es, cuando va, lenta. Ni se te ocurra poner puntos de interrupción en diferentes hitos, porque algunas veces pararán y otras no. El inspector de variables locales tampoco anda muy fino que digamos.

No obstante QT sí que está muy maduro y funciona muy bien. Al menos en las partes que he usado, que son todo el tema de los Widgets y la sincronización. Eso sí, desde Visual Studio con su correspondiente plugin. Desde el propio Creator da verdadero asquito de usar.

Bueno, os decía que ahora el SDK de QT tiene hasta un instalador, tanto para MAC como para Windows y Linux. El de Windows va muy bien, lo uso en el trabajo y no he tenido problemas. (Y es por eso por lo que he dejado de hablar de Metro y C++ ya que estoy metido de lleno con QT y apenas tengo tiempo de mirar otras cosas. Ya sabéis, donde manda patrón no manda marinero).

No obstante, en el iMAC no conseguía instalarlo, e internet no es que me fuera de mucha ayuda… Hasta que de refilón, y mirando otra cosa, lo encontré.

***

Para instalar el SDK de QT es necesario tener xCode instalado previamente. Pero si la versión que tienes es la 4.3 el instalador de QT va a protestar y te va a decir que no tienes un compilador de C++ en tu equipo, pese a que xCode está ahí y compila sin problemas un proyecto…

¿La solución? Abrir xCode, ir a Preferencias, pestaña de Descargas, e instalar las Command Line Tools. Necesitas una cuenta de Developer en Apple, que es gratuita.

Una vez instalado eso, ya podrás poner en marcha tu copia de QT.