Mostrando entradas con la etiqueta C++. Mostrar todas las entradas
Mostrando entradas con la etiqueta C++. Mostrar todas las entradas

viernes, mayo 13, 2011

Lo que todo programador de C debería saber sobre el "comportamiento indefinido"

Este ha sido uno de los artículos damnificados por la caída de blogger.com y por eso no lo he puesto aquí antes, pero me gustaría reseñar este What Every C Programmer Should Know About Undefined Behavior 1/3 del blog de LLVM. No solo hace una estupenda introducción al qué y al por qué del comportamiento indefinido en C sino que apunta a una serie de artículos que merece la pena mencionar también: A Guide to Undefined Behavior in C and C++, Part 1, Part 2 y Part 3.

Del primero de esa serie, para que quede claro de qué va el asunto :)
Of course it is physically possible to pick up a basketball and run with it. It is also possible you will get away with it during a game. However, it is against the rules; good players won’t do it and bad players won’t get away with it for long.
Como se puede leer es uno de esos temas que conviene saber (y muy bien) si se pretende hacer código portable. Así que, para leer con detenimiento.

Soy un atento seguidor del blog de LLVM, pero esta vez lo he visto en el twitter de TooManySecrets

viernes, abril 29, 2011

¿Testeo o especificaciones?

¿Cómo se determina el comportamiento de un sistema? ¿Testeo o leyes, estándares, especificaciones...? Como siempre, es mi postura, las dos. El ejemplo surge de una incialización incompleta de un array de caracteres ¿Cómo se debería comportar? Mi teoría en principio, más o menos errónea, era que debía ocurrir lo mismo que con una copia de cadenas en C. Se copia hasta el cero terminador y ahí acaba el proceso. El resto de elementos del array indefinidos, sin inicializar. Pero estaba la duda... ¿Cómo se comporta y/o se debe comportar un sistema?

Para responder a las dos preguntas ni los test son suficiente ni lo que diga el estándar lo es. Los dos juntos pueden calmar más :)

En el punto 8.5.2.3 del estándar C++0x se habla de lo que ocurre si se proporcionan menos caracteres a la inicialización:

If there are fewer initializers than there are array elements, each element not explicitly initialized shall be
zero-initialized


Sin embargo este punto está ausente en el C++98. Los test aparentemente dicen que se suelen rellenar de ceros ¿Por qué? y ¿Podemos confiar en que siempre sea así?

Asumiendo (que quizás sea mucho asumir) que la incialización de cadenas es análoga a la incialalización en forma de lista (con llaves) esto puede tener una expliación. Según se puede leer en el punto 8.5.1.7 del C++98:
If there are fewer initializers in the list than there are members in the aggregate, then each member not explicitly initialized shall be default-initialized (8.5)
[Example:
struct S { int a; char* b; int c; };
S ss = { 1, "asdf" };
initializes ss.a with 1, ss.b with "asdf", and ss.c with the value of an expression of the form
int(), that is, 0. ]



En C++0x pensaba que quizás cambia algo pero el punto 8.5.1.7 para decir básicamente lo mismo:
If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member
not explicitly initialized shall be initialized from an empty initializer list (8.5.4)

Las diferencias vienen de los mejoradas listas de inicialización de C++0x.

Leyendo todo esto yo tengo claro que no me fiaría de que el resto de los caracteres estuviesen a 0, aunque funcione así probablemente en casi todas las implementaciones... O sea, que no conviene fiarse de lo que hay después del cero terminador. Además ¿Para qué? Es tentar a la suerte...

Lecciones a aprender, al menos tal como yo lo veo:
  • Intentar no buscar los casos difíciles, en los que hay que consultar la ley. En ellos es probable que no todas las implementaciones la hayan leído correctamente
  • Conocer los estándares y especificaciones que usamos o al menos saber buscar en ellas en caso de duda
  • Testear. cuantas más pruebas se hagan al código más fácil es encontrar los "casos patológicos"

jueves, abril 15, 2010

Publicado GCC 4.5

