El Problema Persistente

C++ no tiene una historia de memory safety. Tiene una historia de "disciplina y tooling". AddressSanitizer detecta errores en tiempo de ejecución. Los analizadores estáticos detectan algunos en tiempo de compilación. Las C++ Core Guidelines son una guía de estilo, no un mecanismo de cumplimiento. Y después de tres décadas de parches incrementales, se siguen publicando CVEs por use-after-free y buffer overflows.

La guía de la NSA del 2022, el empuje posterior de CISA, y ahora los requerimientos del EU Cyber Resilience Act de 2025 hicieron que "simplemente tener más cuidado" sea una posición insostenible para cualquier producto que toque industrias reguladas. De ahí la necesidad de evaluar alternativas reales.

Carbon: La Apuesta Interop-First de Google

El pitch de Carbon es directo: ser para C++ lo que Kotlin fue para Java. Interop bidireccional completa, migración gradual, y una sintaxis modernizada que elimina los footguns más peligrosos.

Interop en la Práctica

El siguiente ejemplo importa una clase C++ de audio buffer en Carbon:

import Cpp library "audio/ring_buffer.h";

fn ProcessBlock(buf: Cpp.RingBuffer*, block_size: i32) -> bool {
 var samples: auto = buf->ReadBlock(block_size);
 if (samples.size() != block_size) {
 return false;
 }

 var i: i32 = 0;
 while (i < block_size) {
 samples[i] = ApplyGain(samples[i], 0.7);
 ++i;
 }

 return buf->WriteBlock(samples);
}

La directiva import Cpp es genuinamente impresionante cuando funciona. Las pruebas muestran que una implementación completa de ring buffer, incluyendo operaciones de fence con std::atomic, puede importarse y llamarse desde Carbon sin escribir una sola línea de binding. El compilador entiende el name mangling de C++, layouts de vtable, y hasta la semántica de std::unique_ptr.

Donde se rompe todo: el código SIMD. Las codebases que usan extensiones vectoriales de GCC y __attribute__((target("avx2"))) excesivamente encontrarán que la capa de interop de Carbon se ahoga con esto. Cada función SIMD necesita un wrapper con C-linkage plano, lo cual derrota el propósito del interop seamless.

Evaluación del Costo de Migración

Para una codebase representativa de 180k líneas, la estimación:

  • Código limpio C++17 moderno (~60% de la codebase): ~2.5 meses-ingeniero para portar
  • Código genérico template-heavy (~25%): ~6 meses-ingeniero, con rediseño significativo
  • SIMD/intrinsics/platform-specific (~15%): No factible de portar. Mantener como C++ e interoperar.

Estimación total realista: 8-10 meses-ingeniero para una migración incompleta. Ese es el número honesto que raramente se menciona en las conferencias.

Cpp2/cppfront: El Middle Ground Pragmático de Herb Sutter

Cpp2 toma un enfoque fundamentalmente distinto. En vez de construir un lenguaje nuevo con interop, define una sintaxis nueva que transpila a C++20 estándar. El build system existente, el debugger, el profiler, todo sigue funcionando porque la salida es C++.

La Sintaxis en Acción

La misma lógica de procesamiento del ring buffer en Cpp2:

ProcessBlock: (inout buf: RingBuffer, block_size: int) -> bool = {
 samples := buf.ReadBlock(block_size);
 if samples.size() != block_size {
 return false;
 }

 for (0 ..< block_size) do (i) {
 samples[i] = ApplyGain(samples[i], 0.7);
 }

 return buf.WriteBlock(samples);
}

El parámetro inout hace la mutación explícita en el sitio de llamada. Los parámetros son in (const ref) por defecto, lo que elimina toda una clase de errores de mutación accidental.

Lo Que Realmente Transpila

El compilador cppfront convierte eso en aproximadamente este C++20:

auto ProcessBlock(RingBuffer& buf, int block_size) -> bool {
 auto samples = buf.ReadBlock(block_size);
 if (samples.size() != block_size) {
 return false;
 }

 for (auto i = 0; i < block_size; ++i) {
 samples[i] = ApplyGain(samples[i], 0.7);
 }

 return buf.WriteBlock(samples);
}

El insight clave: cppfront agrega safety de inicialización (sin variables sin inicializar), null safety (sin null dereference en cpp2::unique_ptr), bounds safety (subscript chequeado por default), y type safety (sin conversiones narrowing implícitas). Todo enforced a nivel de sintaxis Cpp2, todo compilado a C++20 con zero overhead.

La Trampa

Los mensajes de error de cppfront son deficientes. Cuando algo sale mal durante la transpilación, se obtiene un error apuntando a una línea en el fuente Cpp2, pero si el error está en el C++ generado, el compilador falla con código generado que el desarrollador no escribió. Una frustración habitual: invertir una tarde entera depurando un fallo de instanciación de plantilla que resulta ser una cláusula requires faltante que cppfront debería haber generado pero no lo hizo.

Además, el soporte de IDEs es limitado. Existe una extensión de VS Code con resaltado de sintaxis básico, pero no hay go-to-definition confiable, no hay herramientas de refactoring, y el language server falla con archivos de más de ~500 líneas. Este aspecto tiene mayor impacto del que suele reconocerse.

Val: El Enfoque de Mutable Value Semantics

Val (antes conocido como Hylo) toma la salida más radical de C++. Su premisa central: si cada valor tiene un único dueño y las mutaciones siempre ocurren in-place, no se necesita borrow checker, no se necesita garbage collection, y se obtiene memory safety con cero overhead en tiempo de ejecución.

