Generate generic tuple type from array at comptime

Hello,
I was wondering how to make the following return a generic tuple.
I have all the information I need at comptime, how long the return tuple should be (as long as the possibleArgs array)
and which type is on which position (the type of the DefaultValue). But I just don’t know how to set it up.

const String = []const u8;

const DefaultValue = union(enum) {
    number: i32,
    boolean: bool,
    string: String,
};

const Arg = struct { String, DefaultValue };

pub fn parseArgs(comptime possibleArgs: []const Arg, args: []const String) struct { String, bool } {
    const short1, const defaultValue1 = possibleArgs[0];
    const short2, const defaultValue2 = possibleArgs[1];

    const value1 = switch (defaultValue1) {
        .string => |str| parseStringArg(short1, str, args),
        else => unreachable,
    };
    const value2 = switch (defaultValue2) {
        .boolean => |bl| parseBoolArg(short2, bl, args),
        else => unreachable,
    };

    return .{
        value1,
        value2,
    };
}

Any help and suggestions are appreciated.

To give you some pointers in the right direction:

  • You can call a function to compute the return type like this:
fn ParseArgsType(...) type {...}
pub fn parseArgs(comptime possibleArgs...) ParseArgsType(possibleArgs) {...}
  • You can use the @Type builtin function to create a generic type, such as a tuple, at compile time. It takes the same data structure that you also get when calling @typeInfo, so you can test around with some example tuples for @typeInfo to figure out what you need.
1 Like

Thanks for all your help, much appreciated.
This is the solution I went with:

pub fn parseArgs(comptime possibleArgs: []const Arg, args: []const []const u8, allocator: Allocator) !ParseArgsType(possibleArgs) {
    var result: ParseArgsType(possibleArgs) = undefined;
    inline for (possibleArgs, 0..) |arg, i| {
        const short, const long, const defaultValue = arg;
        const field_name = comptime std.fmt.comptimePrint("{}", .{i});
        @field(result, field_name) = switch (defaultValue) {
            .string => |str| parseStringArg(short, long, str, args, allocator),
            .boolean => |bl| parseBoolArg(short, long, bl, args, allocator),
        };
    }
    return result;
}

fn ParseArgsType(comptime possibleArgs: []const Arg) type {
    const fields = comptime blk: {
        var result: [possibleArgs.len]std.builtin.Type.StructField = undefined;
        for (possibleArgs, 0..) |item, i| {
            const theType = switch (item[2]) {
                .string => |str| @TypeOf(str),
                .boolean => |bl| @TypeOf(bl),
            };
            result[i] = .{
                .name = std.fmt.comptimePrint("{d}", .{i}),
                .type = theType,
                .default_value = null,
                .is_comptime = false,
                .alignment = @alignOf(theType),
            };
        }
        break :blk result;
    };

    return @Type(.{
        .Struct = .{
            .layout = .auto,
            .fields = &fields,
            .decls = &.{},
            .is_tuple = true,
        },
    });
}