That’s final implementation:
const builtin = @import("builtin");
const std = @import("std");
const posix = std.posix;
const Allocator = std.mem.Allocator;
const AnyReader = std.io.AnyReader;
const AnyWriter = std.io.AnyWriter;
const InternalTypeInfo = @import("InternalType.zig");
pub fn readValue(comptime T: type, reader: AnyReader) !T {
const it = InternalTypeInfo.init(T);
return switch (it.type_info) {
.@"struct" => try reader.readStruct(T),
else => read_block: {
const buf = try reader.readBytesNoEof(it.size);
break :read_block std.mem.bytesToValue(T, &buf);
},
};
}
pub fn writeValue(comptime T: type, writer: AnyWriter, value: T) !void {
const it = InternalTypeInfo.init(T);
switch (it.type_info) {
.@"struct" => try writer.writeStruct(value),
else => try writer.writeAll(&std.mem.toBytes(@as(T, value))),
}
}
pub fn Input(comptime T: type) type {
const fn_ti = @typeInfo(T).@"fn";
var p_len = 0;
for (fn_ti.params) |p| {
if (p.type != null) {
p_len += 1;
}
}
var types: [p_len]type = undefined;
var i = 0;
for (fn_ti.params) |p| {
if (p.type != null) {
types[i] = p.type.?;
i += 1;
}
}
return std.meta.Tuple(&types);
}
pub fn callRemote(
comptime T: type,
reader: AnyReader,
writer: AnyWriter,
args: *Input(T),
) !@typeInfo(T).@"fn".return_type.? {
const fn_info = @typeInfo(T).@"fn";
const fn_ret = fn_info.return_type;
inline for (fn_info.params, 0..) |fn_param, i| {
const param_info = @typeInfo(fn_param.type.?);
switch (param_info) {
.pointer => |p| if (p.size == .many) {
const len = blk: {
const j: usize = comptime len_idx_blk: {
for (
fn_info.params[i + 1 ..],
i + 1..,
) |p_size, j| {
if (p_size.type.? == usize)
break :len_idx_blk j;
}
@compileError("Parameter's length not found!");
};
break :blk args[j];
};
try writeValue(usize, writer, len);
for (args[i][0..len]) |val| {
try writeValue(p.child, writer, val);
}
} else if (@typeInfo(p.child) == .pointer) {
@compileError("Parameter can't be pointer to pointer!");
} else try writeValue(p.child, writer, args[i].*),
else => {
try writeValue(fn_param.type.?, writer, args[i]);
},
}
}
loop: inline for (fn_info.params, 0..) |fn_param, i| {
const param_info = @typeInfo(fn_param.type.?);
switch (param_info) {
.pointer => |p| {
if (p.is_const) continue :loop;
if (p.size != .one) {
const len = try readValue(usize, reader);
for (args[i][0..len]) |*val| {
val.* = try readValue(p.child, reader);
}
} else {
args[i] = try readValue(p.child, reader);
}
},
else => continue :loop,
}
}
if (fn_ret) |ret| {
return try readValue(ret, reader);
}
}
pub fn callLocal(
comptime T: type,
@"fn": *const T,
reader: AnyReader,
writer: AnyWriter,
allocator: Allocator,
) !void {
const fn_info = @typeInfo(T).@"fn";
const fn_ret = fn_info.return_type;
var args: Input(T) = undefined;
loop: inline for (fn_info.params, 0..) |fn_param, i| {
const param_info = @typeInfo(fn_param.type.?);
switch (param_info) {
.pointer => |p| {
if (p.is_const) continue :loop;
if (p.size != .one) {
const len = try readValue(usize, reader);
const buf = try allocator.alloc(p.child, len);
args[i] = buf.ptr;
for (args[i][0..len]) |*val| {
val.* = try readValue(p.child, reader);
}
} else {
args[i] = try readValue(p.child, reader);
}
},
else => continue :loop,
}
}
defer {
loop: inline for (fn_info.params, 0..) |fn_param, i| {
const param_info = @typeInfo(fn_param.type.?);
switch (param_info) {
.pointer => |p| {
if (p.is_const) continue :loop;
if (p.size != .one) {
const len = blk: {
const j: usize = comptime len_idx_blk: {
for (
fn_info.params[i + 1 ..],
i + 1..,
) |p_size, j| {
if (p_size.type.? == usize)
break :len_idx_blk j;
}
@compileError("Parameter's length not found!");
};
break :blk args[j];
};
allocator.free(args[i][0..len]);
}
},
else => continue :loop,
}
}
}
const res = @call(.auto, @"fn", args);
inline for (fn_info.params, 0..) |fn_param, i| {
const param_info = @typeInfo(fn_param.type.?);
switch (param_info) {
.pointer => |p| if (p.size == .many) {
const len = blk: {
const j: usize = comptime len_idx_blk: {
for (
fn_info.params[i + 1 ..],
i + 1..,
) |p_size, j| {
if (p_size.type.? == usize)
break :len_idx_blk j;
}
@compileError("Parameter's length not found!");
};
break :blk args[j];
};
try writeValue(usize, writer, len);
for (args[i][0..len]) |val| {
try writeValue(p.child, writer, val);
}
} else if (@typeInfo(p.child) == .pointer) {
@compileError("Parameter can't be pointer to pointer!");
} else try writeValue(p.child, writer, args[i].*),
else => {
try writeValue(fn_param.type.?, writer, args[i]);
},
}
}
if (fn_ret) |ret| {
try writeValue(ret, writer, res);
}
}
As I’ve thought yet, @call
fits there. But I don’t know yet if this is correct approach for doing such type of stuff.
I think it’s hard to use this example in my case:
Since I have unknown amount of arguments… But may be I’m wrong.