Jump to page: 1 2
Thread overview
static if check for array and AA
Jun 21, 2006
Kirk McDonald
Jun 21, 2006
BCS
Jun 21, 2006
Kirk McDonald
Jun 21, 2006
Tom S
Jun 21, 2006
Sean Kelly
Jun 22, 2006
Kirk McDonald
Jun 22, 2006
Sean Kelly
Jun 22, 2006
Oskar Linde
Jun 22, 2006
Sean Kelly
Jun 24, 2006
Bruno Medeiros
Jun 22, 2006
David Medlock
Jun 23, 2006
Don Clugston
Jun 23, 2006
BCS
Jun 26, 2006
Don Clugston
June 21, 2006
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
June 21, 2006
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
June 21, 2006
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
June 21, 2006
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 +/
June 21, 2006
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
June 22, 2006
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
June 22, 2006
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
Just a WAG, but would policys (Alexandrescu) do this?
June 22, 2006
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
June 22, 2006
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


June 22, 2006
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
« First   ‹ Prev
1 2