Pointers to global variables are considered comptime-known in Zig. This fact allows us to synthesize multiple functions by binding one function to multiple variables. Here’s a simple example:
const std = @import("std");
const PFJMember = struct {
name: []const u8,
pub fn createGreetFn(comptime self: *@This()) fn () void {
const ns = struct {
fn greet() void {
std.debug.print("Hello, {s}!\n", .{self.name});
}
};
return ns.greet;
}
};
var brian: PFJMember = .{ .name = "Brian" };
var loretta: PFJMember = .{ .name = "Loretta" };
pub const greetBrian = brian.createGreetFn();
pub const greetLoretta = loretta.createGreetFn();
pub fn main() void {
greetBrian();
greetLoretta();
}
This is a very useful technique. Everything falls apart though when a variable needs to be thread-local. The problem, of course, is the fact that we’re now dealing with multiple variables. A regular pointer can’t point to all of them simultaneously.
To get around this problem, I’m purposing the addition of a new builtin: @tlsCast(). The builtin accepts a pointer to a threadlocal variable and returns a pointer in the .tls address space (doesn’t exist yet). It’d yield the offset into a thread’s thread-local memory, basically.
To bind a threadlocal variable to a function we would do this:
threadlocal var reg: PFJMember = .{ .name = "Reg" };
pub const greetReg = @tlsCast(®).createGreetFn();
Since @tlsCast() returns a pointer of a different type, createGreetFn() in the example would need to accept anytype instead of *@This():
pub fn createGreetFn(comptime ptr: anytype) fn () void {
const Self = @This();
const ns = struct {
fn greet() void {
const self: *Self = switch (@typeInfo(@TypeOf(ptr))) {
.pointer => |pt| switch (pt.address_space) {
.generic => ptr,
.tls => @addrSpaceCast(ptr),
else => @compileError("Unexpected address space"),
},
else => @compileError("Pointer expected"),
};
std.debug.print("Hello, {s}!\n", .{self.name});
}
};
return ns.greet;
}
@addrSpaceCast() would basically add address of the current thread’s thread-safe storage to the comptime-known offset.