Ziglings
:
001_hello.zig
002_std.zig
003_assignment.zig
004_arrays.zig
005_arrays2.zig
006_strings.zig
007_strings2.zig
008_quiz.zig
009_if.zig
010_if2.zig
011_while.zig
012_while2.zig
013_while3.zig
014_while4.zig
015_for.zig
016_for2.zig
017_quiz2.zig
018_functions.zig
019_functions2.zig
020_quiz3.zig
021_errors.zig
022_errors2.zig
023_errors3.zig
024_errors4.zig
025_errors5.zig
026_hello2.zig
027_defer.zig
028_defer2.zig
029_errdefer.zig
030_switch.zig
031_switch2.zig
032_unreachable.zig
033_iferror.zig
034_quiz4.zig
035_enums.zig
036_enums2.zig
037_structs.zig
038_structs2.zig
039_pointers.zig
040_pointers2.zig
041_pointers3.zig
042_pointers4.zig
043_pointers5.zig
044_quiz5.zig
045_optionals.zig
046_optionals2.zig
047_methods.zig
048_methods2.zig
049_quiz6.zig
050_no_value.zig
051_values.zig
052_slices.zig
053_slices2.zig
054_manypointers.zig
055_unions.zig
056_unions2.zig
057_unions3.zig
058_quiz7.zig
059_integers.zig
060_floats.zig
061_coercions.zig
062_loop_expressions.zig
063_labels.zig
064_builtins.zig
065_builtins2.zig
066_comptime.zig
067_comptime2.zig
068_comptime3.zig
069_comptime4.zig
070_comptime5.zig
071_comptime6.zig
072_comptime7.zig
073_comptime8.zig
074_comptime9.zig
075_quiz8.zig
076_sentinels.zig
077_sentinels2.zig
078_sentinels3.zig
079_quoted_identifiers.zig
080_anonymous_structs.zig
081_anonymous_structs2.zig
082_anonymous_structs3.zig
083_anonymous_lists.zig
084_async.zig
085_async2.zig
086_async3.zig
087_async4.zig
088_async5.zig
089_async6.zig
090_async7.zig
091_async8.zig
092_interfaces.zig
093_hello_c.zig
094_c_math.zig
095_for3.zig
096_memory_allocation.zig
097_bit_manipulation.zig
098_bit_manipulation2.zig
099_formatting.zig
100_for4.zig
101_for5.zig
102_testing.zig
103_tokenization.zig
104_threading.zig
105_threading2.zig
106_files.zig
107_files2.zig
108_labeled_switch.zig
109_vectors.zig
110_quiz9.zig
999_the_end.zig
const std = @import("std"); fn fromDirect(Fooer: type, context: *anyopaque) *Fooer { return @alignCast(@ptrCast(context)); } fn fromIndirect(Fooer: type, context: *const anyopaque) *Fooer { return @as(*const *Fooer, @alignCast(@ptrCast(context))).*; } fn fromHeader(context: *ErasedVariableSize) struct { *ErasedVariableSize.Header, *anyopaque } { return .{ @alignCast(@ptrCast(context)), @as([*]u8, @ptrCast(context)) + @sizeOf(ErasedVariableSize.Header) }; } const ErasedDirectly = struct { context: *anyopaque, fooFn: *const fn (context: *anyopaque, bar: u32) u32, resetFn: *const fn (context: *anyopaque) void, pub fn init(fooer: anytype) @This() { const Fooer = @typeInfo(@TypeOf(fooer)).pointer.child; const Fns = struct { pub fn typeErasedFoo(context: *anyopaque, bar: u32) u32 { return fromDirect(Fooer, context).foo(bar); } pub fn typeErasedReset(context: *anyopaque) void { fromDirect(Fooer, context).reset(); } }; return .{ .context = fooer, .fooFn = Fns.typeErasedFoo, .resetFn = Fns.typeErasedReset, }; } pub fn foo(self: @This(), bar: u32) u32 { return self.fooFn(self.context, bar); } pub fn reset(self: @This()) void { self.resetFn(self.context); } }; const ErasedIndirectly = struct { context: *const anyopaque, fooFn: *const fn (context: *const anyopaque, bar: u32) u32, resetFn: *const fn (context: *const anyopaque) void, pub fn init(fooer: anytype) @This() { const FooerPtr = @typeInfo(@TypeOf(fooer)).pointer.child; const Fooer = @typeInfo(FooerPtr).pointer.child; const Fns = struct { pub fn typeErasedFoo(context: *const anyopaque, bar: u32) u32 { return fromIndirect(Fooer, context).foo(bar); } pub fn typeErasedReset(context: *const anyopaque) void { fromIndirect(Fooer, context).reset(); } }; return .{ .context = @ptrCast(fooer), .fooFn = Fns.typeErasedFoo, .resetFn = Fns.typeErasedReset, }; } pub fn foo(self: @This(), bar: u32) u32 { return self.fooFn(self.context, bar); } pub fn reset(self: @This()) void { self.resetFn(self.context); } }; const ErasedDirectlyWithVTable = struct { context: *anyopaque, v_table: *const struct { fooFn: *const fn (context: *anyopaque, bar: u32) u32, resetFn: *const fn (context: *anyopaque) void, }, pub fn init(fooer: anytype) ErasedDirectlyWithVTable { const Fooer = @typeInfo(@TypeOf(fooer)).pointer.child; const Fns = struct { pub fn typeErasedFoo(context: *anyopaque, bar: u32) u32 { return fromDirect(Fooer, context).foo(bar); } pub fn typeErasedReset(context: *anyopaque) void { fromDirect(Fooer, context).reset(); } }; return .{ .context = fooer, .v_table = &.{ .fooFn = Fns.typeErasedFoo, .resetFn = Fns.typeErasedReset, }, }; } pub fn foo(self: @This(), bar: u32) u32 { return self.v_table.fooFn(self.context, bar); } pub fn reset(self: @This()) void { self.v_table.resetFn(self.context); } }; const ErasedVariableSize = opaque { pub const Header = struct { fooFn: *const fn (context: *anyopaque, bar: u32) u32, resetFn: *const fn (context: *anyopaque) void, }; fn bufferSize(Fooer: type) usize { return @sizeOf(Fooer) + @sizeOf(Header); } pub fn create(fooer: anytype, buffer: *align(8) [bufferSize(@TypeOf(fooer))]u8) *@This() { const Fooer = @TypeOf(fooer); const Fns = struct { pub fn typeErasedFoo(context: *anyopaque, bar: u32) u32 { return fromDirect(Fooer, context).foo(bar); } pub fn typeErasedReset(context: *anyopaque) void { fromDirect(Fooer, context).reset(); } }; @memcpy(buffer[0..@sizeOf(Header)], @as([*]const u8, @ptrCast(&Header{ .fooFn = Fns.typeErasedFoo, .resetFn = Fns.typeErasedReset, }))); @memcpy(buffer[@sizeOf(Header)..], @as([*]const u8, @ptrCast(&fooer))); return @ptrCast(buffer); } pub fn foo(self: *@This(), bar: u32) u32 { const header, const raw = fromHeader(self); return header.fooFn(raw, bar); } pub fn reset(self: *@This()) void { const header, const raw = fromHeader(self); header.resetFn(raw); } }; pub fn main() void { const Fooer = struct { data: u32 = 0xBEEF, pub fn foo(self: @This(), bar: u32) u32 { return self.data +% bar; } pub fn reset(self: *@This()) void { self.data = 0; } pub fn testTypeErasure(fooer: *@This(), type_erased_fooer: anytype) !void { std.debug.print("Testing {}\n", .{@TypeOf(type_erased_fooer)}); errdefer std.debug.print("\tfailed\n", .{}); if (fooer.foo(42) != type_erased_fooer.foo(42)) return error.FstFoo; fooer.reset(); type_erased_fooer.reset(); if (fooer.foo(42) != type_erased_fooer.foo(42)) return error.SndFoo; std.debug.print("\tsuccess\n", .{}); } }; { var fooer: Fooer = .{}; var fooer_for_erasure: Fooer = .{}; fooer.testTypeErasure(ErasedDirectly.init(&fooer_for_erasure)) catch {}; } { var fooer: Fooer = .{}; var fooer_for_erasure: Fooer = .{}; const fooer_ptr = &fooer_for_erasure; fooer.testTypeErasure(ErasedIndirectly.init(&fooer_ptr)) catch {}; } { var fooer: Fooer = .{}; var fooer_for_erasure: Fooer = .{}; fooer.testTypeErasure(ErasedDirectlyWithVTable.init(&fooer_for_erasure)) catch {}; } { var fooer: Fooer = .{}; var erased_fooer_buffer: [20]u8 align(8) = undefined; fooer.testTypeErasure(ErasedVariableSize.create(fooer, &erased_fooer_buffer)) catch {}; } }