#"Type erasing anonymous struct"

1 messages · Page 1 of 1 (latest)

dark elk
#

I'm trying to make my own implementation of a thread pool and when I create a task I want to be able to pass in an anonymous struct (.{ arg1, arg2, ... }) and be able to keep that in memory until the task is executed with @call(.auto, func, args). Is this possible?

#

the ideal api would be schedule(function, .{ args });

primal otter
#

you can allocate @TypeOf(args) like std.Thread does

dark elk
#

hmm but would i be able to put that in a struct?

const Task = struct {
  fn_ptr: *const fn (???) void,
  ctx: ???,
};
primal otter
#

itd have to be a generic struct, or use a type erased pointer and somehow get the type info back

dark elk
#

yeah i have no clue how i would un-type erase an anonymous struct

#

maybe with a comptime struct but then how would i dynamically schedule tasks?

primal otter
#

they alloc a Closure that contains the args and captures the function at comptime, then calls that later

dark elk
#

huh. I'll see if i can make this work

#

thanks for the pointer :)

primal otter
#

np zeroLike

slender adder
#

proof of concept that seems like it may apply:

const Task = struct {
    args: *const anyopaque,
    doFn: *const fn (args: *const anyopaque) void,
    deinit: *const fn (std.mem.Allocator, args: *const anyopaque) void,
};

fn createTask(
    allocator: std.mem.Allocator,
    comptime func: anytype,
    args: anytype,
) !Task {
    const gen = struct {
        fn doFn(ptr: *const anyopaque) void {
            const args_ptr: *const @TypeOf(args) = @ptrCast(@alignCast(ptr));
            @call(.auto, func, args_ptr.*);
        }

        fn deinit(ally: std.mem.Allocator, ptr: *const anyopque) void {
            const args_ptr: *const @TypeOf(args) = @ptrCast(@alignCast(ptr));
            ally.destroy(args_ptr);
        }
    };

    const args_duped = try allocator.create(@TypeOf(args));
    args_duped.* = args;

    return .{
        .args = args_duped,
        .doFn = gen.doFn,
        .deinit = gen.deinit,
    };
}
#

type-erasing "function calls" basically consists of generating a wrapper function that takes in the type erarsed pointer, casting that back to the type of the argument tuple type, and calling the function with that tuple - then storing a pointer to the tuple or a copy of it in the struct with the function pointer generated based on the original function + argument tuple pair

dark elk
#

so you basically generate a function that already knows what args should be and casts the anyopaque to that type! i love you inkryption