2309d自定义属性

原文
D中的用户定义属性(UDA)主要用来编译时反省.它允许你用编译时元数据标记声明,然后其他代码可在编译时从中检查这些元数据.
命令行选项解析代码,传递"--help"选项时,它会打印出自动生成的帮助文本.想法是在结构中封装命令行选项,如:

struct Options
{
     double xmin;
     double xmax;
     double ymin;
     double ymax;
     int xres;
     int yres;
}

可用编译时反省来迭代结构成员并按选项名使用字段名(如,这样你就可带'--xmin=1.0 --ymax=10.0'参数等运行程序).
可在没有UDA时就这样,但假设想更进一步,让"--help"自动打印出所有可用选项.当然,可手写一个按大串打印此信息的showHelp()函数,但帮助文本Options结构的定义分离了.

如果随后修改"选项"结构,则帮助文本将过时,并且可能包含误导性或错误信息.如果可直接嵌入帮助文本到选项定义中会更好,这样"--help"*总是*是最新的.
此方法是给它添加UDA:

struct Desc { string text; }
struct Options
{
     @Desc("Minimum X-coordinate") double xmin;
     @Desc("Maximum X-coordinate") double xmax;
     @Desc("Minimum Y-coordinate") double ymin;
     @Desc("Maximum Y-coordinate") double ymax;
     @Desc("Output horizontal resolution") int xres;
     @Desc("Output vertical resolution") int yres;
}

这些UDA自身无操作.但是现在你可在showHelp()函数中检查它们,并按输出的一部分打印出UDA串:

void showHelp() {
    Options opts;
    writefln("Usage: myprogram [options]");
    writeln("Options:");
    foreach (field; __traits(allMembers(Options))) {
        alias desc = getUDAs!(mixin("opts."~field), Desc);
        writefln("--%s", field);
        if (desc.length > 0)
            writefln("\t%s", desc);
    }
}

现在showHelp()打印出选项中每个字段的描述,并且只要在更改字段定义时,更新选项中的UDA,它会总是显示正确信息.

还可自动执行选项区间.如,如果Options.xres必须介于105000之间,则可显式编写代码来检查这一点,但同样,检查定义选项分离了,因此它可能会不同步.
但是使用UDA,可添加此信息选项定义中,并让解析选项的代码自动强制:

struct Desc { string text; }
struct Range { int minVal, maxVal; }
struct Options
{
     @Desc("Minimum X-coordinate") double xmin;
     @Desc("Maximum X-coordinate") double xmax;
     @Desc("Minimum Y-coordinate") double ymin;
     @Desc("Maximum Y-coordinate") double ymax;
     @Range(10, 5000) @Desc("Output horizontal resolution") int xres;
     @Range(8, 4000) @Desc("Output vertical resolution") int yres;
}

然后在解析选项的代码中:

void parseOption(ref Options opts, string name, string value) {
    foreach (field; __traits(allMembers(Options))) {
        if (name == field) {
            import std.conv : to;
            alias T = typeof(mixin("opts."~field));
            T val = value.to!T; //解析选项值
            //自动强制选项值区间
            alias range = getUDAs!(mixin("opts."~field), Range);
            if (range.length > 0) {
                enforce(val >= range.minVal,
                    "value of "~field~" is below minimum");
                enforce(val <= range.maxVal,
                    "value of "~field~" is above maximum");
            }
            //通过检查区间(或未找到`UDA`区间),把解析的值`赋值`给`选项结构`.
            mixin("opts."~field) = val;
        }
    }
}

现在只需更新"选项"中的UDA区间,parseOption会自动强制新区间.在A位置更改代码,会自动更新检查UDA所有其他代码.

还可用UDA标记需要特殊处理的选项.如,假设选项中的int字段,一般只是从10基数转换而来,但一个指定的字段(如"octalvalue")需要用户输入八进制.

与其在parseOption中为每个此类异常编写if-then-else意大利面条,不如创建一个附加到"octalvalue"定义中的如Octal的新的UDA.
然后在parseOption()中,检查此UDA字段,如果存在,则用8为基数而不是用10为基数来解析值.

对只用一个if语句就可完成的事情来说,这似乎太多,但是如果未来需要来另一个八进制字段怎么办?使用UDA,只需在选项定义中附加它,一切都会正常工作(tm).
浏览选项定义,会立即告诉你按八进制解析此字段;无需深入研究parseOption()代码就知道.UDA本身就是文档,这是件好事.

你可能感兴趣的:(dlang,d,d)