| Thread overview | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
August 30, 2013 Introduction to traits (and __traits) | ||||
|---|---|---|---|---|
| ||||
Hello all,
I find myself wanting to write for the first time one of these isSomething(T) or hasSomething(T) templates that perform compile-time checks, so I was hoping people could give me some good general advice on traits.
The goal here is to be able to confirm (i) type T has certain members and (ii) they have certain properties. (This is for my graph library, Dgraph.)
So, to start off with, I thought I'd try starting with,
template isGraph(G)
{
isGraph = __traits(hasMember, G, "directed") && isBoolean!(typeof(G.directed));
}
... which I'd assumed would cover the case where G does not have a member "directed". But in fact if I pass it a struct that does not have the entity .directed defined therein, it will return an error: "no property 'directed' for type ..."
So, can someone give me a good idea of how to go about writing such a compile-time template that checks (i) for the existence of certain methods/functions/members and (ii) confirms their characteristics, such as their return values or arguments?
Thanks & best wishes,
-- Joe
| ||||
August 30, 2013 Re: Introduction to traits (and __traits) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Joseph Rushton Wakeling | Am 30.08.2013 21:36, schrieb Joseph Rushton Wakeling: > Hello all, > > I find myself wanting to write for the first time one of these > isSomething(T) or hasSomething(T) templates that perform compile-time > checks, so I was hoping people could give me some good general advice on > traits. > > The goal here is to be able to confirm (i) type T has certain members > and (ii) they have certain properties. (This is for my graph library, > Dgraph.) > > So, to start off with, I thought I'd try starting with, > > template isGraph(G) > { > isGraph = __traits(hasMember, G, "directed") && > isBoolean!(typeof(G.directed)); > } > > ... which I'd assumed would cover the case where G does not have a > member "directed". But in fact if I pass it a struct that does not have > the entity .directed defined therein, it will return an error: "no > property 'directed' for type ..." > > So, can someone give me a good idea of how to go about writing such a > compile-time template that checks (i) for the existence of certain > methods/functions/members and (ii) confirms their characteristics, such > as their return values or arguments? > > Thanks & best wishes, > > -- Joe You need to put it into a static if, otherwise the compiler will continue semantic checks on the second part of the expression. E.g. template isGraph(G) { static if(__traits(hasMember, G, "directed")) enum bool isGraph = isBoolean!(typeof(G.directed)); else enum bool isGraph = false; } | |||
August 30, 2013 Re: Introduction to traits (and __traits) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Benjamin Thaut | On 30/08/13 21:40, Benjamin Thaut wrote:
> You need to put it into a static if, otherwise the compiler will continue
> semantic checks on the second part of the expression. E.g.
>
> template isGraph(G)
> {
> static if(__traits(hasMember, G, "directed"))
> enum bool isGraph = isBoolean!(typeof(G.directed));
> else
> enum bool isGraph = false;
> }
Ahh, right, thanks. :-)
Is there a recommended way for handling the case where there are many such members -- say about 10 or more? The static if's could become very highly nested with this approach. I suppose I could go through like this:
static if(!__traits(hasMember, G, "one"))
enum bool isGraph = false;
else static if(!__traits(hasMember, G, "two"))
enum bool isGraph = false;
else static if ...
...
else
{
// Now I know all the members exist and I can
// start checking out their properties ...
}
| |||
August 30, 2013 Re: Introduction to traits (and __traits) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Joseph Rushton Wakeling | On 08/30/2013 01:57 PM, Joseph Rushton Wakeling wrote: > On 30/08/13 21:40, Benjamin Thaut wrote: >> You need to put it into a static if, otherwise the compiler will continue >> semantic checks on the second part of the expression. E.g. >> >> template isGraph(G) >> { >> static if(__traits(hasMember, G, "directed")) >> enum bool isGraph = isBoolean!(typeof(G.directed)); >> else >> enum bool isGraph = false; >> } > > Ahh, right, thanks. :-) > > Is there a recommended way for handling the case where there are many > such members -- say about 10 or more? The static if's could become very > highly nested with this approach. I suppose I could go through like this: > > static if(!__traits(hasMember, G, "one")) > enum bool isGraph = false; > else static if(!__traits(hasMember, G, "two")) > enum bool isGraph = false; > else static if ... > ... > else > { > // Now I know all the members exist and I can > // start checking out their properties ... > } > How about allSatisfy: http://dlang.org/phobos/std_typetuple.html#.allSatisfy Ali | |||
August 30, 2013 Re: Introduction to traits (and __traits) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Ali Çehreli | On 30/08/13 23:06, Ali Çehreli wrote: > How about allSatisfy: > > http://dlang.org/phobos/std_typetuple.html#.allSatisfy I'll have a look at that, thanks :-) Here's what I came up with, for now. It should probably be extended with further tests on the characteristics of the various member functions but it seems to be sufficient for now: //////////////////////////////////////////////////////// template isGraph(G) { static if (!__traits(hasMember, G, "directed") || !__traits(hasMember, G, "edge") || !__traits(hasMember, G, "edgeCount") || !__traits(hasMember, G, "vertexCount") || !__traits(hasMember, G, "isEdge") || !__traits(hasMember, G, "edgeID") || !__traits(hasMember, G, "addEdge") || !__traits(hasMember, G, "degreeIn") || !__traits(hasMember, G, "degreeOut") || !__traits(hasMember, G, "incidentEdgesIn") || !__traits(hasMember, G, "incidentEdgesOut") || !__traits(hasMember, G, "neighboursIn") || !__traits(hasMember, G, "neighboursOut")) { enum bool isGraph = false; } else static if (!isBoolean!(typeof(G.directed))) { enum bool isGraph = false; } else static if (G.directed && (__traits(hasMember, G, "degree") || __traits(hasMember, G, "incidentEdges") || __traits(hasMember, G, "neighbours"))) { enum bool isGraph = false; } else static if (!G.directed && (!__traits(hasMember, G, "degree") || !__traits(hasMember, G, "incidentEdges") || !__traits(hasMember, G, "neighbours"))) { enum bool isGraph = false; } else { enum bool isGraph = true; } } //////////////////////////////////////////////////////// | |||
August 30, 2013 Re: Introduction to traits (and __traits) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Joseph Rushton Wakeling | Joseph Rushton Wakeling:
> static if (!__traits(hasMember, G, "directed") ||
> !__traits(hasMember, G, "edge") ||
> !__traits(hasMember, G, "edgeCount") ||
> !__traits(hasMember, G, "vertexCount") ||
> !__traits(hasMember, G, "isEdge") ||
> !__traits(hasMember, G, "edgeID") ||
> !__traits(hasMember, G, "addEdge") ||
> !__traits(hasMember, G, "degreeIn") ||
> !__traits(hasMember, G, "degreeOut") ||
> !__traits(hasMember, G, "incidentEdgesIn") ||
> !__traits(hasMember, G, "incidentEdgesOut") ||
> !__traits(hasMember, G, "neighboursIn") ||
> !__traits(hasMember, G, "neighboursOut"))
Perhaps can shorten that code writing a hasMembers helper (and I suggest to keep those names sorted):
static if (hasMembers!(G, "addEdge
degreeIn
...
vertexCount".split) {
Bye,
bearophile
| |||
August 30, 2013 Re: Introduction to traits (and __traits) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to bearophile | On Fri, Aug 30, 2013 at 11:51:37PM +0200, bearophile wrote: > Joseph Rushton Wakeling: > > > static if (!__traits(hasMember, G, "directed") || > > !__traits(hasMember, G, "edge") || > > !__traits(hasMember, G, "edgeCount") || > > !__traits(hasMember, G, "vertexCount") || > > !__traits(hasMember, G, "isEdge") || > > !__traits(hasMember, G, "edgeID") || > > !__traits(hasMember, G, "addEdge") || > > !__traits(hasMember, G, "degreeIn") || > > !__traits(hasMember, G, "degreeOut") || > > !__traits(hasMember, G, "incidentEdgesIn") || > > !__traits(hasMember, G, "incidentEdgesOut") || > > !__traits(hasMember, G, "neighboursIn") || > > !__traits(hasMember, G, "neighboursOut")) > > Perhaps can shorten that code writing a hasMembers helper (and I > suggest to keep those names sorted): [...] Here's a first stab at a possible implementation: /* Warning: untested code */ import std.typetuple : allSatisfy; template isString(T) { // There may already be something in Phobos that does // this, but I'm too lazy to look. enum isString = is(T == string); } template hasMembers(alias T, Members...) if (allSatisfy!isString(Members)) { // Template recursion + exprTuple slicing FTW :) enum hasMembers = __traits(hasMember, T, Members[0]) && hasMembers!(T, Members[1..$]); } void myFunc(G)(G graph) { static if (hasMembers!(G, "directed", "edge", /* ... */)) { ... } } T -- Computers shouldn't beep through the keyhole. | |||
August 30, 2013 Re: Introduction to traits (and __traits) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to bearophile | On 30/08/13 23:51, bearophile wrote:
> Perhaps can shorten that code writing a hasMembers helper (and I suggest to keep
> those names sorted)
Nice thought, I might look into that at some point. :-)
| |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply