most non-function templates could be advantageously replaced by function templates

Timothee Cour thelastmammoth at gmail.com
Mon May 13 23:56:09 PDT 2013


Phobos has a number of *non-function template* traits (std.traits,
std.typetuple etc), eg:

---- in std.traits:
template hasMember(T, string name) {
    static if (is(T == struct) || is(T == class) || is(T == union) || is(T
== interface))
        enum bool hasMember =
            staticIndexOf!(name, __traits(allMembers, T)) != -1 ||
            __traits(compiles, { mixin("alias Identity!(T."~name~") Sym;");
});
    else
        enum bool hasMember = false;
}
----

I see those *disadvantages* with non-function templates:

* syntax is less clear than regular function templates (there's no way to
tell the return type without looking at the code or using some convention
relating to the template name)

* they're not DRY, as the template name is typically repeated (maybe in
multiple places) inside the template body (hasMember appears 3 times here)

* behavior of templates is actually weird, for example:

----
 template A1(T){enum A1=0;}
template A2(T){enum B=0;}
template A3(T){enum A3=0;enum B=0;}
void main(){
    enum a1=A1!int;    //ok
    enum a2=A2!int.B;    //ok
    //    enum a3=A3!int.B;    //Error: no property 'B' for type 'int'
}
----

Maybe this was for historical reasons (ie CTFE might've come after those
design patterns were invented).

I would propose to use *regular template functions* instead, whenever
possible (essentially for the ones that return a value):

For our example, this would become:
----
 bool hasMember(T, string name)(){
    static if (is(T == struct) || is(T == class) || is(T == union) || is(T =
= interface))
        return staticIndexOf!(name, __traits(allMembers, T)) != -1 ||
                __traits(compiles, { mixin("alias Identity!(T."~name~") Sym;
"); });
    else return false;
}
// can be used in exactly the same way:
static assert(hasMember!(A,"field"));
----

*Advantages*:
* clear syntax: returns bool, and body is simpler to read as well
* DRY: hasMember only mentioned once
* no weird behavior as above
* doesn't break any code as usage is the same (backwards compatible)

In fact, I was able to convert with no problems a number of other such
templates:
 staticIndexOf
 isSame
 expectType
 genericIndexOf
etc...

For the latter (genericIndexOf), the template body contained a clause like
that:
 enum next  = genericIndexOf!(e, tail).index;
enum index = (next == -1) ? -1 : 1 + next;
during conversion, I just had to add the line:
return Tuple!(int, "index",int, "next")(index,next);
which could further be simplified using a helper function as its a common
idiom, into:
return TupleNamed!("index","next")(index,next);


*Limitations*:
One typical case where this isn't possible is when a template defines an
type alias, eg:
----
template Iota(int stop) {
    static if (stop <= 0)
        alias Iota=TypeTuple!();
    else
        alias Iota=TypeTuple!(Iota!(stop-1), stop-1);
}
----
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.puremagic.com/pipermail/digitalmars-d/attachments/20130513/9a1fcb80/attachment.html>


More information about the Digitalmars-d mailing list