While perusing cpp code I’ve noticed two patterns I’m interested in,
first the less interesting:
Q1: Type T as a hashmap key
In cpp they use std::type_index(typeid(T)), closest thing in zig I
know of is @typeName(T), which can be used as a key of
std.StringHashMap. But as someone who knows nothing about how
hashmaps works, I am convinced an usize would be much more efficient
than a []const u8. I actually got something to work, only anonymous
structs survive optimization, but it is quite goofy and might stop
working at any time I guess.
Question: Is there any better way and does avoiding
std.StringHashMap make sense ? These systems get fetched every frame
of a game, I’d imagine it makes a difference, and that’s also why I
would avoid hashmaps completly in favour of Q2.
//! zig version: 0.16.0
const std = @import("std");
const AudioSystem = struct {
// pub const Index: usize = 0;
// optimization kills the option above, only anonymous structs work
pub const Index = struct { x: u8 = 0 }{};
};
const RenderSystem = struct {
pub const Index = struct { x: u8 = 0 }{};
};
const SystemManager = struct {
map: std.AutoHashMap(usize, usize),
fn put(self: *@This(), T: anytype, val: usize) !void {
try self.map.put(@intFromPtr(&T.Index), val);
}
fn get(self: @This(), T: anytype) ?usize {
return self.map.get(@intFromPtr(&T.Index));
}
};
pub fn main(init: std.process.Init) !void {
var manager: SystemManager = .{ .map = .init(init.arena.allocator()) };
try manager.put(AudioSystem, 67);
try manager.put(RenderSystem, 420);
std.debug.print(
\\audio {?d} {*}
\\render {?d} {*}
\\
, .{
manager.get(AudioSystem), &AudioSystem.Index,
manager.get(RenderSystem), &RenderSystem.Index,
});
}
Output:
$ zig build-exe -O ReleaseFast test.zig ; ./test
audio 67 test.AudioSystem.Index__struct_2267@100ec68
render 420 test.RenderSystem.Index__struct_2281@100ec69
Q2: Type T as an index into an array
Blah blah blah something called static methods apparently, hard to
understand cppnese. Every Component T should know its index into an
array, where a pool of said components is stored.
The only solution I could think of:
const CompId = enum(usize) {
hp,
mana,
};
const ManaComp = struct {
pub const Id: usize = @intFromEnum(CompId.mana);
};
const HpComp = struct {
pub const Id: usize = @intFromEnum(CompId.hp);
};
fn getId(T: anytype) usize {
return T.Id;
}
Question: This works, but is there some magic where a function at
comptime could return a global incrementing counter to populate these?