Sobre delegados, closures, punteros y la falacia del puntero loco

Leía esta mañana –mientras se carga una serie de gráficos en mi actual proyecto- el blog de Marino Posadas que, aunque publica poco, publica bien, y me he quedado más que estupefacto con lo que nos ha contado. He de matizar que en ningún momento dudo de la palabra de Marino, y es por eso por lo que me ha llamado tanto la atención de lo que nos ha contado.

Antes de seguir aquí lo mejor es que leáis su entrada, y volváis a esta.

***

El primer malentendido sobre el texto nos lo ha solucionado el propio Marino en los comentarios: no, Anders no se tiró seis meses para inventar los delegados, sino que estuvo tal tiempo creando el .NET, y durante el cual salió la idea del delegado. Haremos la vista gorda e ignoraremos el hecho de que en Delphi (y C++ Builder) el propio Anders ya tenía algo así.

Además, siempre he dicho que el delegado es el paso siguiente al puntero a función y/o función de retrollamada (o callback, como le se suele citar habitualmente). Eso también queda fuera de toda discusión.

Cuenta Marino que Anders primero analizó que una mayoría de las BSOD en Windows venían a cuenta de drivers defectuosos, y que otra mayoría venía causada por una variación sobre un mismo tema: el moldeo o casting incorrecto de punteros a función, algo que es bastante común en el desarrollo de sistemas.

Por lo tanto, para acabar con el segundo problema, decidió implementar los delegados en .NET. Aquí tenemos la primera falacia de todas, y encima autocontenida. Es decir, los delegados están en .NET, pero no en la creación de drivers, ni en la de software de sistemas, que se sigue haciendo en C++, así que solución, nada de nada.

Claro, a no ser que pensara que el mundo™ iba a abandonar sus millones de líneas de código™ y se iba a pasar derechito al .NET™, acabando en un par de meses con el infierno de los punteros.

Espero que ya se haya dado cuenta de que no, que el .NET está y sirve para lo que está y sirve, y para nada más. Por lo tanto, está claro que no ha acabado con el problema de los punteros locos.

Lo que ha hecho ha sido crear una plataforma de desarrollo en la que se minimiza el problema, pero el código nativo (nuevo y viejo) sigue adoleciendo del mismo. Además, en .NET un delegado vacío, hasta donde llego, lanza una excepción, de igual forma que la ejecución de un puntero a función nulo lanza, también, una excepción, que si eres buen programador, habrás capturado.

***

Vamos ahora a por la segunda falacia. ¿Sabéis qué es en realidad un delegado? Pues un delegado es una clase normal y corriente, pero que está cableada en cierta medida dentro del compilador de C#. De hecho, en C++/CLI podemos usarla como una clase más. Podéis leer algo sobre esto aquí.

¿Y qué hace esa clase? Pues tiene un constructor que viene a recibir un puntero a método o función global. Tiene un método que es el que ejecutará ese puntero recibido. Y tiene una serie de eventos y sobrecarga de operadores que nos permiten la semántica de utilizar “+=” y “-=” para añadir/quitar punteros a función.

Y cuando mandamos ejecutar el delegado, lo que hace es comprobar si el puntero es válido, y si lo es, lo ejecuta. Y si se trata de un delegado multicast, entonces contiene una bolsa de punteros a función que va comprobando que sean válidos y ejecutando uno detrás de otro.

Ni más ni menos.

***

Ahora vamos a lo que vamos, que es la tercera falacia y la más importante de todas. ¿Sabíais que con C++ hacer eso mismo es casi trivial? ¿Sabíais que ya está hecho en Boost (no lo he mirado, pero me apuesto un gallifante a que los hay, y seguro que de varios tipos)? ¿Realmente quería Anders acabar con el tema de los problemas con las funciones de retrollamada? No lo creo, ya que si eso realmente hubiera estado en su agenda, lo que se habría hecho es modificar Win32 para que aceptara un objeto de tipo delegado creado con alguna clase de C++, con un operador puntero para C.

Porque niños, hacer eso mismo en C++ no sólo es trivial, sino mucho más directo, ya que podemos sobrecargar el operador de llamada a función y el de puntero, como nos lo demuestran los predicados de la STL… Pero claro, Anders tiene que barrer para casa y expresar las genialidades de .NET y C#…

En fin…

