Skip to content

Commit

Permalink
Remove vtable
Browse files Browse the repository at this point in the history
  • Loading branch information
sam701 committed Oct 12, 2023
1 parent 03f7c59 commit 07b458f
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 156 deletions.
26 changes: 18 additions & 8 deletions example/simple.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,29 @@ var config = struct {
port: u16 = 3000,
base_value: f64 = undefined,
expose_metrics: bool = false,
testar: []i32 = undefined,
}{};

var host_option = cli.Option{
.long_name = "host",
.help = "host to listen on",
.short_alias = 'h',
.value = cli.OptionValue{ .string = null },
.value_ref = cli.valueRef(&config.host),
.required = true,
.value_name = "IP",
};
var port_option = cli.Option{
.long_name = "port",
.help = "post to bind to",
.value = cli.OptionValue{ .int = null },
.value_ref = cli.valueRef(&config.port),
};
var ar_option = cli.Option{
.long_name = "array",
.help = "slice test",
.value = cli.OptionValue{ .int = null },
.value_ref = cli.valueRef(&config.testar),
};
var expose_metrics_option = cli.Option{
.long_name = "expose-metrics",
Expand Down Expand Up @@ -66,16 +75,17 @@ var app = &cli.App{
};

pub fn main() anyerror!void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
// defer arena.deinit();
const al = arena.allocator();
try port_option.value_ref.?.put("356", allocator);
std.log.debug("port value = {}", .{config.port});

var ctx = cli.Context.init(al);
host_option.value_ref = try ctx.valueRef(&config.host);
port_option.value_ref = try ctx.valueRef(&config.port);
expose_metrics_option.value_ref = try ctx.valueRef(&config.expose_metrics);
base_value_option.value_ref = try ctx.valueRef(&config.base_value);
try host_option.value_ref.?.put("awesome.com", allocator);
std.log.debug("host value = {s}", .{config.host});

try ar_option.value_ref.?.put("45", allocator);
try ar_option.value_ref.?.put("94", allocator);
try ar_option.value_ref.?.put("23456789", allocator);
try ar_option.value_ref.?.finalize(allocator);
std.log.debug("testar value = {any}", .{config.testar});
return cli.run(app, allocator);
}

Expand Down
2 changes: 1 addition & 1 deletion src/command.zig
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const std = @import("std");
const vref = @import("./value_ref.zig");
pub const ValueRef = vref.ValueRef;
pub const Context = vref.Context;
pub const valueRef = vref.valueRef;

