www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - A mixin template for automatic property generation (criticize my

reply "aldanor" <i.s.smirnov gmail.com> writes:
I'm trying (hard) to learn D templating, and (thanks to the great 
help on this forum) I've been able to solve one of the recent 
tasks I've been struggling about for a while. Wonder if anyone 
would take a moment to criticize the code so we rookies could 
learn?

Could this be done more elegantly?

P.S. will there ever be a static foreach for declarations?..

P.P.S. this is a great community, thanks again for all who 
answered to my previous questions :)

Task at hand: there's a bunch of "private" variables (extern 
globals from a C library) scattered across a number of modules 
whose definitions look like this:

     private extern (C) __gshared int foo_g;
     private extern (C) __gshared long bar_g;

for which one needs to generate module-level accessors like these:

     int foo()  property {
         func();
         return foo_g;
     }

     long bar()  property {
         func();
         return bar_g;
     }

where "func" is some callable.

Ideally, it would be done like so:

     mixin makeModuleProperties("_g", func);

and it should respect qualified names and work across modules 
(that was the biggest problem -- just generating a huge code 
string and mixing it in turned out to be too fragile with respect 
to fully qualified names, and quite a mess in general).

Solution:

     import std.traits;
     import std.string;
     import std.typetuple;

     template ID(alias T) {
         alias ID = T;
     }

     private bool _propertyNameMatches(alias parent, string 
suffix, alias name)() {
         static if (!__traits(compiles, __traits(getMember, 
parent, name)))
             return false;
         else {
             alias symbol = ID!(__traits(getMember, parent, name));
             enum name = __traits(identifier, symbol);
             enum n = suffix.length;
             static if (!is(symbol) && is(typeof(symbol))) // 
variables only
                 return (name.length > n) && (name[$ - n .. $] == 
suffix);
             else
                 return false;
         }
     }

     private mixin template _makeProperty(alias parent, string 
suffix, alias func, string name) {
         mixin(("typeof(__traits(getMember, parent, name)) %s() 
 property "
               ~ "{ func(); return __traits(getMember, parent, 
name); }").format(
               name[0 .. $ - suffix.length]));
     }

     private mixin template _makeProperties(alias parent, string 
suffix, alias func, names...) {
         static if (names.length > 0) {
             static if (_propertyNameMatches!(parent, suffix, 
names[0]))
                 mixin _makeProperty!(parent, suffix, func, 
names[0]);
             mixin _makeProperties!(parent, suffix, func, names[1 
.. $]);
         }
     }

     mixin template makeProperties(alias parent, string suffix, 
alias func = {}) {
         static if (__traits(compiles, __traits(allMembers, 
parent)))
             mixin _makeProperties!(parent, suffix, func, 
__traits(allMembers, parent));
     }

     mixin template makeModuleProperties(string suffix, alias func 
= {}) {
         mixin makeProperties!(mixin(__MODULE__), suffix, func);
     }

     unittest {
         struct Foo {
             static int x_g = 1;
             static int y_g = 2;
             static int z = 3;
             static mixin makeProperties!(Foo, "_g");
         }
         int counter = 0;
         mixin makeProperties!(Foo, "_g", { counter++; });
         assert(Foo.x == 1);
         assert(Foo.y == 2);
         assert(x == 1);
         assert(counter == 1);
         assert(y == 2);
         assert(counter == 2);
         static assert(!is(typeof(z)));
     }
Dec 17 2014
parent "aldanor" <i.s.smirnov gmail.com> writes:
I've no idea why the forum decided to wrap all code; anyway:

https://gist.github.com/aldanor/ddc45b2710a2deb9ee2b
Dec 17 2014