10 comentarios sobre “Sobre delegados, closures, punteros y la falacia del puntero loco”

  1. Rafael, por matizar algunas de las cosas que comentas:

    La ventaja de los delegados frente a los viejos punteros a función es clara. Si haces un cast de un tipo de función a otro no tienen una excepción, tienes un puntero erroneo con las consecuencias que esto tiene. El caso que comentas de puntero a función nulo es el más trivial. Tener una excepción en lugar de un puntero loco es un grandísimo paso.

    En boost hay un equivalente a los delegados, los Fucntion, y son parte de los draft de los estándares luego la idea es buena y sirve.

    El API de Win32 es un API C, no C++ de hay que usar clases de C++ como el Function de Boost no sea una opción valida.

    Un saludo.

  2. Jo, no has tardado a pillar «mis» falacias. Sigh! 🙂

    ¿Por dónde andas? Porque te haces difícil de leer por la bogosfera y demás internet-era.

  3. Un par de apuntes, bueno, uno:
    Tu dices:
    «… Aquí tenemos la primera falacia de todas, y encima autocontenida. Es decir, los delegados están en .NET, pero no en la creación de drivers, ni en la de software de sistemas, que se sigue haciendo en C++, así que solución, nada de nada.»
    Y luego dices:
    «… hacer eso mismo en C++ no sólo es trivial, sino mucho más directo»
    Posadas dice:
    «Resultó que el 90% eran debidas a drivers, y ahí solo podían ponerse serios con los fabricantes y poco más»

    Con esto yo entiendo que sigue siendo problema de los fabricantes y que por mucho que haga MS si no se usan las herramientas que el lenguaje nos ofrece … Por eso creo que llamarlo falacia quizás sea un poco … exagerado (IMHO)

    Por lo demás me han encantado los dos artículos 🙂

  4. Por completar el tema: el asunto de los fabricantes es realmente peliagudo. Según tengo entendido, algunos no pasan el HCL (Hardware Compatibility List) a propósito, por que se verían obligados a reescribir el código de los drivers de sus dispositivos (o lo que es peor: a modificar el hardware primero).

    En cuanto al lenguaje C++, reconozco y entiendo muy bien el cariño que Rafael lo tiene. Si hay un lenguaje que haya resistido el paso del tiempo, que haya permitido construir todo tipo de software, que se haya implantado tanto en las universidades como en la industria, ese es C++. A mí me gusta más C#, pero por que me he vuelto muy cómodo (será la edad), y porque encuentro en C# una elegancia, una simplicidad y una potencia (si quiero también puedo empotrar rutinas en C/C++) que no me ofrece ningún otro.

    Y, aunque no es cierto que sobre gustos no haya nada escrito (y si no, mirad todo lo que se escribe sobre la moda), para mí es una cuestión casi de estética. Y eso que le están saliendo competidores duros: F# tiene expresiones que son la bomba y una potencia impresionante. Aunque pienso que se quedará para complementar a los demás en temas de cálculo. En eso no tiene comparación. Las demos que me hizo Don Syme en otra entrevista, cuando me presentó el lenguaje me dejaron con la boca abierta…

    En fin, que el debate continúa…))

    Saludos

  5. Somos bastantes los que opinamos que .NET se queda bastante corto para muchas tareas… No obstante, para las típicas de bases de datos, web, etc, va muy bien.

  6. Estoy totalmente de acurdo con Rodrigo Corral. Pensar que un puntero a función es similar a un delegado, es desconocer completamente las implicaciones en cuanto a seguridad que tienen uno y otro. Cualquier persona que haya programado mínimamente en C++ sabrá lo fácil que es errar el casting en un puntero a función. Por supuesto el mismo problema se puede tener utilizando delegados, pero la diferencia radical es en las consecuencias. Mientras que un error de ese tipo en un puntero a función produce un error a veces extremadamente difícil de detectar, a veces incluso con consecuencias en la seguridad de la aplicación, un error de ese tipo en un delgado produce una excepción, y a menudo puede detectarse incluso en tiempo de compilación.

    PD: No entiendo el afán de cierta gente de creerse más listos que todos los demás. Habitualmente suelen ser los que menos motivos tienen para ello.

  7. @Listof, ¿tu has leído bien la entrada y los comentarios? Yo he dicho que un delegado es un paso más allá que el puntero a función, y que C++ tiene, no sólo eso (los delegados), sino implementado de una forma muchísimo más potente y clara, mediante la utilización de clases y plantillas.

    Otra cosa es que el programador de C++ sea un cenutrio y esté usando C++ como un mejor C (a la Stroustrup), olvidándose de cosas tan interesantes como los autopunteros, los predicados (que va más allá del concepto de delegado) y el uso de closures y propiedades mediante adaptors, etc.

    Así que apuntate a lo de «listillos».

  8. Bueno, no he sido yo el que ha dicho: «Además, en .NET un delegado vacío, hasta donde llego, lanza una excepción, de igual forma que la ejecución de un puntero a función nulo lanza, también, una excepción, que si eres buen programador, habrás capturado.»

    Creo que esta afirmación demuestra que no conoces la verdadera naturaleza del problema, puesto que el problema menos importante con respecto a los punteros de función es que sea nulo.

    Tampoco he sido yo el que ha escrito: «Porque niños, hacer eso mismo en C++ no sólo es trivial, sino mucho más directo, ya que podemos sobrecargar el operador de llamada a función y el de puntero, como nos lo demuestran los predicados de la STL… Pero claro, Anders tiene que barrer para casa y expresar las genialidades de .NET y C#…
    «

  9. Por cierto, respecto a tu último comentario. ¿A qué te refieres con implementar delegados mediante clases y plantillas? ¿Puedes poner un ejemplo?.

    En cuanto a los predicados, me gustaría saber de qué forma permiten garantizar la seguridad de tipos, que como ya hemos dicho, es la característica distintiva de los delegados frente a los punteros a función. Pensar que esta diferencia es un simple avance y no un cambio radical en cuanto a la seguridad de la aplicación, me hace reafirmarme en mi última nota.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *