Skip to content

Instantly share code, notes, and snippets.

@rikkimax
Last active February 5, 2024 16:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rikkimax/812de12e600a070fe267f3bdc1bb3928 to your computer and use it in GitHub Desktop.
Save rikkimax/812de12e600a070fe267f3bdc1bb3928 to your computer and use it in GitHub Desktop.

Call Site UDA's

Requirements

Can only be used for functions that are templated, on runtime parameters.

Storage

A UDA on the argument, will be stored with the argument position.

After symbol lookup is completed, and it is time to determine if a template instantiation is required, the UDA's from all arguments will be used as an extra criteria for the template instantion.

This will affect the name mangling of a template instance, by including the UDA's to make the unique.

The storage of the call site UDA will be upon the parameter position, and for alias sequence, will be with respect to the elements index also.

Access

To access a UDA on a parameter, use __traits(getAttributes, parameter).

Do not use the parameter type, this does not identify the parameter.

The trait getAttributes will be extended to support an optional filter parameter for the type to prevent the need for templates.

A new trait hasAttribute with a filter parameter that specifies a type is added determine if a given attribute type is available on a symbol.

Examples

CLI

CLI access ala std.getopt has a common need for providing extra meta data with a value.

string username, password;

getopt(
    @description("My program")
    @description("Second line")
    commandsInfo,
    @description("My programs help info")
    @flag("help") @flag("h") helpInfo,
    @description("The username to connect with")
    @flag("username") @flag("u") username,
    @description("The password to connect with")
    @flag("password") @flag("p") password
);
shared CommandsInfo commandsInfo;
shared HelpInfo helpInfo;

void getopt(Args...)(ref string[] cli, ref Args args) {
    void printHeader() {
        static foreach(i; 0 .. Args.length) {
            static if (is(args[i] : typeof(commandsInfo))) {
                foreach(programDescription; __traits(getAttributes, args[i], description))) {
                    // TODO: writeln programDescription
                }
            }
        }
    }
    
    void printHelpEntry(string flag, string description) {
        if (flag is null) {
            // TODO: write fill flag
        } else {
            // TODO: write flag
        }
        
        if (description !is null) {
            // TODO: write fill between flag and description
            // TODO: write description
        }
        
        // TODO: writeln
    }
    
    void printHelp() {
        static foreach(i; 0 .. Args.length) {
            static if (is(args[i] : typeof(commandsInfo))) {
            } else {
                flag[] flags = [__traits(getAttributes, args[i], flag)];
                description[] descriptions = [__traits(getAttributes, args[i], description)];
                
                if (flags.length < descriptions.length) {
                    foreach(i; 0 .. flags.length) {
                        printHelpEntry(flags[i], descriptions[i]);
                    }
                    
                    foreach(i; flags.length .. descriptions.length) {
                        printHelpEntry(null, descriptions[i]);
                    }
                } else {
                    foreach(i; 0 .. descriptions.length) {
                        printHelpEntry(flags[i], descriptions[i]);
                    }
                    
                    foreach(i; descriptions.length .. flags.length) {
                        printHelpEntry(flags[i], null);
                    }
                }
                
                foreach(programDescription; )) {
                    // TODO: writeln programDescription
                }
            }
        }
    }

    foreach(str; cli) {
        switch(str) {
            static foreach(i; 0 .. Args.length) {
                static foreach(f; __traits(getAttributes, args[i], flag)) {
                    case "--" ~ f.value:
                        str = str[1 .. $];
                        goto case "-" ~ f;
                    case "-" ~ f.value:
                        str = str[1 .. $];
                        // TODO: remove from cli array
                        
                    static if (is(Args[i] : typeof(commandsInfo)) {
                    } else static if (is(Args[i] : typeof(helpInfo)) {
                        foreach(help; __traits(getAttributes, args[i], typeof(helpInfo))) {
                            // TODO: writeln help.value
                        }
                        printHelp;
                    } else {
                        printHeader;
                        str.formattedRead(args[i]);
                    }
                        return;
                }
            }
            
            default:
                // TODO: unrecognized option
                return;
        }
    }
}

Interpolated Strings

This has the same capability of 1036e, except it does not rely on templates to convey the additional information.

i"prefix$(expr)suffix"

Becomes:

@IPrefix("prefix")
@ISuffix("suffix")
@IExpression("expr")
expr

This can be extended to support a single identifier, positional arguments, as well as formatting.

i"$ident$(expr)$(ident:format)${1:format}"

Becomes:

@IExpression("ident")
ident,
@IExpression("expr")
expr,
@IExpression("ident")
@IFormat("format")
ident,
@IFormat("format")
@IPosition(1)
IPosition(1)

SQL

db.execi(i"SELECT * FROM items WHERE id = $desiredId");

Becomes:

db.execi(@IPrefix("SELECT * FROM items WHERE id = ") desiredId);

For the function:

auto execi(Args...)(Sqlite db, Args args) {
    enum query = () {
        string ret;

        static foreach(i; 0 .. Args.length) {
            foreach(prefix; __traits(getAttributes, args[i], IPrefix)) {
                ret ~= prefix.value;
            }
            ret ~= "?";
            ret ~= i.text;
        }

        return ret;
    }();

    auto statement = Statement(db, query);
    static foreach (i, arg; args)
        statement.bind(i + 1, arg);
    return statement.execute();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment