You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Zig 0.11.0中std::function等价物及函数引用映射实现方法

在Zig 0.11.0中实现i32到函数引用的映射

Zig没有直接对应C++ std::function的内置类型,但可以根据函数签名是否统一,用两种方案实现需求:

1. 统一函数签名场景(最简单)

如果所有要关联的函数都有完全一致的签名,直接用函数指针作为哈希表的值类型即可,这是最高效的方案。

示例代码:

const std = @import("std");
const allocator = std.heap.page_allocator;

// 定义统一的函数签名
const HandlerFn = fn(value: i32) void;

pub fn main() !void {
    // 初始化HashMap:key为i32,value为HandlerFn类型的函数指针
    var map = std.HashMap(i32, HandlerFn).init(allocator);
    defer map.deinit();

    // 定义几个符合签名的函数
    fn printDouble(n: i32) void {
        std.debug.print("Double: {}\n", .{n * 2});
    }

    fn printSquare(n: i32) void {
        std.debug.print("Square: {}\n", .{n * n});
    }

    // 向映射中插入键值对
    try map.put(1, printDouble);
    try map.put(2, printSquare);

    // 根据key获取函数并调用
    if (map.get(1)) |fn_ptr| {
        fn_ptr(5); // 输出:Double: 10
    }

    if (map.get(2)) |fn_ptr| {
        fn_ptr(5); // 输出:Square: 25
    }
}

2. 不同函数签名场景(模拟std::function的多态)

如果需要关联的函数签名不同(比如参数数量、类型或返回值不同),可以通过类型擦除封装一个通用的可调用结构体,模拟std::function的行为。

示例代码:

const std = @import("std");
const allocator = std.heap.page_allocator;

// 封装通用可调用对象:通过类型擦除支持不同签名的函数/闭包
const Callable = struct {
    ctx: anyopaque,
    // 这里的call函数可根据实际需求调整参数和返回值,示例统一为接收i32参数、无返回值
    call: fn(ctx: anyopaque, arg: i32) void,

    // 辅助函数:将普通函数转换为Callable
    pub fn fromFn(comptime Fn: type, fn_ptr: Fn) Callable {
        // 编译期校验函数签名
        comptime std.debug.assert(std.meta.FnTypeOf(Fn) == fn(i32) void);
        return .{
            .ctx = @ptrCast(anyopaque, fn_ptr),
            .call = struct {
                fn call(ctx: anyopaque, arg: i32) void {
                    const f = @ptrCast(Fn, ctx);
                    f(arg);
                }
            }.call,
        };
    }

    // 辅助函数:将闭包转换为Callable(闭包需分配在堆上)
    pub fn fromClosure(comptime Closure: type, closure: *Closure) Callable {
        return .{
            .ctx = closure,
            .call = struct {
                fn call(ctx: anyopaque, arg: i32) void {
                    const c = @ptrCast(*Closure, @alignCast(@alignOf(Closure), ctx));
                    c.call(arg);
                }
            }.call,
        };
    }
};

pub fn main() !void {
    var map = std.HashMap(i32, Callable).init(allocator);
    defer map.deinit();

    // 插入普通函数
    fn printTriple(n: i32) void {
        std.debug.print("Triple: {}\n", .{n * 3});
    }
    try map.put(3, Callable.fromFn(@TypeOf(printTriple), printTriple));

    // 插入闭包(堆分配后需手动销毁)
    var closure = try allocator.create(struct {
        factor: i32,
        fn call(self: *@This(), n: i32) void {
            std.debug.print("Multiplied by {}: {}\n", .{self.factor, n * self.factor});
        }
    });
    defer allocator.destroy(closure);
    closure.factor = 4;
    try map.put(4, Callable.fromClosure(@TypeOf(closure), closure));

    // 调用映射中的函数
    if (map.get(3)) |callable| {
        callable.call(callable.ctx, 5); // 输出:Triple: 15
    }

    if (map.get(4)) |callable| {
        callable.call(callable.ctx, 5); // 输出:Multiplied by 4: 20
    }
}

注意事项

  • 闭包在Zig中是带上下文的结构体,需要手动管理内存(示例中用堆分配,记得销毁)。
  • 类型擦除会带来少量性能开销,若不需要多态性,优先选择第一种方案。
  • Zig 0.11.0的std.HashMap需要手动调用deinit释放资源,避免内存泄漏。

内容的提问来源于stack exchange,提问作者user16028444

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 19:13:17