Wednesday, August 27, 2014

Survey analytics, mixins!

Currently I'm working on my Degree in ICT industry project. For this I am using D.
I'm currently in the requirements phase and doing some requirements gathering. To make sense of all this data I'm writing a tool in D to analyse it.

Unfortunately I had code such as:
YesNoSometimes assmtfdbkParse(string text) {
string[] stes = text.split(",");
foreach(ste; stes) {
switch(ste) {
case "Yes definately":
return YesNoSometimes.Yes;
case "Depends on the student":
return YesNoSometimes.Sometimes;
default:
return YesNoSometimes.No;
}
}
return YesNoSometimes.No;
}
view raw gistfile1.d hosted with ❤ by GitHub
mixin template QParse(string name, string defaultv, RET, ARGS...) if ((EnumMembers!RET).length-1 == ARGS.length) {
mixin("""RET " ~ name ~ "(string text) {
string[] stes = text.split(\",\");
foreach(ste; stes) {
switch(ste) {
" ~ QParseGrabCases!(RET, ARGS) ~ "
default:
return RET." ~ defaultv ~ ";
}
}
return RET." ~ defaultv ~ ";
}""");
}
mixin template QParseOther(string name, RET, ARGS...) if ((EnumMembers!RET).length-1 == ARGS.length) {
mixin("""RET " ~ name ~ "(string text, ref string other) {
string[] stes = text.split(\",\");
foreach(ste; stes) {
switch(ste) {
" ~ QParseGrabCases!(RET, ARGS) ~ "
case \"Other\":
continue;
default:
other = ste;
return RET.Other;
}
}
return RET.Other;
}""");
}
pure string QParseGrabCases(RET, ARGS...)() {
string ret;
enum members = EnumMembers!RET;
foreach(i, arg; ARGS) {
ret ~= " case \"" ~ arg ~ "\":\n";
ret ~= " return " ~ members[i].stringof ~ ";\n";
}
return ret;
}
mixin template QParseOtherBitwise(string name, RET, ARGS...) if ((EnumMembers!RET).length-2 == ARGS.length) {
mixin("""size_t " ~ name ~ "(string text, ref string other) {
size_t ret;
string[] stes = text.split(\",\");
foreach(ste; stes) {
switch(ste) {
" ~ QParseGrabCasesBitwise!(RET, ARGS) ~ "
case \"Other\":
continue;
default:
other = ste;
ret |= RET.Other;
break;
}
}
return ret;
}""");
}
pure string QParseGrabCasesBitwise(RET, ARGS...)() {
string ret;
enum members = (EnumMembers!RET)[1 .. $];
foreach(i, arg; ARGS) {
ret ~= " case \"" ~ arg ~ "\":\n";
ret ~= " ret |= " ~ members[i].stringof ~ ";\n";
ret ~= " break;\n";
}
return ret;
}
view raw gistfile2.d hosted with ❤ by GitHub

Because I love templates, I created mixin templates called QParse, QParseOther and QParseOtherBitwise. These had "business rules" mixed in it e.g. a bitwised enum for RET must include None and Other in it.

Code like the top example could then be replaced by:

mixin QParseOther!("yesNoOther", YesNoOther, "Yes", "No");

One line instead of like 15 lines. I really don't feel like a code monkey anymore.
This blog post really does just show how much I love D's templates and compile time functionality.

No comments:

Post a Comment