Categorías de valor. Referencias lvalue y rvalue

Artículos de la serie:

Categorías de valor en C++


Una expresión es una secuencia de operadores y operandos que especifica un cómputo. Su evaluación puede dar lugar a un valor (por ejemplo, 2*5 genera 10) y/o producir efectos secundarios (fmt::print("{}\n",10) imprime 10 en la consola).

En los estándares C++11 y posteriores, toda expresión es clasificada de acuerdo a la siguiente taxonomía:

Expresiones lambda (I)

Última actualización: 30 de agosto de 2020.

Artículos de la serie:

Objectos función (function objects)


Antes de poder entender qué son y cómo funcionan las expresiones lambda, es preciso que comprendamos el concepto de objeto función.

En el lenguaje C++ las clases pueden sobrecargar el operador llamada a función operator() con el fin de dotar a sus objetos del comportamiento propio de las funciones tradicionales. A modo de ejemplo, consideremos la plantilla estándar std::greater<> proporcionada en el fichero de cabecera <functional>, destinada a realizar la comparación de dos valores dados a través del operador "mayor estricto que" operator>. Una posible implementación de esta plantilla en C++20 vendría dada por:

namespace std {      template<typename T>     struct greater {        constexpr auto operator()(T const& x, T const& y) const -> boolreturn x > y; }     }; }

Expresiones lambda (III)

Artículos de la serie:
 

Expresiones lambda: sintaxis y uso combinado con algoritmos


Los estándares del lenguaje C++11 y posteriores proporcionan las denominadas expresiones lambda con el fin de facilitar la creación de objetos función potencialmente capaces de capturar variables de su entorno. Su utilidad fundamental consiste en la definición in situ de acciones específicas a realizar por parte de los algoritmos. Así, por ejemplo, la eliminación de los valores pares en un vector de enteros puede realizarse de dos formas alternativas:

Expresiones lambda (II)

Artículos de la serie:

Objetos función con estado interno


Los objetos función extienden la utilidad de las funciones tradicionales gracias a la posibilidad de poseer estado interno y proporcionar operaciones adicionales.

A modo de ejemplo, consideremos el problema sencillo de calcular la media aritmética de un conjunto finito de valores numéricos almacenados en coma flotante X = {x1, x2, ... , xn}:

 X = (x1 + x2 + ... + xn)/n.

Pattern matching en C++

Introducción


Fuente: https://commons.wikimedia.org/wiki/File:Jigsaw.svg
Licencia: Public Domain
La técnica de programación conocida como coincidencia de patrones (pattern matching) --de uso común en lenguajes como C#, Haskell, Mathematica, Scala, Swift o Rust-- constituye una herramienta versátil con la que poder extraer información de una serie de tokens según su estructura o forma. En esta técnica, una expresión es comparada con una lista de patrones (patterns) con el fin de que, de producirse una coincidencia (match), pueda extraerse información útil de la misma. La semántica empleada es la de primera coincidencia (first match), y no la de mejor coincidencia (best match). El esquema general a seguir toma la forma:

       inspeccionar <expresión>:
          <comparar_patrón_1> <acción_a_realizar_1>
          <comparar_patrón_2> <acción_a_realizar_2>
          ...

Always auto: Una sintaxis moderna para C++

Artículos de la serie:

Introducción


En sus más de treinta años de evolución, C++ ha adquirido estilos diversos con los que inicializar sus variables, hasta el punto de no existir un consenso definitivo en su comunidad de desarrolladores acerca de cuál resulta preferible. Sin embargo, tras los últimos estándares del lenguaje (particularmente C++17) podemos observar una clara tendencia a adoptar una sintaxis declarativa de izquierda-a-derecha, en sintonía con lenguajes más recientes como Go, Rust o Swift.

Punteros: Qué son y cómo trabajar con ellos

Última actualización: 28 de julio de 2020.

Definiciones básicas


Al trabajar con el lenguaje C++, el empleo de punteros resulta inevitable, ya sea para introducir semántica de referencia en nuestros códigos, alojar objetos en el free store, emplear polimorfismo dinámico u operar con estructuras dinámicas de datos y sus iteradores. Familiarizarse con este tipo de variables, hasta el punto de convertir su manipulación en una tarea natural para el programador, requiere esfuerzo y numerosas horas de práctica. Sin embargo, los conceptos básicos involucrados en su aprendizaje resultan extremadamente simples.

Un puntero es una variable que almacena la dirección en memoria de otro objeto. Como tal, el espacio ocupado en memoria por un puntero (independientemente del tipo de objeto que referencie) coincide con el número de bytes necesario para especificar una dirección de memoria: 4 bytes en la arquitectura x86, 8 bytes en la arquitectura x86-64.

Reflexión estática y serialización de agregados en JSON

Introducción


Le Penseur - Auguste Rodin
Nota: Este artículo actualiza un post originalmente publicado en 2015 acerca de la gestión de ficheros. Esta nueva versión analiza la codificación/descodificación de agregados de datos mediante la biblioteca JSON for Modern C++, así como la introducción de reflexión estática mediante Boost.Hana con el fin de simplificar la tarea del programador.



La transformación de un agregado de datos diversos en un formato legible de intercambio como JSON es una acción recurrente para la que existen múltiples soluciones en C++. Según las necesidades del programador, disponemos de una amplia variedad de bibliotecas de serialización con distintos grados de adaptación a la biblioteca estándar y niveles de rendimiento. Puedes consultar un benchmark exhaustivo a este respecto en el siguiente portal:

https://github.com/miloyip/nativejson-benchmark

std::variant - Polimorfismo sin herencia

Introducción


Última actualización: 6 de agosto de 2020

Como analizamos en un post anterior, la plantilla de clase std::variant<>, incluida por vez primera en el estándar C++17, proporciona la funcionalidad típica de las uniones de C, si bien garantizando la seguridad de tipos. Así, una instancia de std::variant<> podrá contener, en un instante dado, un objeto perteneciente a uno de varios tipos alternativos permitidos, o ningún valor en caso de error. Por ejemplo, una instancia de std::variant<char, int, double> permitirá almacenar un carácter char, un entero int o un double, a conveniencia del programador y sin incurrir en alojamientos dinámicos de memoria.

Puedes consultar algunos ejemplos de uso simples en la siguiente referencia:

https://en.cppreference.com/w/cpp/utility/variant

C++ en macOS - Integrando VSCode, Homebrew y CMake

Última actualización: 2 de octubre de 2023.

Introducción


En este post, continuación de una serie anterior dedicada a MS Windows, explicaremos cómo configurar un entorno de trabajo para proyectos de C++ en macOS basado en:
  • Homebrew [1] como package manager, a través del cual podremos instalar bibliotecas no-estándar de uso extendido en la comunidad de desarrolladores en C++ –tales como Boost [2], {fmt}lib [3], Range-v3 [4] y nlohmann-JSON [5]–, build systems como Ninja [6] y, opcionalmente, el compilador GCC.
  • Visual Studio Code [7], un editor gratuito y de código abierto –si bien la descarga oficial se realiza bajo una licencia de software propietario– desarrollado desde 2015 por Microsoft para los sistemas Linux, MS Windows y macOS. Proporciona, entre otras funcionalidades, un alto grado de configuración por parte del usuario, resaltado de sintaxis, soporte para debugging, autocompleción e información de código (IntelliSense) y control integrado de Git. Fue declarado el entorno más popular entre desarrolladores en el Stack Overflow Developer Survey de 2021.
  • CMake [8] como herramienta de construcción, prueba, distribución e instalación de proyectos ampliamente extendida en el ámbito de la programación profesional en C y C++. Nos permitirá controlar el proceso de construcción de nuestros proyectos (build process) mediante ficheros CMakeLists.txt independientes de nuestro build system nativo (como Make o Ninja).

Magnitudes físicas: El problema de la conversión de unidades

Introducción