GCC 4.5 acaba de ser publicado. En la lista de cambios se pueden leer las novedades de esta versión. De ellas las más visibles para los usuarios pueden ser las mejoras en el mostrado de errores como indicar la columna por defecto, evitar mostrar más errores en caso de falta de un fichero de inclusión o no mostrar los parámetros por defecto de las plantillas, con la ganancia de legibilidad que eso supondrá. Además viene con mejoras en las optimizaciones, en los lenguajes y plataformas soportadas y con un gran avance en el soporte experimental de C++0x además de la inclusión en la rama principal del sistema de plugins.

Al hilo del sistema de plugins ha surgido una acalorada discusión a raíz de una propuesta de incluir en un futuro por defecto el plugin DragonEgg en la distribución de GCC. DragonEgg en un plugin que reeemplaza las optimizaciones y la generación de código de GCC por las de LLVM.

La misma entrada y previsiblemente más comentarios en Publicado GCC 4.5 en barrapunto. Desde que estoy de editor por allí, por aquí se mueve menos la cosa. Espero que sepan perdonarme, pero no me olvido de este otro lugar :)

miércoles, febrero 10, 2010

Clang ya se autocompila y GCC ya tiene sus primeros plugins

Como puede leer en el blog de LLVM/Clang, el compilador con licencia similar a la BSD patrocinado por Apple:
¡Clang ha completado su primera autocompilación! Hemos compilado toda la LLVM y el propio Clang con Clang, unas 550k líneas de código en C++. Los binarios resultantes han pasado todos los test de regresión y el Clang resultante puede además generar toda la LLVM y Clang de nuevo. Este tercer Clang resultó plenamente funcional completando así el bootstraping .
Más comentarios en OSNews y reddit. En noticias relacionadas, el sistema de plugins de GCC va dando sus frutos y la fundación Mozilla ha desarrollado Dehydra y Treehydra, unos plugins para hacer análisis estático de código C++.

La misma entrada y espero que más comentarios en Clang ya se autocompila en barrapunto.

martes, mayo 26, 2009

memcpy y la concurrencia

Si el otro día comentaban en barrapunto que en MS marcaban como peligroso el uso de memcpy hoy podemos encontrar otra razón para usarlo con precaución. En memcpy() concurrency curiosities David Dice habla de un comportamiento antiintuitivo de dicha función que sale a la luz en entornos fuertemente concurrentes: sobreescribir la misma secuencia constante en memoria puede tener valores no válidos temporalmente durante su llamada, leídos por otros threads con resultado catastrófico si no se tiene cuidado. Otro caso más en el que las abstracciones que usamos evolucionan a su manera, quizás con explicación pero con resultados que contradicen la intuición y exigen, a veces, conocer que se cuece allá abajo. (No mucho) más en reddit

La misma entrada y más comentarios en memcpy y la concurrencia en barrapunto

miércoles, enero 14, 2009

Trucos gcc-céntricos y porqué no usarlos

En el estupendo blog Coding Relic el autor, Denton Gentry, ha publicado recientemente dos trucos muy espectaculares pero gcccéntricos. Bueno en realidad uno es específico de la glibc y el otro sí es una extensión de gcc:
  • printf-acular en el que se describe cómo personalizar printf para que admita más tipos de datos que los que normalmente admite, en el ejemplo sacar direcciones MAC formateadas.
  • Variable Scoping with gcc, en el que se explica el (espectacular) funcionamiento de __attribute__(cleanup), que permite funcionalidades del tipo RAII de C++
Y ahora, después de ver un par de extensiones tan útiles, potentes y elegantes es cuando me toca desrecomendarlas :) Creo firmemente en el valor de los estándares y en desarrollar siempre en el nivel más estricto de estándar o visto de otro modo en el nivel más amplio de disponibilidad, siempre y cuando las soluciones sean comparables. Además, es bueno pensar en la gente que retocará el código y cabe la posibilidad de que si no se documenta resulte de sólo escritura. Yo sólo lo vería admisible en un hack puntual documentado como mínimo con neones. No obstante, hay gente que no opina lo mismo como se puede leer en Uso de extensiones del GCC en (el kernel) Linux.


La misma entrada y más comentarios en Trucos gcc-céntricos y porqué no usarlos en barrapunto

lunes, noviembre 17, 2008

Varios sobre concurrencia y rivales de GCC

Ración de varios variados:

Movidillo parece el micromundo de los compiladores libres, veremos si la competición les sirve para mejorar a todos ellos.

