Novedades de C++17

Después de tres años de trabajo, C++17 ha sido finalmente estandarizado. Esta nueva versión de C++ incorpora y elimina elementos del lenguaje, con el fin de ponerlo al día y convertirlo en un lenguaje moderno y eficaz. El comité ISO de C++ se ha tomado muy en serio su labor, C++11 supuso este cambio de mentalidad, que se ha mantenido en C++14 y ahora en C++17, la última versión de C++.

Repasemos las noveades que incorpora C++17 respecto a C++14

if-init

Ahora podemos incluir una sentencia de inicialización antes de la condición en sentencias if y switch. Esto es particularmente útil si queremos operar con un objeto y desconocemos su validez.

Declaraciones de descomposición

Azúcar sintántico que permite mejorar la legibiliad en ciertas situaciones. Por ejemplo, en el caso de las tuplas, su uso se vuelve trivial.

Esto funciona para multitud de estructuras de datos, como estructuras, arrays, std::array, std::map,…

Deduction Guides

Ahora es menos necesario que nunca indicar los tipos en ciertas expresiones. Por ejemplo, al crear pares y tuplas:

Esto por supuesto también sirve para estructuras y otras construcciones:

template auto

Fold expressions

Imagina que quieres hacer una función suma, que admita un número ilimitado de parámetros. En C++17 no se necesita apenas código.

Namespaces anidados

Bastante autoexplicativo

Algunos [[atributos]] nuevos

[[maybe_unused]]

Se usa para suprimir la advertencia del compilador de que no estamos usando una determinada variable.

[[fallthrough]]

Permite usar los switch en cascada sin advertencias del compilador.

Variables inline

Ahora es posible definir variables en múltiples sitios con el mismo nombre y que compartan una misma instancia. Es recomendable definirlas en un fichero de cabecera para luego reutilizarlas en ficheros fuente.

if constexpr

Ahora es posible introducir condicionales en tiempo de compilación (similar a las macros #IFDEF pero mejor hecho). Estas expresiones con constexpr, lo que quiere decir que son código C++ que se evalúa en tiempo de compilación, no de ejecución.

std::optional

Tomado de la programación funcional, se incorpora el tipo optional, que representa un valor que puede existir o no. Este tipo ya existe en Rust bajo el nombre de Option y en Haskell como Maybe.

std::variant

Descritas como las unions pero bien hechas. Pueden contener variables de los tipos que nosotros indiquemos.

std::any

Si con std::variant restringimos los posibles tipos de la variable a los indicados, con std::any admitimos cualquier cosa.

std::filesystem

Se añade a la librería estándar este namespace con el tipo path y métodos para iterar y operar con directorios. Dile adiós a las funciones POSIX o Win32 equivalentes.

Algoritmos en paralelo

Muchos de los algoritmos de STL ahora pueden ejecutarse en paralelo bajo demanda. Con std::execution::par indicamos que queremos que el algoritmo se ejecute en paralelo.

¿Qué novedades se esperan en C++20?

Ya hemos visto lo que trae C++17. Ahora veremos que se espera que traiga C++20 en 2020.

  • Módulos. Reemplazar el sistema de includes
  • Corrutinas. Mejorar la programación asíncrona
  • Contratos. Mejorar la calidad del código
  • Conceptos. Mejorar la programación genérica
  • Redes. Estandarizar la parte de red en C++ tal y como se ha hecho con std::filesystem
  • Rangos. Nuevos contenedores

Referencias:

 

Programando para Haiku – Barras de menús – Parte III

Seguimos con los tutoriales de Haiku, hoy veremos como crear barras de menú. Partimos del código de la ventana vacía, lo tienes en la foto junto con el comando de compilación.

MiAplicacionHaikuCodigo
Haz click en la imagen para verlo en grande

Debemos saber que en la API de BeOS hay tres clases que nos van a servir.

  • BMenuBar, se trata de la barra en sí, que esta pegada a la ventana. Un BMenuBar contiene BMenus.
  • BMenu, son los menús en sí. Un menú es un elemento que contiene acciones, organizadas como si fuera una lista. Los menús no definen acciones, pero sí su organización. Así, a un BMenu le tendremos que añadir BMenuItems u otro BMenus (menús anidados).
  • BMenuItem, son las acciones de los menús. Cada BMenuItem tiene la capacidad de generar un BMessage nuevo, listo para ser procesado por nuestra aplicación.

En la imagen se puede ver perfectamente

BMenu

Vamos a crear una barra de menú con dos menús, uno de ellos simple y otro con un menú anidado y un separador.

Y el resultado es el siguiente:

BMenuBar

Como vemos, SetShortcut hace que los menús sean seleccionables con combinaciones de teclado. Hay que tener en cuenta que en BeOS, la tecla que en Windows normalmente Ctrl, es Alt. Así operaciones como copiar y pegar con Alt+C y Alt+V. Para responder al evento solo hace falta escuchar en la función MessageReceived. En el caso de B_QUIT_REQUESTED, el mensaje ya está implementado en la función QuitRequested.

Programando para Haiku – File panel – Parte II

Continuamos los tutoriales de programación en Haiku. Hoy veremos como usar el File Panel. ¿Qué es el File Panel? El File Panel es el diálogo que nos aparece cuando queremos abrir o guardar un archivo o carpeta. En todos los sistemas operativos gráficos es similar.

File Panel de Windows

BFilePanel

La clase básica es BFilePanel. Esta clase se encarga de mostrar esa ventanita que nos deja elegir el archivo o carpeta para abrir o para guardar. Lo primero que tenemos que saber si queremos hacer un File Panel es si va a ser para abrir un archivo existente o para guardar un nuevo archivo. Así, distinguimos entre B_OPEN_PANEL y B_SAVE_PANEL. Si estamos dentro de un B_OPEN_PANEL además indicaremos si aceptamos archivos, carpetas, enlaces simbólicos o alguna combinación de estas cosas. Por último, ¿cómo recibimos la información del panel? Pues usando BMessage, como es habitual en Haiku/BeOS. Pero hay que indicar quién va procesar el mensaje, el conocido como BMessenger. Veamos código:

En este código creamos un file panel para abrir un archivo. El BMessenger encargado de procesarlo será el del mismo contexto en el que se ejecute este código. Hay que tener en cuenta que tanto BApplication como BWindow heredan de BMessenger y por tanto cualquier objeto de estas clases es apto. El siguiente parámetro es la carpeta por defecto, que con NULL la dejamos a elección de Haiku. Luego indicamos que queremos abrir archivos, no carpetas ni enlaces simbólicos. Por último especificamos el ID del BMessage que enviará el panel. Esto nos servirá para después saber que ID tenemos que leer dentro de la función MessageReceived del BMessenger. Por último mostramos el panel para que el usuario decida el archivo a abrir. Si la acción es cancelada también será disparado el mensaje, tendremos que comprobar si el usuario eligió el archivo o cerró el diálogo.

Haiku File Panel

Leer la respuesta

Dentro de la función MessageReceived del BMessenger tenemos que accionar un caso especial si el ID del BMessage es el que hemos especificado en el panel.

Tenemos que comprobar si el mensaje tiene la propiedad “refs”. La propiedad “refs” la ajusta el File Panel cuando se ha seleccionado un archivo. Si la propiedad existe entonces lo leemos. Leeremos una entryref. Un entryref es una entrada dentro del sistema de archivos. Sin embargo esta estructura es de bajo nivel y no sabe exactamente donde se ubica. BEntry representa localizaciones dentro del sistema de archivos. Se construye con un entry_ref y esta clase ya sabe donde se ubica de forma legible por un humano (o un programador perezoso). Si queremos saber la ruta del archivo antes tendremos que crear un objeto vacío BPath que llenaremos con contenido. Finalmente la ruta, como string, la podremos leer llamando a la función Path dentro del objeto BPath.

Ya hemos visto como se usan los file panel en Haiku. Los file panel de guardar archivo se programan exactamente igual cambiando esa pequeña flag al principio.

Programando para Haiku – BApplication, BWindow y BButton – Parte I

Hoy voy a comenzar una serie de tutoriales donde explicaré la programación de una apliación en Haiku. Para ello vamos a usar C++ y la BeAPI. Siempre que tengais cualquier duda podéis visitar la documentación oficial: tanto la antigua de BeOS en el BeBook y la nueva de Haiku en Haiku API. Aun así hay APIs nuevas que todavía no aparecen documentadas. En ese caso hay que recurrir al código fuente.

Librerías en Haiku

Haiku (y BeOS) comparte con UNIX muchas características de bajo nivel. El tema de las librerías es uno de ellos. También han de empezar por lib y terminar por .so si son compartidas y .a si son estáticas. Para compilar también se usa GCC. Sin embargo hay una pequeña diferencia con el resto de sistemas UNIX. En UNIX normalmente disponemos de una librería del C, libc y una librería de funciones matemáticas, libm. En Haiku no existe libm, en cambio existen muchas más, libroot y libbe, para interactuar con el sistema de manera básica, libtracker, con funciones relacionadas con el explorador de archivos, libnetwork y libnetapi, con funciones de red, y muchas otras.

Además la API se divide en Kits, cada Kit se encarga de una tareas diferentes dentro del sistema operativo. AppKit, Game Kit, Interface Kit, Media Kit, Storage Kit, etc… Si queremos usar la funcionalidad de un kit tendremos que revisar que hemos añadido la librería correcta al compilador y que hemos añadido #include dentro del código que lo usemos.

Un hola mundo

Vamos a empezar por lo simple, una aplicación que muestre una ventana y ya.

Creamos un archivo de C++, será el punto de inicio de nuestra aplicación. Como sabéis, el punto de inicio de un programa de C o C++ es la función main.

Hemos creado un objeto llamado app del tipo AplicacionPrueba y después hemos ejecutado la aplicación. AplicacionPrueba tiene que ser del tipo BApplication. Es la clase básica de todas las aplicaciones Haiku/BeOS. BApplication provee de mensajería entre los distintos procesos del programa (hay que tener en cuenta que BeOS se diseñó pensando en el multiproceso). Vamos a ver como definimos AplicacionPrueba

Las Application necesitan un MIME type, al igual que se usa para indicar los tipos de archivo. No es necesario que sea real. Además hemos creado un objeto VentanaPrueba y la mostramos. VentanaPrueba es del tipo BWindow y es la ventana básica de Haiku, lo que vemos. Veamos la definición:

BWindow necesita un tamaño, que es indicado con BRect, un título, un estilo (por defecto es BTITLEDWINDOW, pero podemos tener ventanas sin bordes o modal) y opciones varias. En las opciones varias podemos especificar que al cerrar la ventana se cierre la aplicación (BQUITONWINDOWCLOSE), que el usuario no pueda cambiar su tamaño (BNOTRESIZABLE), que no se pueda minimizar (BNOTMINIMIZABLE) y otras opciones por el estilo.

Además dentro de la clase hemos definido dos funciones virtuales, es decir, que tienen implementación por defecto de la clase padre, BWindow, pero nosotros podemos modificar su comportamiento.

QuitRequested es llamada cuando algo pide el cierre de la ventana. El objeto global beappmessenger es del tipo BApplication, pero está definido en todos los puntos de nuestra aplicación sin que nosotros hagamos nada. Gracias a este objeto podemos enviar mensajes entre procesos. En este caso enviamos el mensaje a la aplicación de BQUITREQUESTED. Y luego llamamos a la función sin modificar.

MessageReceived es muy importante. Se encarga de procesar todos los mensajes que recibe la ventana. Para distinguir los mensajes (que son del tipo BMessage) tenemos que inspeccionar la propiedad what. Se trata de un valor de tipo uint32. Hay algunos ya definidos por el sistema como BQUITREQUESTED pero nosotros podemos definir más. Veremos más tarde como. De momento simplemente devolvemos el procesado de mensajes a BWindow padre.

Con esto ya podemos compilar.

Añadiendo BView, BButton y BGroupLayout

Ahora vamos a añadir cosas a nuestra ventana sosa. Ponemos una vista dentro de la ventana. Las vistas en Haiku son muy potentes pero eso lo trataré en otro momento. A esa vista le añadiremos un botón.

Aquí hemos hecho varias cosas. Por una parte he creado un layout horizontal. Es decir, dispongo el espacio de la ventana de manera horizontal, según se vayan añadiendo elementos lo harán a la derecha. Esto no estaba en BeOS y es particular de Haiku, pero recomiendo usar este sistema pues permite realizar un responsive design. Creamos una vista o panel. Bounds() indica que cubra todo el espacio disponible. El resto son propiedades de la vista más o menos estándar. Con SetViewColor le podemos poner un color de fondo, y con SetLayout le aplicamos el layout previamente creado.

Creamos un botón, que es del tipo BButton. BButton tiene muchos constructores si revisais la documentación pero este es muy cómodo si usamos el sistema de layouts. Simplemente indicamos el texto que va a mostrar y el mensaje que envía. En este caso NULL pues no vamos a poner ninguno.

sizer->AddView() lo usamos para añadir el botón al layaout y AddChild para añadir la vista a la ventana. Puedes compilar.

Añadiendo eventos. Mensajería con BMessage. Diálogo con BAlert.

Vamos ahora a crear un evento para el botón. Cuando pulsemos el botón mostrará un mensaje al usuario.

Los eventos se realizan por el sistema de mensajería basado en BMessage y BHandler. Para crear un BMessage necesitamos un ID, que es del tipo uint32. Eso es lo mínimo y con eso ya serviría para este caso.

Pero los mensajes pueden llevar información adicional de cualquier tipo. Por ejemplo si queremos añadir además una cadena de texto al mensaje usaremos AddString.

Podremos recuperar el valor en cualquier momento con FindString.

Ahora si vamos a MessageReceived podemos añadir código que gestione este tipo de mensaje.

Con un simple case gestionamos el mensaje. Para mostrar un diálogo simple se puede usar BAlert. Es muy simple, indicamos el título, el contenido del mensaje y el texto del botón que aparecerá. Y con Go lo mostramos.

Esta ha sido la primera parte del tutorial. Os ha gustado. Hay algo que no haya quedado claro. Comentádmelo.

Mi primer debug. Primeros pasos con gdb, Valgrind y strace.

¿A quién no le ha pasado? Estas programando en C++ y de repente cuando antes todo iba bien, ahora el programa se cierra inesperadamente (un crash) y no sabes el motivo. En algunos lenguajes como Rust, el propio compilador y el lenguaje evitan estas situaciones, pero en C++ la situación es mucho más estimulante.

Recientemente, trabajando en Kovel tuve uno de estos incidentes inesperados. Pero más inesperada fue su aparición, pues en Debian, donde programo actualmente, el programa se ejecutaba normalmente. Sin embargo en Windows el programa no llegaba a arrancar. Pensé que sería una diferencia Linux-Windows pero al probar en Fedora ocurrió lo mismo que en Windows, no llegaba a arrancar. Si encontraba el fallo en Fedora, que no se daba en Debian, resolvería también el fallo en Windows.

Preparando la aplicación y el entorno

Símbolos de depuración

Aunque no es obligatorio, es recomedable compilar los ejecutables que vayamos a someter a depuración con símbolos de depuración. En Windows se usan archivos independientes (ficheros PDB) mientras que en Linux se usan los mismos ejecutables con más metadatos en su interior. En GCC simplemente hay que añadir la opción -g para retener los datos de depuración.

Ficheros core

Ahora sería conveniente activar la generación de los ficheros core en el sistema. En algunas distro ya está activado:

Los ficheros core los usaremos si nuestra aplicación se paró en un punto de difícil acceso o que no podemos recrear nosotros mismos.

Instalar gdb, Valgrind y los símbolos de las librerías

Ahora vamos a instalar el componente más importante, el debugger, la aplicación que usaremos para analizar la ejecución del programa.

gdb

Además querremos tener los símbolos de depuración de las bibliotecas que use nuestro ejecutable. Con DNF, en Fedora, el proceso usa un comando específico:

Y si queremos mantener los símbolos de depuración actualizados:

Vamos a usar Valgrind también, aunque menos

Cazando al vuelo

Supongamos que sabemos como generar el error. Llamamos a nuestro programa desde gdb:

Entraremos en gdb, con su propios comandos de herramientas. Lo primero que haremos será iniciar el programa, con el comando run o r

El programa se iniciará. Nosotros provocaremos el error. Una vez lo hayamos provocado podremos introducir más comandos. Vamos a ver que pasos se han seguido para producir el error.

Y desde aquí podemos inspeccionar que funciones fueron llamadas justo antes de que el programa petase. En este punto también podemos buscar el valor de ciertas variables que nos interesen con p nombrevariable.

Volviendo al pasado

No sabemos como se produjo el error, pero tenemos un fichero core que nos va a permitir restablecer la situación del pasado para poder analizarla. Llamamos a gdb con el fichero core y nuestra aplicación.

Una vez dentro podemos dirigirnos al punto crítico.

Y analizamos como antes.

Valgrind y fugas de memoria

Valgrind es muy usado para comprobar en que partes nuestro programa tiene fugas de memoria. En determinados casos puede ser más útil que gdb.

Nuestro programa se ejecutará aproximadamente 20 o 30 veces más lento, pero se nos informará en todo momento de la gestión errónea de memoria que está produciéndose. En alguna situación será interesante saber de donde provienen estos fallos con mayor precisión, la opción –track-origins=yes es nuestra amiga.

Valgrind es muy estricto y puede generar falsos positivos. Hay varias GUI disponibles para Valgrind, una de ellas es KCacheGrind.

KCacheGrind

Otra de ellas es Valkyrie

Valkyrie

¿Y si algún fichero no existe?

Para terminar vamos a suponer que nuestro programa falla porque hay un archivo que no logra encontrar y no puede abrirlo. Gracias a strace es posible saber que archivos está abriendo el programa.

Y nos saldrá en tiempo real los archivos que ha abierto nuestro programa.

Strace

Y espero que con este pequeño resumen ya sepais que hacer cuando vuestro programa se cierra inesperadamente.