digitalmars.D.learn - Introduction to traits (and __traits)
- Joseph Rushton Wakeling (22/22) Aug 30 2013 Hello all,
- Benjamin Thaut (10/34) Aug 30 2013 You need to put it into a static if, otherwise the compiler will
- Joseph Rushton Wakeling (16/25) Aug 30 2013 Ahh, right, thanks. :-)
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (4/30) Aug 30 2013 How about allSatisfy:
- Joseph Rushton Wakeling (46/48) Aug 30 2013 I'll have a look at that, thanks :-)
- bearophile (9/22) Aug 30 2013 Perhaps can shorten that code writing a hasMembers helper (and I
- H. S. Teoh (27/45) Aug 30 2013 [...]
- Joseph Rushton Wakeling (2/4) Aug 30 2013 Nice thought, I might look into that at some point. :-)
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
Aug 30 2013
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, -- JoeYou 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; }
Aug 30 2013
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 ... }
Aug 30 2013
On 08/30/2013 01:57 PM, Joseph Rushton Wakeling wrote:On 30/08/13 21:40, Benjamin Thaut wrote:How about allSatisfy: AliYou 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 ... }
Aug 30 2013
On 30/08/13 23:06, Ali Çehreli wrote:How about 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; } } ////////////////////////////////////////////////////////
Aug 30 2013
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
Aug 30 2013
On Fri, Aug 30, 2013 at 11:51:37PM +0200, bearophile wrote:Joseph Rushton Wakeling:[...] 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.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):
Aug 30 2013
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. :-)
Aug 30 2013