Zig opaque

34 阅读2分钟

opaque 是Zig中的不透明度类型,大小和对齐未知(尽管非零)。

可用于封装不暴露实现的数据结构,使得外部只能通过提供的公共方法访问和修改。

opaque相互转换

const std = @import("std");
const print = std.debug.print;

const Derp = opaque {};
const Wat = opaque {};

fn bar(d: *Derp) void {
    print("In bar with Derp at {p}\n", .{d});
}

fn foo(w: *Wat) void {
    // 显式转换:*Wat → *Derp(需确保内存布局兼容,这里两个都是空不透明类型,兼容)
    bar(@ptrCast(w));
}

pub fn main() void {
    // 分配合法的内存载体(不透明类型不能直接声明变量,需用字节数组承载)
    var wat_storage: [1]u8 align(@alignOf(Wat)) = undefined; // 空不透明类型大小为0,这里分配1字节即可
    const wat_ptr: *Wat = @ptrCast(&wat_storage);
    
    foo(wat_ptr); // 传入合法的 *Wat 指针
}

使用堆内存实现封装

user.zig

const std = @import ("std");

const UserImpl = struct {
  _count: i32,
};
pub const User = opaque {

};
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();

pub fn create(count:i32) !*User {
  // 分配内存存储InterUser实例
  const user_ptr = try allocator.create(UserImpl);
  // 初始化
  user_ptr.* = UserImpl{._count = count};
  return @as(*User, @ptrFromInt(@intFromPtr(user_ptr)));
}

pub fn getCount(user:*const User) i32 {
  const user_ptr:*const UserImpl = @as(*const UserImpl, @ptrFromInt(@intFromPtr(user)));
  return user_ptr._count;
}

main.zig

const std = @import("std");
const print = std.debug.print;
const user = @import("user.zig");
pub fn main() !void {
    const user1 = try user.create(10);
    print("User count: {}\n", .{user.getCount(user1)});
}

使用栈内存分配opaque

vec2.zig

const std = @import("std");

const Vec2Impl = struct {
    data: @Vector(2, f32),
    fn init(x: f32, y: f32) Vec2Impl {
        return Vec2Impl{
            .data = .{ x, y },
        };
    }
};
pub const size_of_Vec2Impl = @sizeOf(Vec2Impl);
pub const type_of_Vec2Impl = @TypeOf(Vec2Impl);
pub const align_of_Vec2Impl = @alignOf(Vec2Impl);
pub const Vec2 = opaque {};
pub fn init(vec2_ptr: *Vec2, x: f32, y: f32) void {
    const aligned_ptr: *align(align_of_Vec2Impl) Vec2 = @alignCast(vec2_ptr);
    const impl_ptr: *Vec2Impl = @ptrCast(aligned_ptr);
    impl_ptr.* = Vec2Impl.init(x, y);
}
pub fn getX(vec2_ptr: *const Vec2) f32 {
    const aligned_ptr: *const align(align_of_Vec2Impl) Vec2 = @alignCast(vec2_ptr);
    const impl_ptr: *const Vec2Impl = @ptrCast(aligned_ptr);
    return impl_ptr.data[0];
}

main.zig

const std = @import("std");
const print = std.debug.print;
const vec2 = @import("vec2.zig");

pub fn main() void {
    var v_storage: [vec2.size_of_Vec2Impl]u8 align(vec2.align_of_Vec2Impl) = undefined;
    const v_ptr: *vec2.Vec2 = @ptrCast(&v_storage);
    vec2.init(v_ptr, 3.0, 4.0);
    const x = vec2.getX(v_ptr);
    print("Vec2 x component: {}\n", .{x});
}