www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - static if check for array and AA

reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
I'm fairly certain I've seen this come up before, but I can't seem to 
rediscover the thread.

I'm trying to write a static if condition to check whether a certain 
type is an array, and another check for an associative array. The former 
proved to be moderately simple:

bool isArray(T) () {
     static if (is(typeof(T.init[0])[] == T)) {
         return true;
     } else {
         return false;
     }
}

Note that this only checks for dynamic arrays.

Associative arrays are harder. The above trick can't quite work as the 
type of the key can't be relied upon. It's easy enough to do the check 
using RTTI:

bool isAA(T) () {
     TypeInfo t = typeid(T);
     if (cast(TypeInfo_AssociativeArray) t) {
         return true;
     } else {
         return false;
     }
}

But of course that is a runtime check, and this should really be a 
compile-time check. (Also, the various subclasses of TypeInfo are 
undocumented, so I'm not sure if it's a good idea to rely on them.)

Anyone else have some template judo to share?

-Kirk McDonald
Jun 21 2006
parent reply BCS <BCS pathlink.com> writes:
Kirk McDonald wrote:
 I'm fairly certain I've seen this come up before, but I can't seem to 
 rediscover the thread.
 
 I'm trying to write a static if condition to check whether a certain 
 type is an array, and another check for an associative array. The former 
 proved to be moderately simple:
 
 bool isArray(T) () {
     static if (is(typeof(T.init[0])[] == T)) {
         return true;
     } else {
         return false;
     }
 }
 
 Note that this only checks for dynamic arrays.
 
 Associative arrays are harder. The above trick can't quite work as the 
 type of the key can't be relied upon. It's easy enough to do the check 
 using RTTI:
 
 bool isAA(T) () {
     TypeInfo t = typeid(T);
     if (cast(TypeInfo_AssociativeArray) t) {
         return true;
     } else {
         return false;
     }
 }
 
 But of course that is a runtime check, and this should really be a 
 compile-time check. (Also, the various subclasses of TypeInfo are 
 undocumented, so I'm not sure if it's a good idea to rely on them.)
 
 Anyone else have some template judo to share?
 
 -Kirk McDonald

template isAA(T) { const isAA = is((cast(T)null).keys); } ??? or something like that
Jun 21 2006
next sibling parent reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
BCS wrote:
 Kirk McDonald wrote:
 
 I'm fairly certain I've seen this come up before, but I can't seem to 
 rediscover the thread.

 I'm trying to write a static if condition to check whether a certain 
 type is an array, and another check for an associative array. The 
 former proved to be moderately simple:

 bool isArray(T) () {
     static if (is(typeof(T.init[0])[] == T)) {
         return true;
     } else {
         return false;
     }
 }

 Note that this only checks for dynamic arrays.

 Associative arrays are harder. The above trick can't quite work as the 
 type of the key can't be relied upon. It's easy enough to do the check 
 using RTTI:

 bool isAA(T) () {
     TypeInfo t = typeid(T);
     if (cast(TypeInfo_AssociativeArray) t) {
         return true;
     } else {
         return false;
     }
 }

 But of course that is a runtime check, and this should really be a 
 compile-time check. (Also, the various subclasses of TypeInfo are 
 undocumented, so I'm not sure if it's a good idea to rely on them.)

 Anyone else have some template judo to share?

 -Kirk McDonald

template isAA(T) { const isAA = is((cast(T)null).keys); } ??? or something like that

Now there's an idea... The templates are now: template isArray(T) { const bool isArray = is(typeof(T.init[0])[] == T); } template isAA(T) { const bool isAA = is(typeof(T.init.values[0])[typeof(T.init.keys[0])] == T); } I'd forgotten about those .keys and .values properties. :-) -Kirk McDonald
Jun 21 2006
next sibling parent Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
How bout this ?



// ----
import std.stdio;


void main() {
	alias char[][int[]] Foo;

	static if (is(typeof(Foo.keys))) {
		static if (is(typeof(Foo.values))) {
			static if (is(Foo : typeof(Foo.values[0])[typeof(Foo.keys[0])])) {
				writefln("AA !");
			} else writefln("!AA3");
		} else writefln("!AA2");
	} else writefln("!AA1");
}
// ----


-- 
Tomasz Stachowiak  /+ a.k.a. h3r3tic +/
Jun 21 2006
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
Kirk McDonald wrote:
 
 I'd forgotten about those .keys and .values properties. :-)

For extra credit, come up with a simple way to detect static arrays that doesn't rely on implementation-defined behavior. :-) Sean
Jun 21 2006
next sibling parent reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Sean Kelly wrote:
 Kirk McDonald wrote:
 
 I'd forgotten about those .keys and .values properties. :-)

For extra credit, come up with a simple way to detect static arrays that doesn't rely on implementation-defined behavior. :-) Sean

My first stab at this is this non-working template: template isStaticArray(T) { const bool isStaticArray = is(typeof(T.init[0])[T.length] == T); } My reasoning being a bit of the spec that claims "length" is a compile-time constant for static arrays. Makes sense, might even be true... but it turns out something else entirely is wrong: [inittest.d] import std.stdio; void main() { int[] a; int[10] b; writefln("%s\n%s", typeof(a).init, typeof(b).init ); static if(is(typeof(typeof(b).init) == int)) { writefln("(int[10]).init is an int!"); } } $ dmd inittest $ ./inittest [] 0 (int[10]).init is an int! Wait, what? It turns out the above template fails because "is(typeof(T.init[0]))" fails, as it doesn't describe a valid type. (You can't subscript an int!) This oddity actually makes our job easier, as we see below. It also seems that T.length isn't a compile time constant after all (or at least the template doesn't work if I use it), but there's an easy workaround: Divide T's sizeof by T.init's sizeof. The working template, then, is: template isStaticArray(T) { const bool isStaticArray = is(typeof(T.init)[(T).sizeof / typeof(T.init).sizeof] == T); } Ugly! But it works. The complete test suite I have is: [arraytest.d] import std.stdio; template isArray(T) { const bool isArray = is(typeof(T.init[0])[] == T); } template isStaticArray(T) { const bool isStaticArray = is(typeof(T.init)[(T).sizeof / typeof(T.init).sizeof] == T); } template isAA(T) { const bool isAA = is(typeof(T.init.values[0])[typeof(T.init.keys[0])] == T); } void main() { int i; int[] j; int[10] l; int[int] k; Object o = new Object; writefln("%s", isArray!(typeof(i))); writefln("%s", isArray!(typeof(j))); writefln("%s", isArray!(typeof(l))); writefln("%s", isArray!(typeof(k))); writefln("%s", isArray!(typeof(o))); writefln(""); writefln("%s", isStaticArray!(typeof(i))); writefln("%s", isStaticArray!(typeof(j))); writefln("%s", isStaticArray!(typeof(l))); writefln("%s", isStaticArray!(typeof(k))); writefln("%s", isStaticArray!(typeof(o))); writefln(""); writefln("%s", isAA!(typeof(i))); writefln("%s", isAA!(typeof(j))); writefln("%s", isAA!(typeof(l))); writefln("%s", isAA!(typeof(k))); writefln("%s", isAA!(typeof(o))); } -Kirk McDonald
Jun 22 2006
next sibling parent Sean Kelly <sean f4.ca> writes:
Kirk McDonald wrote:
 Sean Kelly wrote:
 Kirk McDonald wrote:

 I'd forgotten about those .keys and .values properties. :-)

For extra credit, come up with a simple way to detect static arrays that doesn't rely on implementation-defined behavior. :-) Sean

My first stab at this is this non-working template: template isStaticArray(T) { const bool isStaticArray = is(typeof(T.init[0])[T.length] == T); } My reasoning being a bit of the spec that claims "length" is a compile-time constant for static arrays. Makes sense, might even be true... but it turns out something else entirely is wrong: [inittest.d] import std.stdio; void main() { int[] a; int[10] b; writefln("%s\n%s", typeof(a).init, typeof(b).init ); static if(is(typeof(typeof(b).init) == int)) { writefln("(int[10]).init is an int!"); } } $ dmd inittest $ ./inittest [] 0 (int[10]).init is an int! Wait, what? It turns out the above template fails because "is(typeof(T.init[0]))" fails, as it doesn't describe a valid type. (You can't subscript an int!) This oddity actually makes our job easier, as we see below. It also seems that T.length isn't a compile time constant after all (or at least the template doesn't work if I use it), but there's an easy workaround: Divide T's sizeof by T.init's sizeof. The working template, then, is: template isStaticArray(T) { const bool isStaticArray = is(typeof(T.init)[(T).sizeof / typeof(T.init).sizeof] == T); } Ugly! But it works.

Thanks :-) I was trying to sort something like this out the other day and couldn't come up with a working syntax... but then I'd completely forgotten about tricks with .init. At the time, I was thinking it might be nice if this detected static arrays: template isStaticArray( T : T[int] ) { const bool isStaticArray = true; } template isStaticArray( T ) { const bool isStaticArray = false; } But since the type identifier would still be different from dynamic arrays, specializing templates for all arrays (dynamic and static) is still annoying: template fn( T, bool isArray : true = isStaticOrDynamicArray!(T) ) { void fn( T ar ) {} } Then more mess to determine the element type of the array, etc. I'm starting to wonder if C++ style template specialization is even the correct way to go about things in D, as it seems far more flexible to use a single generic wrapper containing a big static if block. This seems to be the rough equivalent to concepts in D, with the drawback that at that point the template will either be instantiated or it won't and compilation will halt. Sean
Jun 22 2006
prev sibling parent reply Oskar Linde <Oskar_member pathlink.com> writes:
In article <e7djp7$42g$1 digitaldaemon.com>, Kirk McDonald says...

