I have a generic Arena type:
pub fn Arena(comptime T: type) type {
return struct {
arena: *std.heap.ArenaAllocator,
value: T,
pub fn deinit(self: @This()) void {
if (@hasDecl(T, "deinit")) {
self.value.deinit();
}
const allocator = self.arena.child_allocator;
self.arena.deinit();
allocator.destroy(self.arena);
}
};
}
I added the @hasDecl
recently but I’m not sure if if its a good idea. Is there a better way to do that?
Here is example usage, I have a struct that interacts with some C code that needs manual drops (hence the deinit
):
pub const ZenohHandler = struct {
config: *zenoh.c.z_owned_config_t,
session: *zenoh.c.z_owned_session_t,
pubs: std.StringArrayHashMap(zenoh.c.z_owned_publisher_t),
pub fn init(p_allocator: std.mem.Allocator, eni: gcat.ENI, maybe_config_file: ?[:0]const u8) !gcat.Arena(ZenohHandler) {
var arena = try p_allocator.create(std.heap.ArenaAllocator);
arena.* = .init(p_allocator);
errdefer p_allocator.destroy(arena);
errdefer arena.deinit();
const allocator = arena.allocator();
const config = try allocator.create(zenoh.c.z_owned_config_t);
if (maybe_config_file) |config_file| {
try zenoh.err(zenoh.c.zc_config_from_file(config, config_file.ptr));
} else {
try zenoh.err(zenoh.c.z_config_default(config));
}
errdefer zenoh.drop(zenoh.move(config));
var open_options: zenoh.c.z_open_options_t = undefined;
zenoh.c.z_open_options_default(&open_options);
const session = try allocator.create(zenoh.c.z_owned_session_t);
const open_result = zenoh.c.z_open(session, zenoh.move(config), &open_options);
try zenoh.err(open_result);
errdefer zenoh.drop(zenoh.move(session));
var pubs = std.StringArrayHashMap(zenoh.c.z_owned_publisher_t).init(allocator);
errdefer pubs.deinit();
errdefer {
for (pubs.values()) |*publisher| {
zenoh.drop(zenoh.move(publisher));
}
}
for (eni.subdevices) |subdevice| {
for (subdevice.inputs) |input| {
for (input.entries) |entry| {
if (entry.pv_name == null) continue;
var publisher: zenoh.c.z_owned_publisher_t = undefined;
var view_keyexpr: zenoh.c.z_view_keyexpr_t = undefined;
std.log.warn("zenoh: declaring publisher: {s}, ethercat type: {s}", .{ entry.pv_name.?, @tagName(entry.type) });
const result = zenoh.c.z_view_keyexpr_from_str(&view_keyexpr, entry.pv_name.?.ptr);
try zenoh.err(result);
var publisher_options: zenoh.c.z_publisher_options_t = undefined;
zenoh.c.z_publisher_options_default(&publisher_options);
publisher_options.congestion_control = zenoh.c.Z_CONGESTION_CONTROL_DROP;
const result2 = zenoh.c.z_declare_publisher(zenoh.loan(session), &publisher, zenoh.loan(&view_keyexpr), &publisher_options);
try zenoh.err(result2);
const put_result = try pubs.getOrPutValue(entry.pv_name.?, publisher);
if (put_result.found_existing) return error.PVNameConflict; // TODO: assert this?
}
}
}
return gcat.Arena(ZenohHandler){ .arena = arena, .value = .{
.config = config,
.session = session,
.pubs = pubs,
} };
}
pub fn deinit(self: ZenohHandler) void {
for (self.pubs.values()) |*publisher| {
zenoh.drop(zenoh.move(publisher));
}
zenoh.drop(zenoh.move(self.config));
zenoh.drop(zenoh.move(self.session));
}
}
I am both allocating with the arena, and there are hidden allocations in the C dependency that I have to drop. I want to use an arena for the zig allocations, and still drop the C allocations.
Is there a better way than @hasDecl
to address this issue? Maybe I should just store a pointer to an arena allocation in the struct and free that as part of deinit?