La misma entrada y más comentarios en Varios sobre concurrencia y rivales de GCC en barrapunto

martes, noviembre 11, 2008

Publicado LLVM 2.4

Acaba de ser anunciada la versión 2.4 de LLVM (Low Level Virtual Machine). A LLVM y su compilador asociado clang se les ha querido ver como un competidor de GCC, en parte por su licencia BSD, por su diseño más modular, por su novedoso enfoque de las optimizaciones y por el apoyo de Apple. La versión 2.4 trae una buena cantidad de novedades entre las que destacan la mejora en la generación de código, compilación más rápida y soporte para la arquitectura PIC16. En el propio anuncio se apunta a una serie de presentaciones y vídeos para saber más. [Vía reddit]


La misma entrada y más comentarios en Publicado LLVM 2.4 en barrapunto

lunes, noviembre 10, 2008

Un vistazo al interior de memcached

Leo en El valle del Viento Helado un artículo acerca de la arquitectura interna de memcached. memcached es un sistema de cachés genéricas muy usado en aplicaciones web. En el artículo se repasa tanto la elección de libevent como sistema de gestión de eventos sobre descriptores de fichero, el uso de funciones de entrada/salida no bloqueantes y sobre todo una gestión de memoria basada en un slab allocator. Contiene además enlaces con más información sobre el tema aunque como siempre la información última está en el código fuente.

La misma entrada y más comentarios en Un vistazo al interior de memcached en barrapunto

viernes, junio 20, 2008

Propuesta para introducir C++ en gcc

Volviendo al tono un poco menos noticioso de esta bitácora, me gustaría referenciar la propuesta de Ian Lance Taylor de permitir determinadas características de C++ en el código de gcc, sobre todo aquellas que permiten hacer el código más compacto y mantenible. Nombra explícitamente STL, polimorfismo y punteros inteligentes en contraposición a la recolección de basura que se usa en gcc. Hay que hacer notar que la propuesta debería ser aprobada (no sin antes haber pasado por el adecuado flame C vs C++) aunque Ian propone crear una rama gcc-in-c++ para experimentar.

Me ha parecido una propuesta muy razonable, con una exposición muy clara de lo que se ganaría con el cambio. Es precisamente el (moderado) uso de las características de C++ lo que me ha parecido más reseñable. C++ es un lenguaje grande y no todas son adecuadas a todos los problemas, con lo que hay veces que es sano delimitar como se usa. Esto da lugar no obstante a estándares de codificación con casi obsesivo nivel de detalle, pero de los que se puede aprender si se lee con atención crítica.

Por cierto que para evitar dependencias indeseadas (y que C++ se convieta en el caballo de troya que potencialmente es) se propone un enlazado estático con la libstdc++. No sé que opinará Ulrich Drepper.

Se puede leer más sobre el tema en el propio blog de Ian , en reddit (posteado por un servidor), en LWN y (con poco éxito de crítica y público) en menéame.

La misma entrada y más comentarios en Propuesta para introducir C++ en gcc en barrapunto

jueves, abril 24, 2008

La vida privada de volatile

El estándar de C(++) tiene unas cuantas esquinitas. Seguramente volatile es una de ellas. Para aclarar el significado exacto de este calificativo en Coding Relic han publicado La vida privada de volatile, en donde no solo se analiza la letra del estándar sino que se examina el código generado para MIPS en distintos casos.

Conviene recordar los posibles usos de volatile, así como para qué no debe usarse:
La vida privada de volatile en barrapunto

miércoles, abril 09, 2008

La aritmética de punteros, su desbordamiento y la seguridad

Me he enterado vía el valle del viento helado de una nueva polémica acerca de gcc y su implementación. El hecho es que gcc cambió su comportamiento en cuanto a comparaciones entre punteros incrementados y ha provocado muchos meses después una alerta de seguridad desproporcionada y según los desarrolladores de gcc, falsa (Se puede oír desde aquí el ruido y la furia)

El caso es que, entrando en profundidad, es una optimización casi "trivial" que hace que p + C1 < p + C2 pase a ser C1 < C2 sin tener en cuenta el posible desbordamiento (overflow) de ambas operaciones. Hay que decir además que el estándar (de C y de C++) declara no definido el valor del resultado de la suma entre un puntero y un entero cuando se sobrepasa el tamaño del objeto al que apunta.

