Skip to content

Instantly share code, notes, and snippets.

@alexnask
Created April 11, 2018 17:15
Show Gist options
  • Select an option

  • Save alexnask/41abc5ce1c76bbd9f41608331f9545d8 to your computer and use it in GitHub Desktop.

Select an option

Save alexnask/41abc5ce1c76bbd9f41608331f9545d8 to your computer and use it in GitHub Desktop.
Tuple types in zig
const std = @import("std");
const TypeId = @import("builtin").TypeId;
const allocPrint = std.fmt.allocPrint;
// This should be equivalent to Tuple(f32, f32)
fn TestTuple() type {
return struct {
const Self = this;
const Types = []type { f32, f32 };
field0: f32,
field1: f32,
fn init(arg0: f32, arg1: f32) Self {
return Self {
.field0 = arg0,
.field1 = arg1
};
}
fn get(self: &Self, comptime i: usize) &(@memberType(Self, i)) {
return if (i == 0) &self.field0 else &self.field1;
}
// Takes a series of pointers or nullable pointers and unpacks the tuple fields into them.
// null pointers are ignored.
fn unpack(self: &const Self, args: ...) void {
if (args.len == 0) {
@compileError("You have to unpack at least one field.");
}
if (args.len > Self.Types.len) {
@compileError("Cannot unpack more fields than the tuple has.");
}
comptime var i = 0;
inline while (i < args.len) : (i += 1) {
comptime var nullable = false;
if (@typeId(@typeOf(args[i])) == TypeId.Nullable) {
nullable = true;
}
comptime const arg_type = if (nullable) @typeOf(args[i]).Child else @typeOf(args[i]);
comptime const type_id = @typeId(arg_type);
if (type_id != TypeId.Pointer) {
@compileError("Must pass pointers to unpack.");
}
if (arg_type.Child != Self.Types[i]) {
@compileError("Invalid pointer type.");
}
if (nullable) {
if (args[i] != null) {
*??args[i] = if (i == 0) self.field0 else self.field1;
}
} else {
*args[i] = if (i == 0) self.field0 else self.field1;
}
}
}
};
}
fn ChildRefType(comptime ref_type: type, comptime i: usize) type {
// TODO: Check for constness of ref_type, propagate it to the result type
return &(@memberType(ref_type.Child, i));
}
// get the i'th field of a struct
pub fn get(self: var, comptime i: usize) ChildRefType(@typeOf(self), i) {
return if (i == 0) &self.field0 else &self.field1;
}
pub fn Tuple(comptime types: []type) type {
if (types.len == 0) {
@compileError("No types passed to Tuple.");
}
var allocator_data: [1024 * 8]u8 = undefined;
const alloc = &std.heap.FixedBufferAllocator.init(allocator_data).allocator;
var member_names: [types.len][]const u8 = undefined;
var member_types: [types.len][]const &TypeInfo = undefined;
for (types) |t, i| {
member_names[i] = allocPrint(alloc, "field{}", i) catch unreachable;
member_types[i] = @reflect(t);
}
// Free the member names upon exiting the function.
defer for (member_names) |m| {
alloc.free(m);
};
// Build our tuple type.
// These types should be builtin so no need for import, right?
const struct_detail = StructTypeInfoDetail.init(false, member_names, member_types);
const detail = TypeInfoDetail {
.Struct = struct_detail
};
// Perhaps:
// const detail = TypeInfoDetail.init(TypeId.Struct, false, member_names, member_types);
// TODO: Add methods
// init(args: ...), get(comptime i: usize) &types[i], ...
const type_info = TypeInfo.init(false, false, detail);
return @reify(type_info);
// TODO: Better TypeBuilder interface, look at the TypeId's currently available.
// var builder = TypeBuilder.init();
// builder.buildTypeAndStuff();
// @reify(builder)
}
fn vararg_types(args: ...) []type {
var ts: [args.len]type = undefined;
var i = 0;
while (i < args.len) : (i += 1) {
ts[i] = @typeOf(args[i]);
}
return ts;
}
pub fn make_tuple(args: ...) Tuple(vararg_types(args)) {
const TupleType = @typeOf(this).ReturnType;
var tup: TupleType = undefined;
inline for (args) |arg, i| {
*tup.get(i) = arg;
}
return tup;
}
pub fn main() !void {
var stdout_file = try std.io.getStdOut();
var adapter = std.io.FileOutStream.init(&stdout_file);
var stream = &adapter.stream;
const Point = TestTuple();
//const Point = Tuple(f32, f32);
var p = Point.init(0.0, 0.0);
try stream.print("({}, {})\n", *p.get(0), *p.get(1));
*p.get(0) = 1.0;
try stream.print("({}, {})\n", *p.get(0), *p.get(1));
*get(p, 1) = 1.0;
try stream.print("({}, {})\n", *p.get(0), *p.get(1));
*p.get(1) = 2.0;
var to_unpack: f32 = undefined;
var nullable: ?&f32 = null;
p.unpack(nullable, &to_unpack);
try stream.print("UNPACKED {}\n", to_unpack);
// const pp = Point.init(1.0, 1.0);
// *get(pp, 0) = 2.0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment