www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How do I iterate over enum members at runtime?

reply Andrej Mitrovic <none none.none> writes:
E.g.:

enum Metrics : int 
{
	SM_CXSCREEN = 0,
	SM_CYSCREEN,
	SM_CXVSCROLL,
	SM_CYHSCROLL,
	SM_CYCAPTION,
	SM_CXBORDER,
}

void foo(int m)
{
}

void main()
{
    foreach (m; Metrics)
    {
        foo(m);
    }
}

This won't work.

I know there's traits to get strings at compile time, e.g.:
auto b = [ __traits(allMembers, Metrics) ];

but this doesn't help me try out those enum values at runtime. It could almost
work if I could use a mixin() in a foreach loop, but that's lucid dreaming.

Another alternative might be to create an array out of the enum, but I don't
know of any way of doing this.
Apr 09 2011
parent reply Jesse Phillips <jessekphillips+d gmail.com> writes:
On Sat, 09 Apr 2011 16:20:06 -0400, Andrej Mitrovic wrote:

 I know there's traits to get strings at compile time, e.g.: auto b = [
 __traits(allMembers, Metrics) ];
 
 but this doesn't help me try out those enum values at runtime. It could
 almost work if I could use a mixin() in a foreach loop, but that's lucid
 dreaming.
 
 Another alternative might be to create an array out of the enum, but I
 don't know of any way of doing this.
You have everything you need, just put them all together: enum Metrics : int { SM_CXSCREEN = 0, SM_CYSCREEN, SM_CXVSCROLL, SM_CYHSCROLL, SM_CYCAPTION, SM_CXBORDER, } void foo(int m) { } void main() { foreach (m; __traits(allMembers, Metrics)) { foo(mixin("Metrics." ~ m)); } }
Apr 09 2011
next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
What the.. I was sure mixin wouldn't work in a foreach loop.

Thanks, Jesse!
Apr 09 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
 What the.. I was sure mixin wouldn't work in a foreach loop.
Whyever not? mixins work in most places. I believe that the problem is that they have to be a whole expression or statement rather than just a piece of one, so sometimes you can't use a mixin for something small and have to put more of the code in a mixin, but string mixins do work in most places. - Jonathan M Davis
Apr 09 2011
prev sibling next sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 4/10/11, Jonathan M Davis <jmdavisProg gmx.com> wrote:
 What the.. I was sure mixin wouldn't work in a foreach loop.
Whyever not? mixins work in most places. I believe that the problem is that they have to be a whole expression or statement rather than just a piece of one, so sometimes you can't use a mixin for something small and have to put more of the code in a mixin, but string mixins do work in most places. - Jonathan M Davis
Well in this case it works, but it's not always so easy. For example: import std.conv; enum Metrics : int { val0, val1, val2 } void foo(int m) { } void main() { foreach (index; 0..3) { foo(mixin("Metrics.val" ~ to!string(index))); } } So even though you might think "hey, it's obvious index in this case can never be anything other than 0, 1, or 2", you still won't be able to compile this. CTFE is a tricky thing.
Apr 09 2011
parent Jesse Phillips <jessekphillips+D gmail.com> writes:
You must iterate a compile time only construct.  So yes it would be nice to be
able to say: static foreach and have the compiler tell you, no I can't do that
(and force it if it can).

Andrej Mitrovic Wrote:

 Well in this case it works, but it's not always so easy. For example:
 
 import std.conv;
 enum Metrics : int
 {
     val0,
     val1,
     val2
 }
 
 void foo(int m)
 {
 }
 
 void main()
 {
    foreach (index; 0..3)
    {
        foo(mixin("Metrics.val" ~ to!string(index)));
    }
 }
 
 So even though you might think "hey, it's obvious index in this case
 can never be anything other than 0, 1, or 2", you still won't be able
 to compile this. CTFE is a tricky thing.
Apr 09 2011
prev sibling next sibling parent Cliff Hudson <cliff.s.hudson gmail.com> writes:
You could probably make a template out of the ugly __traits invocation as
well?

