Buscando el camino
Hay muchas críticas que se le pueden hacer a C++: que si permite comportamiento indefinido sin avisar (incluso lo aplica en el modo de optimización más agresivo), que si ha crecido hasta límites desmesurados, que si el precio a pagar es el de unos ejecutables monstruosos... Últimamente, la crítica se ha centrado en la seguridad del manejo de la memoria. Por ejemplo, la Casa Blanca ha pedido pasarse a lenguajes más seguros, como por ejemplo C# o Rust. La NSA ha emitido recomendaciones similares sobre abandonar C y C++. Incluso la Wikipedia tiene una página dedicada a las críticas a C++. Esta crítica a C++ no ha sentado nada bien ha Bjarne Stroustrup, el creador original del lenguaje de programación C++.
En cualquier caso, todo esto lo que ha provocado es que hayan surgido nuevos lenguajes de programación para sustituir a C++, siendo los más conocidos C# y Java.
Pero esto no quiere decir que no existan muchos más, que han surgido buscando eliminar puntos de fricción de C++, simplificar la experiencia de programación, y aportar sus propias características. Notables menciones son Zig, V, o Rust.
En 2013, Bas vd Berg creó C2, una evolución del lenguaje de programación C con el objetivo de eliminar complejidad innecesaria. Por ejemplo, no presenta archivos de cabecera .h, no hay macros...
El lenguaje de programación C3 se basa en C2 para ofrecer un mismo objetivo, pero además con ciertas capacidades que lo acercan a C++. Y este es el objetivo de este artículo.
C3
El ¡Hola, mundo! en este lenguaje de programación es como sigue a continuación.
import std::io;
fn void main()
{
io::println("¡Hola, mundo!");
}
Para compilarlo, aunque podríamos haber creado un proyecto completo, podemos simplemente hacer:
$ c3c compile holamundo.c3
$ ./holamundo
¡Hola, mundo!
Las diferencias sintáticas con C son muy pocas: las funciones van ahora precedidas de fn, de manera que se distingan facilmente de cualquier otra estructura. No hay archivos de cabecera, sino verdaderos módulos que son utilizados mediante import. El significado, la semántica del programa, es mucho más evidente que en C.
El siguiente es un programa que trata de generar ternas pitagóricas a partir de números aleatorios.
import std::io;
import std::time;
import std::core::string;
import std::collections::list;
import std::math::random;
struct Terna {
ulong a;
ulong b;
ulong c;
}
fn bool Terna.chk(&self)
{
ulong sqa = self.a * self.a;
ulong sqb = self.b * self.b;
ulong sqc = self.c * self.c;
return ( sqc == ( sqa + sqb ) );
}
fn String Terna.str(&self)
{
return string::tformat( "(%d, %d, %d)", self.a, self.b, self.c );
}
fn void main()
{
random::Lcg128Random rand;
List{ Terna } l;
io::printn("Ternas pitagóricas\nCalculando...");
l.push( {3, 4, 5} );
ulong i;
for(i = 0; i < 1_000_000; i++) {
Terna t = { rand.next_long(), rand.next_long(), rand.next_long() };
if ( t.chk() ) {
l.push( t );
}
}
io::printf("%d ternas encontradas tras %d ciclos.\n", l.len(), i);
foreach(Terna t: l) {
io::printn(t.str());
}
}
El programa crea una estructura Terna, que consta de tres enteros long (128 bits) sin signo. La idea es que la suma de los cuadrados de a y b debe ser igual a c. Esta propiedad se verifica mediante el método Terna.chk(). El método Terna.str() devuelve los valores de la terna como texto.
Así que utilizamos Lcg128Random del módulo std::math::random para generar enteros de 128 bits. Con un bucle, probamos a generar números aleatorios, y si se crea una terna válida, se añade a una lista (esta lista puede crecer dinámicamente).
C3 se siente bastante directo, fácil de comprender (no existe una nueva sintaxis), con un compilador que genera ejecutables rápidamente, y que son muy rápidos ellos mismos.
$ c3c compile ternas_pitagoricas.c3
$ ./ternas_pitagoricas Ternas pitagóricas
Calculando...
1 ternas encontradas tras 1000000 ciclos.
(3, 4, 5)
El ejecutable realiza mil millones (1 000 000 000) de ciclos en unos 17 segundos de manera consistente. Esto invlucra crea una terna, llamar su método chk() (que consiste en un cálculo matemático), y añadirlo o no a una lista.
$ time ./ternas_pitagoricas
Ternas pitagóricas
Calculando...
1 ternas encontradas tras 1000000000 ciclos.
(3, 4, 5)
./ternas_pitagoricas 16,38s user 0,00s system 99% cpu 16,438 total
$ time ./ternas_pitagoricas
Ternas pitagóricas
Calculando...
1 ternas encontradas tras 1000000000 ciclos.
(3, 4, 5)
./ternas_pitagoricas 16,35s user 0,00s system 99% cpu 16,402 total
$ time ./ternas_pitagoricas
Ternas pitagóricas
Calculando...
1 ternas encontradas tras 1000000000 ciclos.
(3, 4, 5)
./ternas_pitagoricas 16,31s user 0,00s system 99% cpu 16,368 total
El ejecutable es de unos 700 k, como se muestra a continuación.
$ ls -l ternas_pitagoricas
-rwxr-xr-x 1 baltasarq baltasarq 702640 mar 23 12:13 ternas_pitagoricas
¿Podemos compararlo con una versión C++? Pues claro.
#include <string>
#include <list>
#include <cstdio>
class Terna {
public:
Terna(ulong a, ulong b, ulong c)
:a(a), b(b), c(c)
{}
bool chk() const
{
ulong sqa = a * a;
ulong sqb = b * b;
ulong sqc = c * c;
return ( sqc == ( sqa + sqb ) );
}
std::string str() const
{
return std::to_string( a )
+ ", " + std::to_string( b )
+ ", " + std::to_string( c );
}
private:
ulong a;
ulong b;
ulong c;
};
int main()
{
std::list<Terna> l;
printf("Ternas pitagóricas\n");
ulong i = 0;
l.push_back( Terna( 3, 4, 5 ) );
for(; i < 1000000000ul; ++i) {
Terna t( std::rand(), std::rand(), std::rand() );
if ( t.chk() ) {
l.push_back( t );
}
}
printf( "%lu ternas encontradas tras %lu ciclos.\n", l.size(), i );
for(Terna t: l) {
printf( "%s\n", t.str().c_str() );
}
}
La salida devuelve la solución tras mil millones de ciclos en unos 17 segundos de manera consistente.
$ g++ ternas_pitagoricas.cpp -o ternas_pitagoricas
$ time ./ternas_pitagoricas
Ternas pitagóricas
1 ternas encontradas tras 1000000000 ciclos.
3, 4, 5
./ternas_pitagoricas 17,20s user 0,00s system 99% cpu 17,270 total
Obtenemos un resultado similar. Esto no es extraño, pues aprovecha el backend de LLVM.
En este caso, C++ es capaz de generar el ejecutable más pequeño, aunque es cierto que no se emplea la estructura de entrada y salida oficial, IOStreams. También es importante notar que el ejecutable enlaza con la librería estándar de C++, que no es, precisamente, pequeña (unos 3 MB). C3 genera, por otra parte, un ejecutable que es autocontenido.
$ g++ ternas_pitagoricas.cpp -o ternas_pitagoricas
$ ls -l ternas_pitagoricas
-rwxr-xr-x 1 baltasarq baltasarq 44320 mar 23 12:54 ternas_pitagoricas
$ ldd ternas_pitagoricas
linux-vdso.so.1 (0x00007faefdf04000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007faefdc00000)
libm.so.6 => /usr/lib/libm.so.6 (0x00007faefdae2000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x00007faefdab5000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007faefd8c4000)
/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007faefdf06000)
$ c3c compile ternas_pitagoricas.c3
Program linked to executable './ternas_pitagoricas'.
$ ldd ternas_pitagoricas
linux-vdso.so.1 (0x00007fad32973000)
libm.so.6 => /usr/lib/libm.so.6 (0x00007fad32796000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007fad325a5000)
/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007fad32975000)
¿El sucesor oficial, o al menos un lenguaje estable? Es difícil decirlo, solo el tiempo lo dirá.
Top comments (0)