El desgraciado error de conversión de unidades que condujo en 1999 a la desintegración de la sonda Mars Climate Orbiter de la NASA es un ejemplo bien conocido de la necesidad de garantizar que nuestro software sea fuertemente tipado, así como del peligro de emplear un mismo tipo primitivo básico para representar magnitudes diferentes y/o medidas de una única magnitud expresadas en unidades distintas.

Integrando VSCode, Mingw-w64 y CMake (Parte IV)

Artículos de la serie:

Depuración en VSCode mediante GDB


En este post analizaremos brevemente el depurador estándar ofrecido por el compilador GCC (el conocido GNU Debugger, abreviado simplemente como GDB) en combinación con el editor VSCode. Asumiremos que hemos realizado ya la instalación del compilador Mingw-w64, así como del editor VSCode y sus extensiones para C++, tal y como explicamos en el primer post de esta serie.

Docencia en C++: Breve introducción al lenguaje

Motivación


Durante 2018 se constituyó, dentro del proceso de estandarización del lenguaje ISO C++, el subgrupo de trabajo SG20 (C++ Education Study Group) focalizado en proporcionar directrices curriculares y de enseñanza para C++.

El documento de trabajo P1389R0 publicado por dicho grupo (disponible en http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1389r0.html) anima a integrar herramientas básicas de programación en los cursos introductorios de C++, tales como package managers, build systems y debuggers, entre otras. Nuestra serie de posts anterior, centrada en la integración de Visual Studio Code, Mingw-w64 y CMake en el entorno MS Windows, responde precisamente a esta directriz.

