Skip to content

Zig 编译期

Zig 的编译期计算是其最强大的特性之一,它允许在编译时执行代码,生成高效的运行时代码。

编译期基础概念

comptime 关键字

comptime 关键字用于标记在编译时执行的代码:

zig
const std = @import("std");

pub fn main() void {
    // 编译时常量
    const compile_time_value = comptime 2 + 3;
    std.debug.print("编译时计算: {}\n", .{compile_time_value});
    
    // 编译时变量
    comptime var counter = 0;
    comptime {
        counter += 1;
        counter *= 2;
    }
    
    std.debug.print("编译时变量: {}\n", .{counter});
    
    // 编译时循环
    comptime var sum = 0;
    comptime var i = 1;
    comptime {
        while (i <= 10) : (i += 1) {
            sum += i;
        }
    }
    
    std.debug.print("1到10的和 (编译时计算): {}\n", .{sum});
}

编译时函数

函数可以在编译时执行:

zig
const std = @import("std");

// 编译时计算斐波那契数
fn fibonacci(n: u32) u32 {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

// 编译时计算阶乘
fn factorial(n: u32) u32 {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}

pub fn main() void {
    // 在编译时计算
    const fib_10 = comptime fibonacci(10);
    const fact_5 = comptime factorial(5);
    
    std.debug.print("斐波那契数列第10项: {}\n", .{fib_10});
    std.debug.print("5的阶乘: {}\n", .{fact_5});
    
    // 编译时生成数组
    const fib_array = comptime blk: {
        var array: [10]u32 = undefined;
        for (array, 0..) |*item, i| {
            item.* = fibonacci(@intCast(i));
        }
        break :blk array;
    };
    
    std.debug.print("斐波那契数组: ");
    for (fib_array) |num| {
        std.debug.print("{} ", .{num});
    }
    std.debug.print("\n");
}

编译时类型操作

类型反射

zig
const std = @import("std");

const Point = struct {
    x: f32,
    y: f32,
    z: f32,
};

const Color = enum {
    Red,
    Green,
    Blue,
};

fn printTypeInfo(comptime T: type) void {
    const type_info = @typeInfo(T);
    
    std.debug.print("类型: {}\n", .{T});
    std.debug.print("大小: {} 字节\n", .{@sizeOf(T)});
    std.debug.print("对齐: {} 字节\n", .{@alignOf(T)});
    
    switch (type_info) {
        .Struct => |struct_info| {
            std.debug.print("结构体字段数: {}\n", .{struct_info.fields.len});
            for (struct_info.fields) |field| {
                std.debug.print("  字段: {s} (类型: {})\n", .{ field.name, field.type });
            }
        },
        .Enum => |enum_info| {
            std.debug.print("枚举值数: {}\n", .{enum_info.fields.len});
            for (enum_info.fields) |field| {
                std.debug.print("  值: {s}\n", .{field.name});
            }
        },
        .Int => |int_info| {
            std.debug.print("整数位数: {}\n", .{int_info.bits});
            std.debug.print("有符号: {}\n", .{int_info.signedness == .signed});
        },
        else => {},
    }
    std.debug.print("\n");
}

pub fn main() void {
    comptime {
        printTypeInfo(Point);
        printTypeInfo(Color);
        printTypeInfo(i32);
        printTypeInfo(u64);
    }
}

泛型函数

zig
const std = @import("std");

// 泛型函数:适用于任何数字类型
fn add(comptime T: type, a: T, b: T) T {
    return a + b;
}

// 泛型函数:计算数组最大值
fn max(comptime T: type, array: []const T) T {
    if (array.len == 0) return 0;
    
    var maximum = array[0];
    for (array[1..]) |item| {
        if (item > maximum) {
            maximum = item;
        }
    }
    return maximum;
}

// 泛型函数:交换两个值
fn swap(comptime T: type, a: *T, b: *T) void {
    const temp = a.*;
    a.* = b.*;
    b.* = temp;
}

pub fn main() void {
    // 使用泛型加法
    std.debug.print("整数加法: {}\n", .{add(i32, 10, 20)});
    std.debug.print("浮点加法: {d:.2}\n", .{add(f64, 3.14, 2.86)});
    
    // 使用泛型最大值
    const int_array = [_]i32{ 1, 5, 3, 9, 2 };
    const float_array = [_]f32{ 1.1, 5.5, 3.3, 9.9, 2.2 };
    
    std.debug.print("整数数组最大值: {}\n", .{max(i32, &int_array)});
    std.debug.print("浮点数组最大值: {d:.1}\n", .{max(f32, &float_array)});
    
    // 使用泛型交换
    var x: i32 = 100;
    var y: i32 = 200;
    
    std.debug.print("交换前: x={}, y={}\n", .{ x, y });
    swap(i32, &x, &y);
    std.debug.print("交换后: x={}, y={}\n", .{ x, y });
}

编译时字符串处理

字符串操作

zig
const std = @import("std");

// 编译时字符串连接
fn compileTimeConcat(comptime str1: []const u8, comptime str2: []const u8) []const u8 {
    return str1 ++ str2;
}

// 编译时字符串重复
fn compileTimeRepeat(comptime str: []const u8, comptime count: usize) []const u8 {
    comptime var result: []const u8 = "";
    comptime var i = 0;
    comptime {
        while (i < count) : (i += 1) {
            result = result ++ str;
        }
    }
    return result;
}

// 编译时生成格式化字符串
fn compileTimeFormat(comptime fmt: []const u8, args: anytype) []const u8 {
    return comptime std.fmt.comptimePrint(fmt, args);
}

pub fn main() void {
    // 编译时字符串操作
    const greeting = comptime compileTimeConcat("Hello, ", "World!");
    const repeated = comptime compileTimeRepeat("Zig ", 3);
    const formatted = comptime compileTimeFormat("Version: {}.{}.{}", .{ 1, 0, 0 });
    
    std.debug.print("连接字符串: {s}\n", .{greeting});
    std.debug.print("重复字符串: {s}\n", .{repeated});
    std.debug.print("格式化字符串: {s}\n", .{formatted});
    
    // 编译时生成标识符
    const field_name = comptime compileTimeConcat("field_", "name");
    std.debug.print("生成的标识符: {s}\n", .{field_name});
}

编译时代码生成

生成结构体

zig
const std = @import("std");

// 编译时生成结构体
fn generateStruct(comptime fields: []const struct { name: []const u8, type: type }) type {
    comptime var struct_fields: [fields.len]std.builtin.Type.StructField = undefined;
    
    comptime {
        for (fields, 0..) |field, i| {
            struct_fields[i] = std.builtin.Type.StructField{
                .name = field.name,
                .type = field.type,
                .default_value = null,
                .is_comptime = false,
                .alignment = @alignOf(field.type),
            };
        }
    }
    
    return @Type(std.builtin.Type{
        .Struct = std.builtin.Type.Struct{
            .layout = .Auto,
            .fields = &struct_fields,
            .decls = &[_]std.builtin.Type.Declaration{},
            .is_tuple = false,
        },
    });
}

pub fn main() void {
    // 定义字段
    const fields = [_]struct { name: []const u8, type: type }{
        .{ .name = "id", .type = u32 },
        .{ .name = "name", .type = []const u8 },
        .{ .name = "score", .type = f64 },
    };
    
    // 生成结构体类型
    const Student = comptime generateStruct(&fields);
    
    // 使用生成的结构体
    const student = Student{
        .id = 12345,
        .name = "张三",
        .score = 95.5,
    };
    
    std.debug.print("学生信息:\n");
    std.debug.print("  ID: {}\n", .{student.id});
    std.debug.print("  姓名: {s}\n", .{student.name});
    std.debug.print("  分数: {d:.1}\n", .{student.score});
}

生成函数

zig
const std = @import("std");

// 编译时生成getter函数
fn generateGetter(comptime T: type, comptime field_name: []const u8) fn (*const T) @TypeOf(@field(@as(T, undefined), field_name)) {
    return struct {
        fn get(self: *const T) @TypeOf(@field(@as(T, undefined), field_name)) {
            return @field(self, field_name);
        }
    }.get;
}

// 编译时生成setter函数
fn generateSetter(comptime T: type, comptime field_name: []const u8) fn (*T, @TypeOf(@field(@as(T, undefined), field_name))) void {
    return struct {
        fn set(self: *T, value: @TypeOf(@field(@as(T, undefined), field_name))) void {
            @field(self, field_name) = value;
        }
    }.set;
}

const Person = struct {
    name: []const u8,
    age: u32,
    
    // 编译时生成的getter和setter
    const getName = comptime generateGetter(Person, "name");
    const getAge = comptime generateGetter(Person, "age");
    const setName = comptime generateSetter(Person, "name");
    const setAge = comptime generateSetter(Person, "age");
};

pub fn main() void {
    var person = Person{
        .name = "李四",
        .age = 25,
    };
    
    // 使用生成的getter
    std.debug.print("姓名: {s}\n", .{person.getName()});
    std.debug.print("年龄: {}\n", .{person.getAge()});
    
    // 使用生成的setter
    person.setName("王五");
    person.setAge(30);
    
    std.debug.print("更新后:\n");
    std.debug.print("姓名: {s}\n", .{person.getName()});
    std.debug.print("年龄: {}\n", .{person.getAge()});
}

编译时条件编译

平台相关代码

zig
const std = @import("std");
const builtin = @import("builtin");

// 编译时平台检测
const is_windows = comptime builtin.os.tag == .windows;
const is_linux = comptime builtin.os.tag == .linux;
const is_macos = comptime builtin.os.tag == .macos;

// 平台相关的路径分隔符
const path_separator = comptime if (is_windows) "\\" else "/";

// 平台相关的换行符
const line_ending = comptime if (is_windows) "\r\n" else "\n";

// 编译时选择实现
fn getPlatformName() []const u8 {
    return comptime if (is_windows)
        "Windows"
    else if (is_linux)
        "Linux"
    else if (is_macos)
        "macOS"
    else
        "Unknown";
}

pub fn main() void {
    std.debug.print("当前平台: {s}\n", .{getPlatformName()});
    std.debug.print("路径分隔符: {s}\n", .{path_separator});
    std.debug.print("换行符长度: {}\n", .{line_ending.len});
    
    // 编译时特性检测
    const has_vector_support = comptime builtin.cpu.arch.endian() == .Little;
    std.debug.print("小端序支持: {}\n", .{has_vector_support});
    
    // 编译时优化级别
    const optimization_level = comptime switch (builtin.mode) {
        .Debug => "调试",
        .ReleaseSafe => "安全发布",
        .ReleaseFast => "快速发布",
        .ReleaseSmall => "小体积发布",
    };
    
    std.debug.print("优化级别: {s}\n", .{optimization_level});
}

特性开关

zig
const std = @import("std");

// 编译时特性开关
const ENABLE_LOGGING = true;
const ENABLE_PROFILING = false;
const DEBUG_MODE = @import("builtin").mode == .Debug;

// 条件编译的日志函数
fn log(comptime fmt: []const u8, args: anytype) void {
    if (comptime ENABLE_LOGGING) {
        std.debug.print("[LOG] " ++ fmt ++ "\n", args);
    }
}

// 条件编译的性能分析
fn profile(comptime name: []const u8, func: anytype) @TypeOf(func()) {
    if (comptime ENABLE_PROFILING) {
        std.debug.print("[PROFILE] 开始: {s}\n", .{name});
        const start_time = std.time.nanoTimestamp();
        
        const result = func();
        
        const end_time = std.time.nanoTimestamp();
        const duration = end_time - start_time;
        std.debug.print("[PROFILE] 结束: {s}, 耗时: {}ns\n", .{ name, duration });
        
        return result;
    } else {
        return func();
    }
}

// 调试断言
fn debugAssert(condition: bool, comptime message: []const u8) void {
    if (comptime DEBUG_MODE) {
        if (!condition) {
            std.debug.panic("断言失败: " ++ message, .{});
        }
    }
}

pub fn main() void {
    log("程序开始执行", .{});
    
    const result = profile("计算操作", struct {
        fn compute() i32 {
            var sum: i32 = 0;
            for (0..1000) |i| {
                sum += @intCast(i);
            }
            return sum;
        }
    }.compute);
    
    debugAssert(result > 0, "计算结果应该大于0");
    
    log("计算结果: {}", .{result});
    log("程序执行完成", .{});
}

编译时内存分配

编译时数组生成

zig
const std = @import("std");

// 编译时生成查找表
fn generateLookupTable(comptime size: usize) [size]u32 {
    comptime var table: [size]u32 = undefined;
    comptime {
        for (table, 0..) |*item, i| {
            item.* = @intCast(i * i); // 平方表
        }
    }
    return table;
}

// 编译时生成素数表
fn generatePrimeTable(comptime limit: usize) []const u32 {
    comptime var primes: [limit]u32 = undefined;
    comptime var count: usize = 0;
    
    comptime {
        var num: u32 = 2;
        while (num < limit and count < limit) : (num += 1) {
            var is_prime = true;
            var i: u32 = 2;
            while (i * i <= num) : (i += 1) {
                if (num % i == 0) {
                    is_prime = false;
                    break;
                }
            }
            if (is_prime) {
                primes[count] = num;
                count += 1;
            }
        }
    }
    
    return primes[0..count];
}

pub fn main() void {
    // 使用编译时生成的查找表
    const square_table = comptime generateLookupTable(10);
    
    std.debug.print("平方表 (0-9):\n");
    for (square_table, 0..) |square, i| {
        std.debug.print("{}² = {}\n", .{ i, square });
    }
    
    // 使用编译时生成的素数表
    const primes = comptime generatePrimeTable(100);
    
    std.debug.print("\n前{}个素数:\n", .{primes.len});
    for (primes, 0..) |prime, i| {
        std.debug.print("{}: {}\n", .{ i + 1, prime });
        if (i >= 9) break; // 只显示前10个
    }
}

编译时错误处理

编译时验证

zig
const std = @import("std");

// 编译时验证函数参数
fn validateAtCompileTime(comptime value: i32) void {
    if (value < 0) {
        @compileError("值不能为负数");
    }
    if (value > 100) {
        @compileError("值不能大于100");
    }
}

// 编译时验证类型
fn requireNumericType(comptime T: type) void {
    const type_info = @typeInfo(T);
    switch (type_info) {
        .Int, .Float => {},
        else => @compileError("类型必须是数字类型"),
    }
}

// 编译时验证数组大小
fn requireMinArraySize(comptime T: type, comptime min_size: usize) void {
    const type_info = @typeInfo(T);
    switch (type_info) {
        .Array => |array_info| {
            if (array_info.len < min_size) {
                @compileError(std.fmt.comptimePrint("数组大小必须至少为{}", .{min_size}));
            }
        },
        else => @compileError("类型必须是数组"),
    }
}

// 使用编译时验证的函数
fn processValue(comptime value: i32) i32 {
    comptime validateAtCompileTime(value);
    return value * 2;
}

fn processArray(comptime T: type, array: T) void {
    comptime requireMinArraySize(T, 3);
    comptime requireNumericType(@TypeOf(array[0]));
    
    std.debug.print("处理数组,大小: {}\n", .{array.len});
    for (array[0..3]) |item| {
        std.debug.print("  {}\n", .{item});
    }
}

pub fn main() void {
    // 这些会在编译时验证
    const result1 = comptime processValue(50);  // OK
    // const result2 = comptime processValue(-10); // 编译错误
    // const result3 = comptime processValue(150); // 编译错误
    
    std.debug.print("处理结果: {}\n", .{result1});
    
    // 数组验证
    const valid_array = [_]i32{ 1, 2, 3, 4, 5 };
    // const invalid_array = [_]i32{ 1, 2 }; // 编译错误:数组太小
    
    processArray(@TypeOf(valid_array), valid_array);
}

编译时性能优化

预计算优化

zig
const std = @import("std");

// 编译时预计算三角函数表
const SIN_TABLE_SIZE = 360;
const sin_table = comptime blk: {
    var table: [SIN_TABLE_SIZE]f64 = undefined;
    for (table, 0..) |*item, i| {
        const angle = @as(f64, @floatFromInt(i)) * std.math.pi / 180.0;
        item.* = @sin(angle);
    }
    break :blk table;
};

// 快速正弦查找
fn fastSin(degrees: u32) f64 {
    return sin_table[degrees % SIN_TABLE_SIZE];
}

// 编译时预计算CRC表
const CRC_TABLE = comptime blk: {
    var table: [256]u32 = undefined;
    for (table, 0..) |*item, i| {
        var crc: u32 = @intCast(i);
        for (0..8) |_| {
            if (crc & 1 != 0) {
                crc = (crc >> 1) ^ 0xEDB88320;
            } else {
                crc >>= 1;
            }
        }
        item.* = crc;
    }
    break :blk table;
};

// 快速CRC计算
fn fastCrc32(data: []const u8) u32 {
    var crc: u32 = 0xFFFFFFFF;
    for (data) |byte| {
        const table_index = @as(u8, @truncate(crc)) ^ byte;
        crc = (crc >> 8) ^ CRC_TABLE[table_index];
    }
    return crc ^ 0xFFFFFFFF;
}

pub fn main() void {
    // 使用预计算的正弦表
    std.debug.print("正弦值:\n");
    for ([_]u32{ 0, 30, 45, 60, 90 }) |angle| {
        std.debug.print("sin({}°) = {d:.6}\n", .{ angle, fastSin(angle) });
    }
    
    // 使用预计算的CRC表
    const test_data = "Hello, Zig!";
    const crc = fastCrc32(test_data);
    std.debug.print("\nCRC32(\"{s}\") = 0x{X}\n", .{ test_data, crc });
}

总结

本章详细介绍了 Zig 的编译期特性:

  • comptime 关键字和编译时执行
  • ✅ 编译时类型操作和反射
  • ✅ 泛型函数和类型参数
  • ✅ 编译时字符串处理
  • ✅ 编译时代码生成
  • ✅ 条件编译和特性开关
  • ✅ 编译时内存分配
  • ✅ 编译时错误处理和验证
  • ✅ 编译时性能优化

Zig 的编译期系统是其最独特和强大的特性,它让开发者能够在编译时完成大量工作,生成高效的运行时代码。在下一章中,我们将学习 Zig 的数组与切片。

本站内容仅供学习和研究使用。