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