Asimismo, el documento P1389R0 sugiere facilitar a nuestros estudiantes una breve introducción acerca del lenguaje en la que se aprecien sus fortalezas y casos de uso moderno, pero que huya de comparaciones innecesarias con el lenguaje C que puedan llevar a considerar C++ un mero 'C con clases', un error muy extendido incluso en nuestros días. En efecto, aun cuando muchas fuentes se refieren a C/C++ como una unidad temática, ambos lenguajes son de hecho radicalmente diferentes, hasta el punto de que introducir C como primer paso en un curso en C++ puede resultar dramáticamente contraproducente (un punto éste analizado de forma muy convincente en CppCon 2015: Kate Gregory 'Stop Teaching C'https://youtu.be/YnWhqhNdYyk). Este post pretende proporcionar una presentación al lenguaje en la línea sugerida por P1389R0.

Integrando VSCode, Mingw-w64 y CMake (Parte III)

Artículos de la serie:

Instalación de bibliotecas no-estándar


En el post anterior se analizó la incorporación de extensiones en nuestro editor VSCode para hacer posible la construcción de proyectos mediante CMake.

En este nuevo artículo explicaremos cómo instalar bibliotecas no-estándar en nuestro sistema y su incorporación en proyectos de CMake. Son varias las bibliotecas de uso extendido en la comunidad de C++ cuya inclusión temprana resulta imprescindible en un curso introductorio sobre este lenguaje. Entre ellas, cabría señalar las siguientes:

Integrando VSCode, Mingw-w64 y CMake (Parte II)

Artículos de la serie:

Construyendo un proyecto 'Hello, world!' con CMake


Este post es una continuación del artículo anterior de la serie, en el que analizamos la integración del editor VSCode, el compilador GCC y el generador de build systems CMake para la gestión de proyectos de C++ en MS Windows. CMake empleará ficheros de configuración CMakeLists.txt para producir archivos de construcción específicos que puedan ser utilizados por la herramienta de construcción nativa de nuestra plataforma (Make o Ninja, por ejemplo).

Integrando VSCode, Mingw-w64 y CMake (Parte I)

Última actualización: 3 de septiembre de 2023

Artículos de la serie:

Introducción


Esta serie de posts se centrará en la instalación y configuración de un entorno de trabajo para proyectos en C++ bajo MS Windows (7 o posterior, 64-bit) basado en:
  • Visual Studio Code (https://code.visualstudio.com/), un editor gratuito y de código abierto –si bien la descarga oficial se realiza bajo una licencia de software propietario– desarrollado desde 2015 por Microsoft para los sistemas Linux, MS Windows y macOS. Proporciona, entre otras funcionalidades, un alto grado de configuración por parte del usuario, resaltado de sintaxis, soporte para debugging, autocompleción e información de código (IntelliSense) y control integrado de Git. Fue declarado el IDE más popular entre desarrolladores en el Stack Overflow Developer Survey de 2022.
  • Mingw-w64 (https://mingw-w64.org/), un entorno de desarrollo de software para MS Windows que incluye un puerto de los compiladores GCC (https://gcc.gnu.org/). Contiene un subconjunto de la API de Windows, lo que permite el desarrollo de aplicaciones nativas tanto de 32 como de 64 bits. Realizaremos su instalación a través de la plataforma MSYS2.
  • CMake (https://cmake.org/), una herramienta de construcción, prueba, distribución e instalación de proyectos ampliamente extendida en el ámbito de la programación profesional en C y C++. Nos permitirá controlar el proceso de construcción de nuestros proyectos (build process) mediante ficheros CMakeLists.txt independientes de nuestro build system nativo (como GNU Make o Ninja).

C++17: Diseñando un verificador variádico de contraseñas

Introducción


El objetivo de este post es el de diseñar un verificador de contraseñas que permita combinar distintas políticas de validación para las claves. Como caso base, nuestro verificador comprobará que la longitud de una contraseña pertenezca a un rango permitido. En situaciones de mayor interés, podremos también incluir reglas de validación adicionales que comprueben la presencia de letras minúsculas y mayúsculas, dígitos y/o símbolos no-alfanuméricos. Para ello, haremos uso de expresiones regulares (regex) y plantillas variádicas (variadic templates [1]) introducidas en el estándar C++11, así como de los denominados fold expressions proporcionados por C++17 [2]. Emplearemos, asimismo, la biblioteca {fmt}lib --en proceso de estandarización para C++20-- con el fin de facilitar el formato de texto [3].

C++20: Nuevas características del lenguaje

C++ se encuentra en constante evolución. El primer proceso de estandarización internacional del lenguaje (ISO C++ 1998-2003) permitió la incorporación de técnicas de programación genérica y, con ellas, el desarrollo de una biblioteca estándar en la que algoritmos, estructuras de datos e iteradores se interrelacionaban de forma natural y eficiente.

Los estándares posteriores C++11/14/17 han proporcionado mejoras sustanciales al núcleo del lenguaje (inferencia automática de tipos, semántica de movimiento, inicialización uniforme, expresiones constantes, funciones lambda, etcétera), así como una notable extensión de la biblioteca estándar mediante la inclusión de punteros inteligentes, concurrencia, sistema de ficheros, expresiones regulares, tipos variantes y un largo etcétera.

El comité de estandarización se ha reunido recientemente en Kona (Hawaii, USA, Feb 18-23 2019) para concluir la especificación de C++20 [1]. El nuevo estándar promete revolucionar el modo en que pensamos y expresamos nuestro código, a la par que permanece fiel a los principios fundacionales de C++ (eficiencia, empleo de abstracciones de sobrecoste cero, retrocompatibilidad). Este post ofrece un breve adelanto de algunas de las principales funcionalidades incluidas en esta nueva versión del lenguaje:
  1. Módulos.
  2. Conceptos.
  3. Corrutinas.
  4. Contratos [Nota del autor: Esta funcionalidad fue retirada del estándar en el encuentro de Cologne en julio de 2019 para su reevaluación por parte del subgrupo de trabajo SG21].
  5. Rangos.

Simplificando el empleo de std::cin

Última actualización: 16 de octubre de 2021

Introducción: Gestión del flujo de entrada estándar


El objeto global std::cin de la clase std::istream, proporcionado en la cabecera <iostream>, controla la entrada de datos desde un búfer de flujo asociado con el objeto stdin, propio del lenguaje C y declarado en <cstdio>. Sus siglas significan character input y es una de las las principales herramientas --junto a std::cout-- empleadas en cualquier curso introductorio del lenguaje. El objetivo de este artículo es el de diseñar una biblioteca que facilite el empleo de std::cin en el ámbito de la enseñanza. Asumiremos aquí el caso tradicional en el que los objetos std::cout y std::cin representan, respectivamente, un flujo de salida y de entrada para la terminal.

El uso de flujos estándar I/O en C++ es poco eficiente en algunos contextos, aun cuando proporcionen una herramienta extensible con seguridad de tipos. Su modo de empleo adolece también de cierta complejidad, si bien el nuevo estándar del lenguaje C++20 incluye una función std::format() que facilita enormemente el formato de texto y la inserción de datos [1]. Como alternativa al uso de flujos de salida estándar, el programador puede recurrir a la biblioteca {fmt}lib, que en buena medida hace innecesario el empleo de objetos std::ostream (incluyendo std::cout) y funciones como std::printf() y std::fprintf() [2].

En relación a la extracción de datos, existe una propuesta de estandarización para C++23 que facilita el parseo de texto y que está encaminada a remplazar el uso tradicional de flujos std::istream (incluyendo std::cin) y funciones como std::scanf [3, 4]. Nuestro estudio será, por supuesto, menos ambicioso, limitándose a diseñar una biblioteca que simplifique el uso de std::cin en los primeros pasos de aprendizaje del lenguaje. En efecto, como justificaremos a continuación, el manejo de este objeto no está exento de dificultades y un correcto tratamiento de los posibles errores de formato en los datos de entrada puede resultar inconveniente para los más principiantes.

Un ejemplo de programación con std::variant

Última actualización: 5 de agosto de 2020

Introducción


La plantilla std::variant<>, incluida por vez primera en el estándar C++17 en el fichero de cabecera <variant>, proporciona la funcionalidad típica de las uniones de C, si bien garantizando la seguridad de tipos. Así, una instancia de tipo variante std::variant contendrá, en un instante dado, un objeto perteneciente a uno de varios tipos alternativos permitidos, o ningún valor en caso de error. Por ejemplo, una instancia de std::variant<char, int, double> podrá contener un carácter char, un entero int o un double, a conveniencia del programador y sin incurrir en alojamiento dinámico de memoria:

VSCode como editor de C++

Nota del autor: El presente artículo se encuentra desactualizado. El lector puede encontrar una introducción más actual a la gestión de proyectos de C++ en VSCode en la serie de posts 'Integrando VSCode, Mingw-w64 y CMake' publicada en agosto de 2019 y revisada periódicamente desde esa fecha.

Visual Studio Code 

VSCode es un editor gratuito y de código abierto –si bien la descarga oficial se realiza bajo una licencia de software propietario– desarrollado desde 2015 por la compañía Microsoft para las plataformas Linux/MS Windows/macOS. Proporciona, entre otras funcionalidades, un alto grado de configuración por parte del usuario, resaltado de sintaxis, soporte para debugging, autocompleción e información de código (IntelliSense) y control integrado de Git. Fue declarado el entorno más popular entre desarrolladores en el Stack Overflow 2018 Developer Survey.

En este artículo, describiremos brevemente cómo configurar un proyecto de C++ en VSCode en el caso específico de MS Windows. Los pasos a dar en otros sistemas operativos serían análogos. Asumiremos que nuestro equipo dispone de un compilador mingw-w64 actualizado (con soporte para C++17) y que éste se encuentra incluido en el PATH del sistema.

Gestión de memoria (IX)

Como continuación de un post anterior de esta serie, en el que analizábamos la relevancia de la jerarquía de memoria en el rendimiento de nuestros procesos, vale la pena citar un segundo ejemplo analizado en numerosas publicaciones y foros de programación (ver referencias al final del artículo), consistente en la detección de los tamaños de las memorias caché a través de un buffer de memoria cuya longitud se incrementa paulatinamente. El resultado, por desgracia, suele diferir del esperado, por razones que discutiremos a continuación.