Es muy importante este último punto, porque deja traslucir un error de concepto: la comprobación de overflow propuesta en la alerta(*) es muy burda porque sólo se comprueba el desbordamiento de la operación aritmética, no que nos estamos saliendo del rango permitido. Sin embargo lo que deben saber el programador y el programa a la vez, cuando están manejando aritmética de punteros es cual es el desplazamiento (offset) máximo, de cuanta memoria se dispone. Si eso no lo sabe, es inútil cualquier otra comprobación...

Hay que hacer notar además que la optimización de la que se habla aquí la aplican casi todos los compiladores y lo que provoca la alerta es en realidad el cambio de comportamiento, no que sea incorrecto.

También recuerda esto que es muy mala política basar el código en detalles de implementación específicos de la plataforma. En este caso y en muchos otros, los desarrolladores de gcc tiran de cita del estándar para argumentar sus decisiones. Los usuarios se quejan, llamándoles abogados del lenguaje (language lawyer) pero en momentos como esos hay que recordar que probablemente están defendiendo sus decisiones de implementación y lo importante de esas decisiones para el éxito de una tecnología.

(*)char *buf;
int len;
len = 1 << 30;
if (buf+len < buf){ Overflow }


La aritmética de punteros, su desbordamiento y la seguridad en barrapunto

miércoles, febrero 13, 2008

Notas sobre errores fatales y portabilidad

Es escasa y dispersa la información que he encontrado acerca de la gestión de errores fatales en distintos sistemas operativos y lo que afecta al funcionamiento de los programas sobre ellos. Así que voy a juntar lo que he ido recopilando en una sola entrada. Puede ser un poco batiburrillo, pero igual le es de utilidad a alguien... (Para mi seguro, que así no pierdo los enlaces). Ordenadas de general a específico[1]:


  • En ANSI C existe signal que permite cambiar la gestión este tipo eventos, entre los que se encuentran SIGSEGV, violación de segmento y SIGFPE, error aritmético. No obstante, por desgracia, su comportamiento en programas con más de un thread no está especificado. Además por si fuese poco ANSI tampoco especifica el handler por defecto, es decir, qué es lo que pasa si salta una señal de ese tipo.

  • De hecho, de los que he probado, en windows con la librería de C del VisualStudio al menos, las señales funcionan por thread y en linux es global por proceso...

  • En POSIX si que se define el comportamiento por defecto de las señales. En concreto SIGSEGV y SIGFPE abortan el proceso completo . Se pueden ver los comportamientos por defecto de las distintas señales en POSIX en la documentación de <signal.h> del OpenGroup. Además, POSIX(R) recomienda pasarse a sigaction (que sólo es POSIX(R), no ANSI).

  • Windows NT no genera señal de violación de segmento, pero se le puede instalar handler

  • En win32 un error fatal se gestiona como una excepción estructurada (SHE) que en C++ se mapea a un excepción normal[2]. Además, una excepción estructurada no capturada, por defecto, supone la muerte del thread en el que se produce, pero no del proceso global .

  • Existe una forma de sobrescribir este comportamiento por defecto y es SetUnhandledExceptionFilter. Recomiendan en Nynaeve[3] no hacer cosas demasiado complicadas en el manejador, como parece lógico.

  • En un artículo de DeveloperWorks comentan un modo de convertir señales en excepciones pero que es un hack completamente erróneo: C++ exception-handling tricks for Linux porque da a entender que se puede replicar el comportamiento por defecto de Windows. Sin embargo, como se puede leer en "Program Error Signals" de la documentación de la glibc:
    La acción por defecto de todas estas señales es terminar el proceso. Si se bloquean o ignoran estas señales o se establece un handler que retorna normalmente, tu programa cascará espantosamente en el momento en que suceda la señal no ser que(*) haya sido generada a través de raise o kill en lugar de un error real.
    cosa que he podido comprobar con el código de DW tratando de ignorar una violación de segmento.

  • En resumen, lo más juicioso (y homogéneo entre plataformas) sería parar el proceso completo en caso de uno de estos errores y tratar de informar lo más completamente posible del error. En caso de win32 habría que forzar el fin del proceso, ya que no es la acción por defecto[4].