pub const App = struct {
name: []const u8,
Expand Down
85 changes: 51 additions & 34 deletions src/value_parser.zig
Original file line number Diff line number Diff line change
@@ -1,53 +1,70 @@
const std = @import("std");

// const Parser = *const fn (dest: *anyopaque, value: []const u8) anyerror!void;
pub fn ValueParser(comptime T: type) type {
// TODO: the parse function might need an allocator, e.g. to copy a string or allocate the destination type if it is a pointer.
return *const fn (dest: *T, value: []const u8) anyerror!void;
}
pub const ValueParser = *const fn (dest: *anyopaque, value: []const u8) anyerror!void;

pub const ValueData = struct {
value_size: usize,
value_parser: ValueParser,
};

pub fn get(comptime T: type) ValueParser(T) {
pub fn getValueData(comptime T: type) ValueData {
return switch (@typeInfo(T)) {
.Int => intParser(T),
.Float => floatParser(T),
.Bool => boolParser(T),
.Int => intData(T),
.Float => floatData(T),
.Bool => boolData(T),
.Pointer => |pinfo| {
if (pinfo.size == .Slice and pinfo.child == u8) {
stringParser(T);
return stringData(T);
}
},
else => @compileError("unsupported value type"),
};
}

fn intParser(comptime T: type) ValueParser(T) {
return struct {
fn parser(dest: *T, value: []const u8) anyerror!void {
dest.* = try std.fmt.parseInt(T, value, 10);
}
}.parser;
fn intData(comptime T: type) ValueData {
return .{
.value_size = @sizeOf(T),
.value_parser = struct {
fn parser(dest: *anyopaque, value: []const u8) anyerror!void {
const dt: *T = @ptrCast(@alignCast(dest));
dt.* = try std.fmt.parseInt(T, value, 10);
}
}.parser,
};
}

fn floatParser(comptime T: type) ValueParser(T) {
return struct {
fn parser(dest: *T, value: []const u8) anyerror!void {
dest.* = try std.fmt.parseFloat(T, value);
}
}.parser;
fn floatData(comptime T: type) ValueData {
return .{
.value_size = @sizeOf(T),
.value_parser = struct {
fn parser(dest: *anyopaque, value: []const u8) anyerror!void {
const dt: *T = @ptrCast(@alignCast(dest));
dt.* = try std.fmt.parseFloat(T, value);
}
}.parser,
};
}

fn boolParser(comptime T: type) ValueParser(T) {
return struct {
fn parser(dest: *T, value: []const u8) anyerror!void {
dest.* = std.mem.eql(u8, value, "true");
}
}.parser;
fn boolData(comptime T: type) ValueData {
return .{
.value_size = @sizeOf(T),
.value_parser = struct {
fn parser(dest: *anyopaque, value: []const u8) anyerror!void {
const dt: *T = @ptrCast(@alignCast(dest));
dt.* = std.mem.eql(u8, value, "true");
}
}.parser,
};
}

fn stringParser(comptime T: type) ValueParser(T) {
return struct {
fn parser(dest: *T, value: []const u8) anyerror!void {
dest.* = value;
}
}.parser;
fn stringData(comptime T: type) ValueData {
return .{
.value_size = @sizeOf(T),
.value_parser = struct {
fn parser(dest: *anyopaque, value: []const u8) anyerror!void {
const dt: *T = @ptrCast(@alignCast(dest));
dt.* = value;
}
}.parser,
};
}
177 changes: 64 additions & 113 deletions src/value_ref.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,130 +3,81 @@ const command = @import("./command.zig");
const vp = @import("./value_parser.zig");

pub const ValueRef = struct {
impl_ptr: *anyopaque,
vtable: *const VTable,
dest: *anyopaque,
value_data: vp.ValueData,
value_type: ValueType,
element_count: usize = 0,

const Self = @This();

const VTable = struct {
put: *const fn (impl_ptr: *anyopaque, value: []const u8) anyerror!void,
pub fn put(self: *Self, value: []const u8, alloc: std.mem.Allocator) anyerror!void {
switch (self.value_type) {
.single => {
return self.value_data.value_parser(self.dest, value);
},
.multi => |*list| {
self.element_count += 1;
try list.ensureTotalCapacity(alloc, self.element_count * self.value_data.value_size);
const value_ptr = list.items.ptr + ((self.element_count - 1) * self.value_data.value_size);
try self.value_data.value_parser(value_ptr, value);
list.items.len += self.value_data.value_size;
},
}
}

/// finalize and destroy
finalize: *const fn (impl_ptr: *anyopaque) anyerror!void,
};
pub fn finalize(self: *Self, alloc: std.mem.Allocator) anyerror!void {
switch (self.value_type) {
.single => {},
.multi => |*list| {
var sl = try list.toOwnedSlice(alloc);
sl.len = self.element_count;

pub fn put(self: *Self, value: []const u8) anyerror!void {
return self.vtable.put(self.impl_ptr, value);
}
pub fn finalize(self: *Self) anyerror!void {
return self.vtable.finalize(self.impl_ptr);
var dest: *[]u8 = @alignCast(@ptrCast(self.dest));
dest.* = sl;
},
}
}
};

// TODO: can ValueRef be an enum????
const ValueType = union(enum) {
single,
multi: std.ArrayListUnmanaged(u8),
};

const AllocError = std.mem.Allocator.Error;
pub const Error = AllocError; // | error{NotImplemented};

pub const Context = struct {
alloc: std.mem.Allocator,

pub fn init(a: std.mem.Allocator) Context {
return .{
.alloc = a,
};
}

fn singleValueRef(ctx: *Context, comptime T: type, dest: *T, parser: vp.ValueParser(T)) AllocError!ValueRef {
const Impl = struct {
dest: *T,
parser: vp.ValueParser(T),
alloc: std.mem.Allocator,

const Self = @This();

fn put(ptr: *anyopaque, value: []const u8) anyerror!void {
const self: *Self = @ptrCast(@alignCast(ptr));
try self.parser(self.dest, value);
}
fn finalize(ptr: *anyopaque) anyerror!void {
const self: *Self = @ptrCast(@alignCast(ptr));

// TODO: clarify what to do with copied strings???
self.alloc.destroy(self);
}
};

// FIXME: this must be destroyed
const im = try ctx.alloc.create(Impl);
im.* = .{
.dest = dest,
.parser = parser,
.alloc = ctx.alloc,
};

return ValueRef{ .impl_ptr = im, .vtable = &.{
.put = Impl.put,
.finalize = Impl.finalize,
} };
}

pub fn sliceRef(ctx: *Context, comptime T: type, dest: *[]const T, parser: vp.ValueParser(T)) AllocError!ValueRef {
const List = std.ArrayList(T);
const Impl = struct {
dest: *[]const T,
parser: vp.ValueParser(T),
list: List,
alloc2: std.mem.Allocator,

const Self = @This();

fn put(ptr: *anyopaque, value: []const u8) anyerror!void {
const self: *Self = @ptrCast(@alignCast(ptr));
var x: T = undefined;
try self.parser(&x, value);
try self.list.append(x);
pub fn valueRef(dest: anytype) ValueRef {
const ti = @typeInfo(@TypeOf(dest));
const t = ti.Pointer.child;

switch (@typeInfo(t)) {
.Pointer => |pinfo| {
switch (pinfo.size) {
.Slice => {
if (pinfo.child == u8) {
return ValueRef{
.dest = @ptrCast(dest),
.value_data = vp.getValueData(t),
.value_type = .single,
};
} else {
return ValueRef{
.dest = @ptrCast(dest),
.value_data = vp.getValueData(pinfo.child),
.value_type = ValueType{ .multi = std.ArrayListUnmanaged(u8){} },
};
}
},
else => @compileError("unsupported value type"),
}
fn finalize(ptr: *anyopaque) anyerror!void {
const self: *Self = @ptrCast(@alignCast(ptr));
self.dest.* = try self.list.toOwnedSlice();
self.alloc2.destroy(self);
}
};

// FIXME: this must be destroyed
const im = try ctx.alloc.create(Impl);
im.* = .{
.dest = dest,
.parser = parser,
.list = List.init(ctx.alloc),
.alloc2 = ctx.alloc,
};

return ValueRef{ .impl_ptr = im, .vtable = &.{
.put = Impl.put,
.finalize = Impl.finalize,
} };
}

pub fn valueRef(ctx: *Context, comptime dest: anytype) Error!ValueRef {
const ti = @typeInfo(@TypeOf(dest));
const t = ti.Pointer.child;

switch (@typeInfo(t)) {
.Pointer => |pinfo| {
switch (pinfo.size) {
.Slice => {
const p = vp.get(pinfo.child);
return ctx.sliceRef(pinfo.child, dest, p);
},
else => @compileError("unsupported value type"),
}
},
else => {
const p = vp.get(t);
return ctx.singleValueRef(t, dest, p);
},
}
},
else => {
return ValueRef{
.dest = @ptrCast(dest),
.value_data = vp.getValueData(t),
.value_type = .single,
};
},
}
};
}

0 comments on commit 07b458f

Please sign in to comment.