December 17, 2014
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)));
    }
December 17, 2014
I've no idea why the forum decided to wrap all code; anyway:

https://gist.github.com/aldanor/ddc45b2710a2deb9ee2b