Created
April 11, 2018 17:15
-
-
Save alexnask/41abc5ce1c76bbd9f41608331f9545d8 to your computer and use it in GitHub Desktop.
Tuple types in zig
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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