It turns out the above template fails because "is(typeof(T.init[0]))" 
fails, as it doesn't describe a valid type. (You can't subscript an int!)

Yes. I've been reporting this a couple of time, but never gotten any (official) response on whether this is the intended behavior. Static arrays are the only type for which typeof(T.init) != T. So the simplest test for a static array is: template isStaticArray(T) { const bool isStaticArray = !is(typeof(T.init) == T); } But having this special case for static arrays complicates template code in many cases. /Oskar
Jun 22 2006
parent reply Sean Kelly <sean f4.ca> writes:
Oskar Linde wrote:
 In article <e7djp7$42g$1 digitaldaemon.com>, Kirk McDonald says...
 
 It turns out the above template fails because "is(typeof(T.init[0]))" 
 fails, as it doesn't describe a valid type. (You can't subscript an int!)

Yes. I've been reporting this a couple of time, but never gotten any (official) response on whether this is the intended behavior. Static arrays are the only type for which typeof(T.init) != T.

This is also somewhat close to implementation-specific behavior. I'd prefer to have a more formal means of detecting static arrays. Sean
Jun 22 2006
parent Bruno Medeiros <brunodomedeirosATgmail SPAM.com> writes:
Sean Kelly wrote:
 Oskar Linde wrote:
 In article <e7djp7$42g$1 digitaldaemon.com>, Kirk McDonald says...

 It turns out the above template fails because "is(typeof(T.init[0]))" 
 fails, as it doesn't describe a valid type. (You can't subscript an 
 int!)

Yes. I've been reporting this a couple of time, but never gotten any (official) response on whether this is the intended behavior. Static arrays are the only type for which typeof(T.init) != T.

This is also somewhat close to implementation-specific behavior. I'd prefer to have a more formal means of detecting static arrays. Sean

I'm not sure that is implementation-specific behavior. I suspect that that is related to the fact that static arrays are the only type that cannot be assigned to a value of the same type (sar1 = sar2). So in order for something like this: T sar = T.init ; to work for all types, then T.init had to an element of the static array. I think that all of this (arrays not being proper value types) is pretty inconsistent, although most of us are so used to C and C++ that we don't even notice or are bothered by it -- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jun 23 2006
prev sibling parent David Medlock <noone nowhere.com> writes:
Sean Kelly wrote:
 Kirk McDonald wrote:
 
 I'd forgotten about those .keys and .values properties. :-)

For extra credit, come up with a simple way to detect static arrays that doesn't rely on implementation-defined behavior. :-) Sean

Jun 22 2006
prev sibling parent reply Don Clugston <dac nospam.com.au> writes:
BCS wrote:
 Kirk McDonald wrote:
 I'm fairly certain I've seen this come up before, but I can't seem to 
 rediscover the thread.

 I'm trying to write a static if condition to check whether a certain 
 type is an array, and another check for an associative array. The 
 former proved to be moderately simple:

 bool isArray(T) () {
     static if (is(typeof(T.init[0])[] == T)) {
         return true;
     } else {
         return false;
     }
 }

 Note that this only checks for dynamic arrays.

 Associative arrays are harder. The above trick can't quite work as the 
 type of the key can't be relied upon. It's easy enough to do the check 
 using RTTI:

 bool isAA(T) () {
     TypeInfo t = typeid(T);
     if (cast(TypeInfo_AssociativeArray) t) {
         return true;
     } else {
         return false;
     }
 }

 But of course that is a runtime check, and this should really be a 
 compile-time check. (Also, the various subclasses of TypeInfo are 
 undocumented, so I'm not sure if it's a good idea to rely on them.)

 Anyone else have some template judo to share?

 -Kirk McDonald

template isAA(T) { const isAA = is((cast(T)null).keys); } ??? or something like that

template isAA(T) { const bool isAA = T.mangleof[0]=='H'; } template isStaticArray(T) { const bool isStaticArray = T.mangleof[0]=='G'; } template isDynamicArray(T) { const bool isDynamicArray = T.mangleof[0]=='A'; } You can check for ANYTHING in this way. Of course this relies on the name mangling algorithm which is not yet stabilised and documented -- but which should be, by DMD 1.0.
Jun 23 2006
parent reply BCS <BCS pathlink.com> writes:
Don Clugston wrote:
 Of course this relies on the name mangling algorithm which is not yet 
 stabilised and documented -- but which should be, by DMD 1.0.

Maybe, but it should it be standardized as part of D? (I hesitate to say yes.)
Jun 23 2006
parent Don Clugston <dac nospam.com.au> writes:
BCS wrote:
 Don Clugston wrote:
 Of course this relies on the name mangling algorithm which is not yet 
 stabilised and documented -- but which should be, by DMD 1.0.

Maybe, but it should it be standardized as part of D? (I hesitate to say yes.)

It should definitely be standardised. Failure to standardise the name mangling and the ABI was one of the major mistakes of C++. It makes linking between different vendors virtually impossible (and it's why so many more libraries use C linking than C++).
Jun 25 2006