www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Bitfield-style enum to strings?

reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
Assuming a plain old bitfield-style enum like:

enum Foo {
     optionA = 1<<0;
     optionB = 1<<1;
     optionC = 1<<2;
     optionD = 1<<3;
     optionE = 1<<4;
}

Does a function already exist somewhere to take an instance of Foo and 
get a list of the switch names as strings?

Something kinda like:

Foo fooVar = Foo.optionB | Foo.optionD;
assert(
     DOES_THIS_FUNC_EXIST(fooVar)
         .equals(["optionB", "optionD"])
);

Seems entirely feasible, although my traits-fu is a bit rusty.
May 07 2015
next sibling parent "Baz" <bb.temp gmx.com> writes:
On Thursday, 7 May 2015 at 17:41:10 UTC, Nick Sabalausky wrote:
 Assuming a plain old bitfield-style enum like:

 enum Foo {
     optionA = 1<<0;
     optionB = 1<<1;
     optionC = 1<<2;
     optionD = 1<<3;
     optionE = 1<<4;
 }

 Does a function already exist somewhere to take an instance of 
 Foo and get a list of the switch names as strings?

 Something kinda like:

 Foo fooVar = Foo.optionB | Foo.optionD;
 assert(
     DOES_THIS_FUNC_EXIST(fooVar)
         .equals(["optionB", "optionD"])
 );

 Seems entirely feasible, although my traits-fu is a bit rusty.
Hi, i have a specialized struct for "bit sets" that handles the string representation: https://github.com/BBasile/enumset/blob/master/import/enumset.d#L242 however it's not std. Building the string is easy (cf toString()) but if it can help...
May 07 2015
prev sibling parent reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On 05/07/2015 01:41 PM, Nick Sabalausky wrote:
 Assuming a plain old bitfield-style enum like:

 enum Foo {
      optionA = 1<<0;
      optionB = 1<<1;
      optionC = 1<<2;
      optionD = 1<<3;
      optionE = 1<<4;
 }

 Does a function already exist somewhere to take an instance of Foo and
 get a list of the switch names as strings?

 Something kinda like:

 Foo fooVar = Foo.optionB | Foo.optionD;
 assert(
      DOES_THIS_FUNC_EXIST(fooVar)
          .equals(["optionB", "optionD"])
 );

 Seems entirely feasible, although my traits-fu is a bit rusty.
Wow, D's seriously awesome, that was actually way easier than I expected: ------------------------------------ import std.traits : isIntegral; auto bitFieldValues(T)(T value) if(is(T==enum) && isIntegral!T) { import std.algorithm : filter, map; import std.conv : to; import std.traits : EnumMembers; // There's gotta be a better way to convert EnumMembers!T // to a range, right? But std.range.only() didn't work, // due to a template instantiation error. T[] members; foreach(m; EnumMembers!(T)) members ~= m; return members .filter!(member => (value & member) == member) .map!(member => to!string(member)); } ------------------------------------ Sample code to use it: ------------------------------------ enum Foo { optionA = 1<<0, optionB = 1<<1, optionC = 1<<2, optionD = 1<<3, optionE = 1<<4, } void main() { import std.range : join; import std.stdio : writeln; Foo fooVar = Foo.optionB | Foo.optionD; // Output: optionB | optionD writeln(bitFieldValues(fooVar).join(" | ")); } ------------------------------------
May 07 2015
next sibling parent reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
Minor fix to work right for "none" fields. Already worked fine on 
combination fields liek "all".

-------------------------------------
enum Foo
{
     none = 0,
     optionA = 1<<0,
     optionB = 1<<1,
     optionC = 1<<2,
     optionD = 1<<3,
     all = optionA | optionB | optionC | optionD,
}

import std.traits : isIntegral;
auto bitFieldValues(T)(T value) if(is(T==enum) && isIntegral!T)
{
     // There's gotta be a better way to convert EnumMembers!T
     // to a range, right? But std.range.only() didn't work,
     // due to a template instantiation error.
     T[] members;
     foreach(m; EnumMembers!(T))
         members ~= m;

     return members
         .filter!(member => member==0? value==0 : (value&member)==member)
         .map!(member => to!string(member));
}

void main()
{
     import std.range : join;
     import std.stdio : writeln;

     Foo fooVar = Foo.optionB | Foo.optionD;

     // Output:
     // optionB | optionD
     // none
     // optionA | optionB | optionC | optionD | all

     writeln(bitFieldValues(Foo.optionB | Foo.optionD).join(" | "));
     writeln(bitFieldValues(Foo.none).join(" | "));
     writeln(bitFieldValues(Foo.all).join(" | "));
}
-------------------------------------
May 07 2015
parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
Gah, missed some imports that time:

On 05/07/2015 05:04 PM, Nick Sabalausky wrote:
 Minor fix to work right for "none" fields. Already worked fine on
 combination fields liek "all".

 -------------------------------------
 enum Foo
 {
      none = 0,
      optionA = 1<<0,
      optionB = 1<<1,
      optionC = 1<<2,
      optionD = 1<<3,
      all = optionA | optionB | optionC | optionD,
 }

 import std.traits : isIntegral;
 auto bitFieldValues(T)(T value) if(is(T==enum) && isIntegral!T)
 {
import std.algorithm : filter, map; import std.conv : to; import std.traits : EnumMembers;
      // There's gotta be a better way to convert EnumMembers!T
      // to a range, right? But std.range.only() didn't work,
      // due to a template instantiation error.
      T[] members;
      foreach(m; EnumMembers!(T))
          members ~= m;

      return members
          .filter!(member => member==0? value==0 : (value&member)==member)
          .map!(member => to!string(member));
 }

 void main()
 {
      import std.range : join;
      import std.stdio : writeln;

      Foo fooVar = Foo.optionB | Foo.optionD;

      // Output:
      // optionB | optionD
      // none
      // optionA | optionB | optionC | optionD | all

      writeln(bitFieldValues(Foo.optionB | Foo.optionD).join(" | "));
      writeln(bitFieldValues(Foo.none).join(" | "));
      writeln(bitFieldValues(Foo.all).join(" | "));
 }
 -------------------------------------
May 07 2015
prev sibling next sibling parent reply Justin Whear <justin economicmodeling.com> writes:
On Thu, 07 May 2015 16:55:42 -0400, Nick Sabalausky wrote:

      // There's gotta be a better way to convert EnumMembers!T // to a
      range, right? But std.range.only() didn't work, // due to a
      template instantiation error.
      T[] members;
      foreach(m; EnumMembers!(T))
          members ~= m;
T[] members = [ EnumMembers!T ];
May 07 2015
parent reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On 05/07/2015 05:19 PM, Justin Whear wrote:
 On Thu, 07 May 2015 16:55:42 -0400, Nick Sabalausky wrote:

       // There's gotta be a better way to convert EnumMembers!T // to a
       range, right? But std.range.only() didn't work, // due to a
       template instantiation error.
       T[] members;
       foreach(m; EnumMembers!(T))
           members ~= m;
T[] members = [ EnumMembers!T ];
Doh! Yup, that works. Still, I would think there should be a way to do it without allocating an array. But it's not a huge deal right now though.
May 07 2015
parent reply "Meta" <jared771 gmail.com> writes:
On Thursday, 7 May 2015 at 21:41:06 UTC, Nick Sabalausky wrote:
 On 05/07/2015 05:19 PM, Justin Whear wrote:
 On Thu, 07 May 2015 16:55:42 -0400, Nick Sabalausky wrote:

      // There's gotta be a better way to convert 
 EnumMembers!T // to a
      range, right? But std.range.only() didn't work, // due 
 to a
      template instantiation error.
      T[] members;
      foreach(m; EnumMembers!(T))
          members ~= m;
T[] members = [ EnumMembers!T ];
Doh! Yup, that works. Still, I would think there should be a way to do it without allocating an array. But it's not a huge deal right now though.
You could also do `TypeTuple!(EnumMembers!T))` I believe.
May 07 2015
parent reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On 05/07/2015 09:17 PM, Meta wrote:
 On Thursday, 7 May 2015 at 21:41:06 UTC, Nick Sabalausky wrote:
 On 05/07/2015 05:19 PM, Justin Whear wrote:
 T[] members = [ EnumMembers!T ];
Doh! Yup, that works. Still, I would think there should be a way to do it without allocating an array. But it's not a huge deal right now though.
You could also do `TypeTuple!(EnumMembers!T))` I believe.
filter doesn't seem to like that.
May 07 2015
parent "Meta" <jared771 gmail.com> writes:
On Friday, 8 May 2015 at 04:11:36 UTC, Nick Sabalausky wrote:
 On 05/07/2015 09:17 PM, Meta wrote:
 On Thursday, 7 May 2015 at 21:41:06 UTC, Nick Sabalausky wrote:
 On 05/07/2015 05:19 PM, Justin Whear wrote:
 T[] members = [ EnumMembers!T ];
Doh! Yup, that works. Still, I would think there should be a way to do it without allocating an array. But it's not a huge deal right now though.
You could also do `TypeTuple!(EnumMembers!T))` I believe.
filter doesn't seem to like that.
Yeah, sorry, forgot it was supposed to go through a range as well.
May 08 2015
prev sibling parent "anonymous" <anonymous example.com> writes:
On Thursday, 7 May 2015 at 20:55:42 UTC, Nick Sabalausky wrote:
     // There's gotta be a better way to convert EnumMembers!T
     // to a range, right? But std.range.only() didn't work,
     // due to a template instantiation error.
     T[] members;
     foreach(m; EnumMembers!(T))
         members ~= m;
only(EnumMembers!T) should work and did work before 2.067. I filed a regression: https://issues.dlang.org/show_bug.cgi?id=14556
May 07 2015