On Sat, Apr 9, 2011 at 3:37 PM, Andrej Mitrovic
<andrej.mitrovich gmail.com>wrote:

 On 4/10/11, Jonathan M Davis <jmdavisProg gmx.com> wrote:
 What the.. I was sure mixin wouldn't work in a foreach loop.
Whyever not? mixins work in most places. I believe that the problem is
that
 they have to be a whole expression or statement rather than just a piece
of
 one, so sometimes you can't use a mixin for something small and have to
put
 more of the code in a mixin, but string mixins do work in most places.

 - Jonathan M Davis
Well in this case it works, but it's not always so easy. For example: import std.conv; enum Metrics : int { val0, val1, val2 } void foo(int m) { } void main() { foreach (index; 0..3) { foo(mixin("Metrics.val" ~ to!string(index))); } } So even though you might think "hey, it's obvious index in this case can never be anything other than 0, 1, or 2", you still won't be able to compile this. CTFE is a tricky thing.
Apr 09 2011
prev sibling next sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Well, the C code I was translating had something like this:
struct SysMetrics
{
    int iIndex;
    char* szLabel;
    char* szDesc;
}

And then it used an array of these structures. So for all existing
enums that started with "SM_", those fields were populated with the
enum value and a name and description. A 200 line header file was
filled by hand. I really hope the author had some help from an editor
for that, lol!.

I've managed to do the same in about a dozen lines of code, although I
didn't fill the description field (I don't need it anyway):
http://codepad.org/EJEuc6qA

It's a shame that an enum with a tag doesn't have a .length property.
I've had to use __traits to build an array just to get the length.
Apr 09 2011
parent reply Jesse Phillips <jessekphillips+D gmail.com> writes:
Andrej Mitrovic Wrote:

 It's a shame that an enum with a tag doesn't have a .length property.
 I've had to use __traits to build an array just to get the length.
It has .max http://www.digitalmars.com/d/2.0/enum.html
Apr 09 2011
parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 4/10/11, Jesse Phillips <jessekphillips+D gmail.com> wrote:
 Andrej Mitrovic Wrote:

 It's a shame that an enum with a tag doesn't have a .length property.
 I've had to use __traits to build an array just to get the length.
It has .max http://www.digitalmars.com/d/2.0/enum.html
That doesn't show me the number of values an enum tag has, only its largest value. void main() { writeln(foo.max); // 101, not 4 } enum foo : int { a, b, c = 100, d }
Apr 09 2011
prev sibling parent reply Dan Olson <zans.is.for.cans yahoo.com> writes:
Jesse Phillips <jessekphillips+d gmail.com> writes:

 On Sat, 09 Apr 2011 16:20:06 -0400, Andrej Mitrovic wrote:

 I know there's traits to get strings at compile time, e.g.: auto b = [
 __traits(allMembers, Metrics) ];
 
 but this doesn't help me try out those enum values at runtime. It could
 almost work if I could use a mixin() in a foreach loop, but that's lucid
 dreaming.
 
 Another alternative might be to create an array out of the enum, but I
 don't know of any way of doing this.
You have everything you need, just put them all together: enum Metrics : int { SM_CXSCREEN = 0, SM_CYSCREEN, SM_CXVSCROLL, SM_CYHSCROLL, SM_CYCAPTION, SM_CXBORDER, } void foo(int m) { } void main() { foreach (m; __traits(allMembers, Metrics)) { foo(mixin("Metrics." ~ m)); } }
I'm exploring more and found there is also std.traits.EnumMembers. It's a little simpler: foreach (m; EnumMembers!Metrics)) { foo(m); } And for number of members in enum Metrics EnumMembers!Metrics.length -- Dan
Apr 10 2011
parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 4/11/11, Dan Olson <zans.is.for.cans yahoo.com> wrote:
 I'm exploring more and found there is also std.traits.EnumMembers.  It's
 a little simpler:

     foreach (m; EnumMembers!Metrics))
     {
         foo(m);
     }

 And for number of members in enum Metrics

     EnumMembers!Metrics.length
Nice find, thanks!
Apr 11 2011