www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Introduction to traits (and __traits)

reply Joseph Rushton Wakeling <joseph.wakeling webdrake.net> writes:
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
parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
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; }
Aug 30 2013
parent reply Joseph Rushton Wakeling <joseph.wakeling webdrake.net> writes:
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
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
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
Aug 30 2013
parent reply Joseph Rushton Wakeling <joseph.wakeling webdrake.net> writes:
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; } } ////////////////////////////////////////////////////////
Aug 30 2013
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
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
next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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.
Aug 30 2013
prev sibling parent Joseph Rushton Wakeling <joseph.wakeling webdrake.net> writes:
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