Ownership Sin Borrowing

fun processBlock(buf: inout RingBuffer, blockSize: Int) -> Bool {
 var samples = buf.readBlock(blockSize)
 if samples.count != blockSize {
 return false
 }

 for i in 0 ..< blockSize {
 &samples[i] = applyGain(samples[i], 0.7)
 }

 return buf.writeBlock(samples)
}

El prefijo & en samples[i] es la forma de Val de indicar "se está mutando esto in-place". Si se omite, el compilador rechaza la compilación. Es explícito, es visible, y elimina la clase de errores de "se copió o se creó un alias sin intención".

Lo que hace a Val genuinamente interesante comparado con Rust es la ausencia de anotaciones de lifetime. No hay 'a lifetimes distribuidos por las firmas de funciones. El sistema de tipos de Val asegura safety a través de value semantics solas, si se posee un valor, se es su único propietario. Sin estado mutable compartido, sin data races por construcción.

Limitaciones Actuales de Val

Val es el más joven de todos, y se nota. El compilador (construido sobre SIL de Swift) es lento, compilar un módulo de prueba de 3000 líneas tardó 47 segundos versus 3 segundos en Cpp2 y 8 en Carbon. La biblioteca estándar es mínima. No existe una historia de FFI que valga la pena mencionar, es posible llamar funciones C a través de una anotación @foreign, pero la interoperabilidad con C++ no existe todavía.

Para un proyecto greenfield con control total del stack, el diseño de Val es probablemente el más elegante. Para migrar una codebase C++ existente, es inviable en 2026.

Circle: La Revolución Unipersonal de Sean Baxter

Circle es el comodín. Sean Baxter construyó en solitario un compilador de C++ que extiende el lenguaje con características de safety de primera clase, pattern matching, reflection, y lo que fundamentalmente es un lenguaje nuevo que resulta ser un superset estricto de C++.

Safety Annotations en la Práctica

#feature on safety

void ProcessBlock(RingBuffer& buf, int block_size) safe {
 auto samples = buf.ReadBlock(block_size);
 if (samples.size() != block_size) {
 return;
 }

 for (int i = 0; i < block_size; ++i) {
 samples[i] = ApplyGain(samples[i], 0.7);
 }

 buf.WriteBlock(std2::move(samples));
}

La palabra clave safe en la firma de la función habilita el borrow checker de Circle para esa función. Es posible mezclar código safe y unsafe a nivel de función, lo que constituye la vía de escape más granular de cualquier lenguaje en esta comparación.

Circle compila codebases C++ existentes, incluso proyectos de 180k líneas, al primer intento. Es un compilador de C++, así que naturalmente lo hace. El camino de migración es: compilar el código con Circle, después incrementalmente agregar #feature on safety a los archivos uno por uno, arreglando las violaciones de safety que el compilador te reporta.

El Problema del Bus Factor

Circle lo mantiene una sola persona. Una persona increíblemente talentosa, pero una persona. No hay especificación formal, no hay comité, no hay respaldo corporativo. Si Sean decide ir a hacer otra cosa, el proyecto muere efectivamente. Para un proyecto hobby, está bien. ¿Para infraestructura de producción que necesita mantenerse por una década? Ese riesgo es real y es lo que limita una recomendación más amplia de Circle.

Benchmark: Implementación de Lock-Free Queue

La misma cola lock-free de Michael-Scott fue implementada en los cuatro lenguajes más el baseline C++20. Los resultados en una instancia AWS c7g.2xlarge (Graviton3, 8 vCPUs):

Lenguaje Enqueue/Dequeue ops/sec Tamaño Binario Tiempo Compilación
C++20 (baseline) 14.2M 48 KB 1.8s
Carbon 13.8M 52 KB 4.2s
Cpp2 (transpilado) 14.1M 48 KB 2.1s
Val 11.6M 67 KB 12.4s
Circle 14.2M 49 KB 2.3s

Cpp2 y Circle son esencialmente idénticos al baseline C++ en performance, lo cual tiene sentido, emiten C++. Carbon está dentro del 3%, lo que es impresionante para un backend de compilador diferente. La penalización del 18% de Val viene de su alias analysis más conservador; el compilador no pudo probar que ciertas operaciones de punteros en la cola lock-free no aliaseaban.

Recomendaciones Prácticas

Para organizaciones con una codebase C++ grande existente y presión regulatoria para mejorar memory safety:

  1. Circle para el camino más rápido a safety annotations sobre código existente, para quienes puedan aceptar el riesgo de mantenedor único.
  2. Cpp2 para la apuesta organizacional más segura, la salida es C++ estándar, de modo que siempre es posible "eyectar" conservando el código transpilado.
  3. Carbon para quienes estén dispuestos a esperar 12-18 meses más para que el tooling madure y la codebase sea C++ moderno relativamente limpio.
  4. Val solo para proyectos greenfield donde se busque el modelo de safety más elegante y no se necesite interop con C++.

Ninguno de estos es Rust. Rust tiene una década de ventaja en ecosistema, tooling y battle-testing en producción. Pero para shops donde 200 ingenieros saben C++ y reentrenar a Rust costaría dos años de velocidad, estos sucesores representan el camino pragmático del medio.

La migración desde C++ no será un proyecto de fin de semana. Ni siquiera va a ser un proyecto de un trimestre para cualquier codebase no trivial. Pero la presión regulatoria no se va a ir, y comenzar ahora, incluso con herramientas imperfectas, es preferible a esperar una solución perfecta que tal vez nunca llegue.