#CLI parsing comptime struct with assigned type

1 messages · Page 1 of 1 (latest)

inland leaf
#

I am writing a CLI argument parser, hopefully in comptime and am wondering how I can create a struct based on another struct at comptime.

The standard library already has the perfect struct for what I want, I just want to be able to give it a type at compile time to be able to parse the values to that type later (and generate structs with the correct types).

    pub const StructField = struct {
        name: [:0]const u8,
        type: type,
        /// The type of the default value is the type of this struct field, which
        /// is the value of the `type` field in this struct. However there is no
        /// way to refer to that type here, so we use `*const anyopaque`.
        /// See also: `defaultValue`.
        default_value_ptr: ?*const anyopaque,
        is_comptime: bool,
        alignment: comptime_int,

        /// Loads the field's default value from `default_value_ptr`.
        /// Returns `null` if the field has no default value.
        pub inline fn defaultValue(comptime sf: StructField) ?sf.type {
            const dp: *const sf.type = @ptrCast(@alignCast(sf.default_value_ptr orelse return null));
            return dp.*;
        }
    };

I'd like to get something similar to thisStructField but allowing me to set a type T to use as the type to parse the CLI argument to later

#

This is the code I have "working ish" initially, where I can parse the value I get at runtime based on a type I set at compile time

pub fn parseArgs(args: *std.process.ArgIterator, comptime Args: type) !Args {
    var result: Args = undefined;
    inline for (comptime std.meta.fields(Args)) |field| {
        @compileLog(field);
        //assert(@typeInfo(field) == .@"struct");
        if (std.mem.eql(u8, field.name, "arg_type")) {
            const value = try std.fmt.parseInt(field.type, args.next() orelse "", 10);
            @field(result, field.name) = value;
        }
    }
    return result;
}

pub fn PositionalArg(comptime T: type) type { //, comptime description: []const u8) type {
    return struct {
        arg_type: T,
        //comptime description = description,
    };
}
near meteor
#

generally you'd use @Type() to construct a type at compile time, by building up a slice of StructFields

#

you might look at std.meta.FieldEnum() for an example doing that where it builds an enum type based on a struct.

#

also if your'e more specific about the struct you want to create, the std lib might already have it.

#

for instance, to keep track of which fields you've seen, you might pass a std.meta.FieldEnum(T) to std.EnumSet

inland leaf
#

For example I’d like to create my own “Types” that a user of the library must adhere to in order for the CLI parser to work.

Almost like they have to inherit from it, so maybe not the best pattern to use with zig.

Something like:

const PositionalArgument = struct {
value_type: type,
required: bool,
description: []const u8,
}

And then the user of said library could create a struct based on the one above giving a value to each of the fields, especially important the type field of course. All of this should be possible compile time since it doesn’t actually need any runtime information

near meteor
#

in that case, seems like your PositionalArgument() type fn would work. what was missing?

inland leaf
#

It works, was wondering if there was a more “zig-like” approach to the problem

near meteor
#

i don't really know what you'd like to improve. maybe share some code with the API you'd like to have. and perhaps that will help show what you'd like to improve.