digitalmars.D.learn - mixin string to template - advice needed
- Marek Janukowicz (62/62) Jul 26 2013 Hello
- anonymous (101/142) Jul 26 2013 I've had a go at it (explanatory comments inside):
- monarch_dodra (10/11) Jul 27 2013 Yay! +1 for template mixin over "raw" mixins.
- Marek Janukowicz (8/8) Jul 27 2013 Thank for for your elaborated example - I especially liked the comments
Hello I have a repetitive piece of code that I'm now generating using string mixin. I humbly ask someone more skilled with D to review the code and help me transforming it into regular template mixin (as I would gladly avoid string mixin if possible). string flaggedAttr(string type, string name, string capName, string upName ) { return " property bool has" ~ capName ~" () { return (_wildcards & MatchWildcard." ~ upName ~") > 0; } property bool has" ~ capName ~ " ( bool has ) { if (has) _wildcards |= MatchWildcard." ~ upName ~ "; else _wildcards &= ~MatchWildcard." ~ upName ~ "; return has; } property " ~ type ~ " " ~ name ~ " () in { assert( has" ~ capName ~ "(), \"" ~ capName ~ " not set in wildcards\" ); } body { return _" ~ name ~ "; } property " ~ type ~ " " ~ name ~ " (" ~ type ~ " val) { has" ~ capName ~ " = true; _" ~ name ~ " = val; return _" ~ name ~ "; } "; } ( ... and the in a struct ... ) mixin(flaggedAttr( "PortNumber", "inPort", "InPort", "IN_PORT" )); ( ... which results in ... ) property bool hasInPort () { return (_wildcards & MatchWildcard.IN_PORT) > 0; } property bool hasInPort ( bool has ) { if (has) _wildcards |= MatchWildcard.IN_PORT; else _wildcards &= ~MatchWildcard.IN_PORT; return has; } property PortNumber inPort () in { assert( hasInPort(), "InPort not set in wildcards" ); } body { return _inPort; } property PortNumber inPort (PortNumber port) { hasInPort = true; _inPort = port; return _inPort; } The problems I struggle to solve: * dynamic method names (like "hasInPort" created from argument "inPort") * different versions of argument string (eg. "InPort" and "IN_PORT" from argument "inPort") * getting value from enum (MatchWildcard) give the name of the value (eg. "IN_PORT") Thank you for any help
Jul 26 2013
On Friday, 26 July 2013 at 20:33:29 UTC, Marek Janukowicz wrote:I have a repetitive piece of code that I'm now generating using string mixin. I humbly ask someone more skilled with D to review the code and help me transforming it into regular template mixin (as I would gladly avoid string mixin if possible). string flaggedAttr(string type, string name, string capName, string upName ) { return " property bool has" ~ capName ~" () { return (_wildcards & MatchWildcard." ~ upName ~") > 0; } property bool has" ~ capName ~ " ( bool has ) { if (has) _wildcards |= MatchWildcard." ~ upName ~ "; else _wildcards &= ~MatchWildcard." ~ upName ~ "; return has; } property " ~ type ~ " " ~ name ~ " () in { assert( has" ~ capName ~ "(), \"" ~ capName ~ " not set in wildcards\" ); } body { return _" ~ name ~ "; } property " ~ type ~ " " ~ name ~ " (" ~ type ~ " val) { has" ~ capName ~ " = true; _" ~ name ~ " = val; return _" ~ name ~ "; } "; } ( ... and the in a struct ... ) mixin(flaggedAttr( "PortNumber", "inPort", "InPort", "IN_PORT" ));[...]The problems I struggle to solve: * dynamic method names (like "hasInPort" created from argument "inPort") * different versions of argument string (eg. "InPort" and "IN_PORT" from argument "inPort") * getting value from enum (MatchWildcard) give the name of the value (eg. "IN_PORT")I've had a go at it (explanatory comments inside): import std.array: front; import std.conv: to; import std.range: drop; /* You can pass the field via a template alias parameter. Then use typeof to get its type, and stringof to get its name: */ mixin template flaggedAttr(alias field) { alias Type = typeof(field); static assert(field.stringof.front == '_'); enum name = drop(field.stringof, 1); /* dropping the underscore */ /* Generating "InPort" from "inPort" is trivial: capitalize the first character: */ enum capName = name.front.toUpper.to!string ~ drop(name, 1); /* To generate "IN_PORT", I wrote a little function 'underscorish' (awful name; implementation further down). You can heavily reduce the amount of string mixin by using it only to translate between names of the surrounding scope and local names. */ mixin("enum flag = MatchWildcard." ~ underscorish(name) ~ ";"); mixin("alias field = _" ~ name ~ ";"); /* Now, method implementations just use 'flag' and 'field'. Don't worry about name clashes. Every mixin has its own namespace. */ property bool has() { return (_wildcards & flag) > 0; } property bool has(bool has) { if (has) _wildcards |= flag; else _wildcards &= ~flag; return has; } property Type prop() in { assert(has(), capName ~ " not set in wildcards"); } body { return field; } property Type prop(Type val) { has = true; field = val; return field; } /* Making the implementations known by the desired names: */ mixin("alias has" ~ capName ~ " = has;"); mixin("alias " ~ name ~ " = prop;"); } import std.uni: isUpper, toUpper; private char[] underscorish(const(char)[] s) { char[] result; foreach(dchar c; s) { if(isUpper(c)) result ~= '_'; result ~= toUpper(c); } return result; } unittest { assert(underscorish("fooBarBaz") == "FOO_BAR_BAZ"); } /* Testing the whole thing: */ enum MatchWildcard { IN_PORT = 1, FOO_BAR = 1 << 1, } struct S { private uint _wildcards = 0; alias PortNumber = uint; private PortNumber _inPort; mixin flaggedAttr!_inPort; private uint _fooBar; mixin flaggedAttr!_fooBar; } void main() { S s; assert(!s.hasInPort); s.inPort = 123; assert(s.inPort == 123); assert(s.hasInPort); assert(!s.hasFooBar); s.fooBar = 321; assert(s.fooBar == 321); assert(s.hasFooBar); }
Jul 26 2013
On Friday, 26 July 2013 at 22:29:44 UTC, anonymous wrote:I've had a go at it (explanatory comments inside):Yay! +1 for template mixin over "raw" mixins. BTW, you can simply your expression: mixin("enum flag = MatchWildcard." ~ underscorish(name) ~ ";"); By only mixing in the value itself: enum flag = mixin("MatchWildcard." ~ underscorish(name)); This improves clarity a little bit. Unfrotunatly, this doesn't seem to work for the alias line: mixin("alias field = _" ~ name ~ ";"); I'm unsure if this is a bug.
Jul 27 2013
Thank for for your elaborated example - I especially liked the comments :) This really shed some light for me on how much information you can get from template "alias" parameter. I also didn't realize you can use small string mixins just for small snippets that are difficult to implement with a regular template. -- Marek Janukowicz
Jul 27 2013