← Volver al listado de tecnologías
Comptime en Zig
Comptime
Conceptos Básicos
Comptime permite ejecutar código en tiempo de compilación:
const std = @import("std");
// Constante evaluada en comptime
const TABLA_CUADRADOS = blk: {
var tabla: [10]i32 = undefined;
for (0..10) |i| {
tabla[i] = @as(i32, @intCast(i * i));
}
break :blk tabla;
};
pub fn main() void {
// La tabla ya está calculada en compilación
std.debug.print("5² = {d}\n", .{TABLA_CUADRADOS[5]}); // 25
// Comptime inline
const resultado = comptime blk: {
var suma: i32 = 0;
for (1..101) |i| {
suma += @as(i32, @intCast(i));
}
break :blk suma;
};
std.debug.print("Suma 1-100: {d}\n", .{resultado}); // 5050
}
Funciones Genéricas
const std = @import("std");
fn maximo(comptime T: type, a: T, b: T) T {
return if (a > b) a else b;
}
fn intercambiar(comptime T: type, a: *T, b: *T) void {
const temp = a.*;
a.* = b.*;
b.* = temp;
}
fn crearArray(comptime T: type, comptime N: usize, valor: T) [N]T {
return [_]T{valor} ** N;
}
pub fn main() void {
std.debug.print("Max i32: {d}\n", .{maximo(i32, 5, 10)});
std.debug.print("Max f64: {d}\n", .{maximo(f64, 3.14, 2.71)});
var x: i32 = 5;
var y: i32 = 10;
intercambiar(i32, &x, &y);
std.debug.print("x={d}, y={d}\n", .{ x, y });
const arr = crearArray(u8, 5, 42);
std.debug.print("Array: {any}\n", .{arr});
}
Type Reflection
const std = @import("std");
fn imprimirInfo(comptime T: type) void {
const info = @typeInfo(T);
std.debug.print("Tipo: {s}\n", .{@typeName(T)});
std.debug.print("Tamaño: {d} bytes\n", .{@sizeOf(T)});
std.debug.print("Alineación: {d}\n", .{@alignOf(T)});
switch (info) {
.Int => |int_info| {
std.debug.print(" Bits: {d}\n", .{int_info.bits});
std.debug.print(" Signedness: {s}\n", .{@tagName(int_info.signedness)});
},
.Struct => |struct_info| {
std.debug.print(" Campos: {d}\n", .{struct_info.fields.len});
for (struct_info.fields) |field| {
std.debug.print(" - {s}: {s}\n", .{ field.name, @typeName(field.type) });
}
},
else => {},
}
}
const Persona = struct {
nombre: []const u8,
edad: u32,
activo: bool,
};
pub fn main() void {
imprimirInfo(i32);
std.debug.print("\n", .{});
imprimirInfo(Persona);
}
Generación de Código
const std = @import("std");
fn GenerarVector(comptime T: type, comptime N: usize) type {
return struct {
datos: [N]T,
const Self = @This();
pub fn init(valor: T) Self {
return .{ .datos = [_]T{valor} ** N };
}
pub fn get(self: Self, index: usize) T {
return self.datos[index];
}
pub fn set(self: *Self, index: usize, valor: T) void {
self.datos[index] = valor;
}
pub fn sumar(self: Self, otro: Self) Self {
var resultado: Self = undefined;
inline for (0..N) |i| {
resultado.datos[i] = self.datos[i] + otro.datos[i];
}
return resultado;
}
pub fn len() usize {
return N;
}
};
}
pub fn main() void {
const Vec3 = GenerarVector(f32, 3);
var v1 = Vec3.init(1.0);
var v2 = Vec3.init(2.0);
v1.set(0, 10.0);
const v3 = v1.sumar(v2);
std.debug.print("v3[0] = {d}\n", .{v3.get(0)}); // 12.0
std.debug.print("Longitud: {d}\n", .{Vec3.len()});
}
Inline Loops
const std = @import("std");
fn sumarTupla(comptime N: usize, tupla: [N]i32) i32 {
var suma: i32 = 0;
// Loop desenrollado en compilación
inline for (tupla) |valor| {
suma += valor;
}
return suma;
}
fn aplicarATodos(comptime N: usize, arr: *[N]i32, comptime f: fn (i32) i32) void {
inline for (arr) |*elem| {
elem.* = f(elem.*);
}
}
fn duplicar(x: i32) i32 {
return x * 2;
}
pub fn main() void {
const tupla = [_]i32{ 1, 2, 3, 4, 5 };
std.debug.print("Suma: {d}\n", .{sumarTupla(5, tupla)});
var arr = [_]i32{ 1, 2, 3 };
aplicarATodos(3, &arr, duplicar);
std.debug.print("Duplicados: {any}\n", .{arr});
}
Comptime Strings
const std = @import("std");
fn formatearComptime(comptime fmt: []const u8, comptime args: anytype) []const u8 {
comptime {
var buf: [1024]u8 = undefined;
const resultado = std.fmt.bufPrint(&buf, fmt, args) catch unreachable;
var final: [resultado.len]u8 = undefined;
@memcpy(&final, resultado);
return &final;
}
}
fn generarNombreCampo(comptime prefix: []const u8, comptime index: usize) []const u8 {
return prefix ++ std.fmt.comptimePrint("{d}", .{index});
}
pub fn main() void {
const mensaje = comptime formatearComptime("Valor: {d}", .{42});
std.debug.print("{s}\n", .{mensaje});
const campo = comptime generarNombreCampo("campo_", 5);
std.debug.print("Campo: {s}\n", .{campo}); // campo_5
}
Testing de Comptime
const std = @import("std");
const testing = std.testing;
fn factorial(comptime n: u64) u64 {
if (n == 0) return 1;
return n * factorial(n - 1);
}
fn fibonacci(comptime n: usize) usize {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
fn esPrimo(comptime n: u64) bool {
if (n < 2) return false;
if (n == 2) return true;
if (n % 2 == 0) return false;
var i: u64 = 3;
while (i * i <= n) : (i += 2) {
if (n % i == 0) return false;
}
return true;
}
fn maximoGenerico(comptime T: type, a: T, b: T) T {
return if (a > b) a else b;
}
fn ContadorGenerico(comptime T: type) type {
return struct {
valor: T,
const Self = @This();
pub fn init() Self {
return .{ .valor = 0 };
}
pub fn incrementar(self: *Self) void {
self.valor += 1;
}
pub fn obtener(self: Self) T {
return self.valor;
}
};
}
test "factorial comptime" {
const f5 = comptime factorial(5);
try testing.expectEqual(@as(u64, 120), f5);
const f10 = comptime factorial(10);
try testing.expectEqual(@as(u64, 3628800), f10);
}
test "fibonacci comptime" {
const fib10 = comptime fibonacci(10);
try testing.expectEqual(@as(usize, 55), fib10);
}
test "es primo comptime" {
try testing.expect(comptime esPrimo(2));
try testing.expect(comptime esPrimo(17));
try testing.expect(comptime esPrimo(97));
try testing.expect(!comptime esPrimo(4));
try testing.expect(!comptime esPrimo(100));
}
test "maximo genérico con diferentes tipos" {
try testing.expectEqual(@as(i32, 10), maximoGenerico(i32, 5, 10));
try testing.expectEqual(@as(f64, 3.14), maximoGenerico(f64, 2.71, 3.14));
try testing.expectEqual(@as(u8, 255), maximoGenerico(u8, 100, 255));
}
test "tipo generado" {
const ContadorI32 = ContadorGenerico(i32);
var contador = ContadorI32.init();
try testing.expectEqual(@as(i32, 0), contador.obtener());
contador.incrementar();
contador.incrementar();
contador.incrementar();
try testing.expectEqual(@as(i32, 3), contador.obtener());
}
test "array generado en comptime" {
const TABLA = comptime blk: {
var t: [5]i32 = undefined;
for (0..5) |i| {
t[i] = @as(i32, @intCast(i * i));
}
break :blk t;
};
try testing.expectEqual(@as(i32, 0), TABLA[0]);
try testing.expectEqual(@as(i32, 1), TABLA[1]);
try testing.expectEqual(@as(i32, 4), TABLA[2]);
try testing.expectEqual(@as(i32, 16), TABLA[4]);
}
test "typeInfo" {
const info = @typeInfo(i32);
try testing.expect(info == .Int);
try testing.expectEqual(@as(u16, 32), info.Int.bits);
}
Ejercicios
- Crea una tabla de senos precalculada en comptime
- Implementa un tipo Matrix(T, rows, cols) genérico con operaciones
- Genera código para serializar structs automáticamente
Resumen
comptimeejecuta código en tiempo de compilación- Los generics usan
comptime T: typecomo parámetro @typeInfopermite introspección de tiposinline fordesenrolla loops en compilación- Los tipos pueden generarse dinámicamente con funciones