Lo peor de dos mundos, C++

Turned-on Macbook Pro

Recientemente me encontraba leyendo un artículo sobre el porqué C++ es considerado mal lenguaje para programar, y caí en la cuenta de unos  detalles adicionales que refuerzan la idea y que son los que desarrollo en este artículo.

Como siempre, debo decir que no tengo nada en particular, en contra de C++, y aunque no me considero un experto, puedo decir que lo he usado lo suficiente, así como otros lenguajes de programación.

Ahora sí, después del descargo, volvamos al punto. Los compiladores de C++ son una proeza de la ingeniería de software y han requerido mucho esfuerzo hacerlos funcionar de manera aceptable. La complejidad del lenguaje es tal son pocos los que se pueden considerar como programadores expertos de C++.

Pero ¿Es C++ tan bueno como se dice?

Si bien se le puede hacer muchas críticas al lenguaje, también tiene muchos méritos. Aquí solo quiero tocar unos cuantos puntos, relacionados a lo peor que nos ofrece C++.

C++ Lenguaje de alto nivel viviendo entre bits

¿No se suponía que C++ era de alto, o al menos medio nivel? Aunque eso es debatible. La gran comunidad de programadores consideraría a C++ como lenguaje de alto nivel, al compararlo con C y ensamblador.

Pues bien, C++ como lenguaje de alto nivel, es pobre comparado con Python, Ruby o Javascript. Lo espantoso de su sintaxis, con instrucciones como:

x = *(float*)&i;
y = ( *((type *&)(j))++ );

Difícilmente podría llamarse a esto de de alto nivel, ya que en el fondo estamos lidiando con punteros, y operaciones básicas sobre registros.

C++ es una herramienta potente y eficiente (en cuanto a código compilado, porque el compilador avanza como tortuga) y si bien no incluye características avanzadas como la recolección de basura, si tiene la facilidad de implementar librerías que puedan suplir muchas de sus deficiencias, como la falta de un tipo nativo de cadenas.

Y si bien es cierto que podemos crear capas y capas de abstracción con librerías que nos permitan trabajar al estilo de los mejores lenguajes de alto nivel; lo cierto es que siempre estaremos limitados por la expresividad del lenguaje y tendremos que convivir con punteros y operadores de bits.

Veámoslo de este modo:

¿De qué me sirve crear librerías poderosas y bien diseñadas si luego tengo que manipularlas con un lenguaje de los años 70?

¿De qué me sirve crear librerías de muy alto nivel, si luego tengo que usarlas al lado de punteros?

El problema no son los punteros, sino la sintaxis.

C++ heredó de C, prácticamente la misma sintaxis, incluyendo lo peor de lo peor, como es la horrible notación de punteros.  No es que C tenga toda la culpa. En su época había escasez de caracteres en los teclados, pero tal vez no tanto como para justificar que se use el mismo operador de multiplicación  (*) para definir punteros.

¿Por qué C usa asteriscos para punteros? Simple, porque lo copió del lenguaje B. ¿Y por qué lo usaba B? esa es otra historia, pero al parecer fue producto de un afán compulsivo de comprimir palabras a caracteres (en una época de hardware limitado) usando símbolos como operadores. Así tenemos que en C (y también en C++), los operadores lógicos son &&, || y !, en lugar de las simples y naturales palabras AND, OR y NOT que  son mucho más legibles y por lo tanto usadas en lenguajes como BASIC o PASCAL.

Si bien C pudo tener sus motivos para copiar de B (después de todo son del mismo creador y distan poco en tiempo), la notación de punteros, ¿Qué necesidad tenía C++ de hacer lo mismo, una década después?  Eso parece más consecuencia de un C-doctrinamiento fundamentalista y fanático que  de razones técnicas para mantener la compatibilidad.

En fin lo que tenemos ahora con C++ es una sintaxis de los años 70, en un compilador con características modernas, como objetos, genéricos, plantillas, y programación funcional.

Usar la triste notación de punteros de C, con los objetos de C++, trae consigo el problema de que referencias como:

(*variable).campo

sean necesarias. Pero como es tan terrible, esta notación, se creó un operador adicional con símbolos (como si no fueran ya suficientes los más de 40 operadores que tiene C++) para aliviar en algo este «problemita», y ahora se tiene el operador «->».

Hablar sobre los problemas de la notación de punteros de C/C++ es un tema extenso, y probablemente uno de los peores errores que tienen estos lenguajes (y que algunos programadores de C ignorarán o nunca se lo plantearán).

Y no es que los punteros sean el problema, sino que se pueden trabajar muy bien con ellos usando un operador que no genere confusión como bien lo hace Modula-2.

Algo triste es que el lenguaje Go, con sus grandes avances, siga arrastrando el mismo problema de usar el caracter «*» para los punteros.

Menos legibilidad da menos trabajo

La sintaxis de C/C++ suele dar más trabajo al compilador (más precisamente a los analizadores sintácticos)  por la declaración de variables, que tienen la forma <tipo> <nombre de variable>, ya que obliga a usar una tabla de símbolos para resolver posibles ambigüedades, a diferencia de los lenguajes tipo Pascal o Modula-2 (o incluso Kotlin) , que usan la forma: VAR <nombre de variable> : <tipo>.

Lo común en el mundo del diseño de lenguajes es que se sacrifique legibilidad en aras de simplificar la tarea del analizador léxico/sintáctico. Otros apuestan por mayor nivel en el lenguaje a costa de más procesamiento de CPU.

La pregunta obligatoria es ¿Qué tan legible es un código de C/C++ en cuanto a declaración de variables? Solo tomemos algunas joyitas:

int **p;  //puntero a un puntero a int
int *&p;  // p referencia a un puntero a un int.
const char * const * const p;   //????

Leer declaraciones complejas de variables, es todo un arte oscuro de los programadores de C/C++, y aunque no es inalcanzable, está claro que no es lo que se pueda decir «sencillo».

Pues bien, aquí nuevamente C/C++ toma lo peor de ambos mundos. Una sintaxis difícil de leer, para un humano, y a la vez, pesado para el mismo compilador, que además debe estar consumiendo más CPU y calor, como si la emisión de C02 no fuera ya suficiente en este pobre planeta.


4 comentarios

  1. No se pueden comparar peras con manzanas. Recuerde con cual lenguaje hicieron los interpretadores y peseudocompiladores de otros lenguajes como Java. Cualquier lenguaje es mejor que otro dependiendo de la necesidad. C es el papá de los otros lenguajes y el único papá que C tiene son los lenguages assembler. C es la mejor alternativa para desarrollar soluciones backend rápidas, limpias y eficaces en el consumo de recursos del ordenador, mas cuando se requiere poder programar a nivel de bits.

  2. Estoy tratando de hacer un compilador para un lenguaje parecido a python, el sueño guajiro es que sea igual de rápido que C/C++ pero que sea compilado, para arquitecturas ARM/x64

    le entras al proyecto ?

Dejar una contestacion

Tu dirección de correo electrónico no será publicada.


*