[1]El orden es de mayor a menor, de general a específico. Debería ser obvio para un programador: a igualdad de condiciones, se debería usar lo que funcione en el estándar más aceptado y amplio ¿por qué limitar el ámbito de aplicación? Primero, buscar en ANSI, y si la funcionalidad no está elegir conscientemente una solución más específica. En el fondo para mi no es más que una consecuencia de la ley de Postel sobre el el código: "Cuanto más estricto (más estándar) es lo que escribes, más aumentarás la interoperabilidad". Puede parecer una obviedad, pero en esas pequeñas microdecisiones de los programadores no es nada extraño usar extensiones del estándar sin necesidad. Y en caso de necesidad se puede echar mano de librerías libres, muchas de ellas con un alto nivel de portabilidad.

[2]Pongo en cursiva normal porque entra la duda si en esos casos, de tan bajo nivel, una excepción, que se confunde con excepciones de de otro tipo, es lo más clarificador para el usuario-programador, máxime cuando el C++ estándar no posee excepciones para gestionar estos casos...

[3]Un blog que he descubierto hace poco... interesante. Además sobre el mismo tema: Beware of custom unhandled exception filters in DLLs y You might be using unhandled exception filters without even knowing it

[4]Cabe preguntarse porque se permite la ejecución del proceso en un caso tan excepcional... habida cuenta además de que una excepción no capturada de de C++ provoca, por defecto el fin del proceso... En este caso está sin duda justificado el Fail Fast: lo que hay que hacer es arreglar el programa, no dejarlo seguir...

(*)Me despisté al traducir. Gracias a Javier Noval por leerse los enlaces y hacerme notar el error ;)

Notas sobre errores fatales y portabilidad en barrapunto

lunes, febrero 04, 2008

(Otros) problemas con la memoria y (otras) soluciones

Alguien en programming.reddit hizo una pregunta de esas difíciles: ¿Cómo manejar en un programa en C los casos en los que la memoria se agota? Y digo que es una de esas preguntas difíciles porque depende del tipo de software que estás haciendo, de su criticidad y del nivel requerido de robustez. Ya se sabe, el difícil compromiso de la gestión de errores críticos.

Y como se ve en las respuestas de los redditenses no sólo depende de nuestro sistema, sino de detalles de implementación de más abajo, como suele ser usual en los casos no del todo raros en los que las abstracciones que usamos comienzan a flaquear. En este caso se habla del comportamiento de Linux (el kernel) a la hora de tratar la falta de memoria, que por defecto usa una estrategia optimista que deja reservar (pero no usar, claro) más memoria de la disponible. No obstante este comportamiento se puede cambiar a uno un poco más controlable a través del parámetro overcommit_memory, que se introdujo no hace tanto... El comportamiento por defecto es llamar al OOM Killer que es un método tan drástico como poco predecible. Todo esto esta muy bien explicado en When Linux Runs Out of Memory que vi referenciado por aquí en tiempos de mayor intensidad técnica :)

Además en las respuestas se apunta a un libro online de los que vienen bien cuando las condiciones son más extremas de lo usual: Small Memory Software. Patterns for systems with limited memory. Apuntado en éste mi del.icio.us particular.

(El título es "(Otros) problemas con la memoria y (otras) soluciones " porque no hace mucho escribí Problemas de memoria (y algunas soluciones), sobre el cuello de botella que supone la gestión de memoria sobre todo en sistemas multicore)

"(Otros) problemas con la memoria y (otras) soluciones" en barrapunto

miércoles, marzo 28, 2007

VLC se pasa a Qt

Leo a través de una noticia sobre el reproductor multimedia VLC en OSNews, VLC: Beyond the Basics, que VLC va a dejar de usar wxWidgets para pasarse a Qt. Las razones que se aducen para el cambio son sobre todo de localización: problemas con lenguajes escritos de derecha a izquierda y problemas con unicode, aunque otra razón es para probar algo nuevo :) La elección ha sido Qt. Como comenta el desarrollador de VLC tema da para muchísimos flames, pero ¿Que librería de interfaces gráficos de usuario multiplataforma usas? ¿Por qué?

VLC se pasa a Qt en barrapunto