Currently, opaque
is not something we can create an instance of. We can only create pointers to opaque. To hide implementation details we might do something like this:
const Context = struct {
field1: usize,
field2: usize,
};
const ContextOpaquePtr = *align(@alignOf(Context)) opaque {};
pub fn startContext() ContextOpaquePtr {
const allocator = gpa.allocator();
const ptr = try allocator.create(Context);
ptr.* = .{ .field1 = 123, .field2 = 456 };
return @ptrCast(ptr);
}
The shapeless nature of opaque basically forces us to use the heap, increasing code size and slowing things down. Most of the times we donāt really that level of secrecy. Itās okay for the caller to know the dimensions and memory alignments of our structures. Opaqueness alone is sufficient.
So hereās my idea: allow the assignment of a backing type to an opaque.
Such an opaque would inherit a size and alignment from the backing type. This info allows the caller to provide memory storage on the stack for what is otherwise unknown structure. The example above can then be implemented like so:
const Context = struct {
field1: usize,
field2: usize,
};
const ContextOpaque = opaque(Context) {};
pub fn startContext() ContextOpaque {
const cxt: Context = .{ .field1 = 123, .field2 = 456 };
return @bitCast(cxt);
}
In addition, there would be a built-in function @expose()
, which would let us selectively exposes decls from the backing type:
const Context = struct {
field1: usize,
field2: usize,
fn getX(self: Context) usize {
return self.field1;
}
};
const ContextOpaque = opaque(Context) {
pub const getX = @expose("getX");
};
The built-in would automatically cast the function so that self
is of the opaque type.