www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Bitfields

reply nail <nail_member pathlink.com> writes:
What about integrated bitfields in D?
It would be nice if I write:

enum A {AA = 0x1, AB = 0x2, AC = 0x4}
enum B {BA = 0x8, BB = 0x4, BC = 0x0}
const uint CA = 0x80;

void foo(bitfield A a)
{
..
}

Then I can use combination of values in A as parameter and can't mix it with
values in B or constant C.

Is it necessary for D?
Aug 18 2004
next sibling parent reply Ilya Minkov <minkov cs.tum.edu> writes:
I think if we had a nice way to assemble bitarrays, it would do that.

nail schrieb:
 What about integrated bitfields in D?
 It would be nice if I write:
 
 enum A {AA = 0x1, AB = 0x2, AC = 0x4}
 enum B {BA = 0x8, BB = 0x4, BC = 0x0}
 const uint CA = 0x80;
 
 void foo(bitfield A a)
 {
 ..
 }
 
 Then I can use combination of values in A as parameter and can't mix it with
 values in B or constant C.
 
 Is it necessary for D?
 
 

Aug 19 2004
parent reply nail <nail_member pathlink.com> writes:
In article <cg1ums$1o4e$1 digitaldaemon.com>, Ilya Minkov says...
I think if we had a nice way to assemble bitarrays, it would do that.

nail schrieb:
 What about integrated bitfields in D?
 It would be nice if I write:
 
 enum A {AA = 0x1, AB = 0x2, AC = 0x4}
 enum B {BA = 0x8, BB = 0x4, BC = 0x0}
 const uint CA = 0x80;
 
 void foo(bitfield A a)
 {
 ..
 }
 
 Then I can use combination of values in A as parameter and can't mix it with
 values in B or constant C.
 
 Is it necessary for D?
 
 


No! In this way I can use CA as index, for example. Or if you mean associative array I can't use literals and construction becomes bulky. bit[A] param; param[AA] = true; param[AC] = true; foo(param); very long... Correct me if I understood smthing incorrectly.
Aug 19 2004
parent reply Regan Heath <regan netwin.co.nz> writes:
On Thu, 19 Aug 2004 18:03:10 +0000 (UTC), nail <nail_member pathlink.com> 
wrote:
 In article <cg1ums$1o4e$1 digitaldaemon.com>, Ilya Minkov says...
 I think if we had a nice way to assemble bitarrays, it would do that.

 nail schrieb:
 What about integrated bitfields in D?
 It would be nice if I write:

 enum A {AA = 0x1, AB = 0x2, AC = 0x4}
 enum B {BA = 0x8, BB = 0x4, BC = 0x0}
 const uint CA = 0x80;

 void foo(bitfield A a)
 {
 ..
 }

 Then I can use combination of values in A as parameter and can't mix 
 it with
 values in B or constant C.

 Is it necessary for D?


No! In this way I can use CA as index, for example. Or if you mean associative array I can't use literals and construction becomes bulky. bit[A] param; param[AA] = true; param[AC] = true; foo(param); very long... Correct me if I understood smthing incorrectly.

How about this... import std.stdio; enum A {AA = 0x1, AB = 0x2, AC = 0x4} enum B {BA = 0x8, BB = 0x4, BC = 0x0} const uint CA = 0x80; void bar(A a) { bit[] arr; arr = (cast(bit *)&a)[0..8]; foreach(bit b; arr) printf("%d\n",cast(int)b); printf("\n"); } void main() { bar(A.AA|A.AB); bar(A.AB|A.AC); } Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Aug 19 2004
parent reply nail <nail_member pathlink.com> writes:
In article <opsczz26cv5a2sq9 digitalmars.com>, Regan Heath says...
On Thu, 19 Aug 2004 18:03:10 +0000 (UTC), nail <nail_member pathlink.com> 
wrote:
 In article <cg1ums$1o4e$1 digitaldaemon.com>, Ilya Minkov says...
 I think if we had a nice way to assemble bitarrays, it would do that.

 nail schrieb:
 What about integrated bitfields in D?
 It would be nice if I write:

 enum A {AA = 0x1, AB = 0x2, AC = 0x4}
 enum B {BA = 0x8, BB = 0x4, BC = 0x0}
 const uint CA = 0x80;

 void foo(bitfield A a)
 {
 ..
 }

 Then I can use combination of values in A as parameter and can't mix 
 it with
 values in B or constant C.

 Is it necessary for D?


No! In this way I can use CA as index, for example. Or if you mean associative array I can't use literals and construction becomes bulky. bit[A] param; param[AA] = true; param[AC] = true; foo(param); very long... Correct me if I understood smthing incorrectly.

How about this... import std.stdio; enum A {AA = 0x1, AB = 0x2, AC = 0x4} enum B {BA = 0x8, BB = 0x4, BC = 0x0} const uint CA = 0x80; void bar(A a) { bit[] arr; arr = (cast(bit *)&a)[0..8]; foreach(bit b; arr) printf("%d\n",cast(int)b); printf("\n"); } void main() { bar(A.AA|A.AB); bar(A.AB|A.AC); } Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/

Gi-gi-gi. Following code compiles and runs normaly, while I expected compile-time error: import std.c.stdio; enum A {AA = 0x1, AB = 0x2, AC = 0x4} enum B {BA = 0x8, BB = 0x4, BC = 0x0} const uint CA = 0x80; void bar(A a) { bit[] arr; arr = (cast(bit *)&a)[0..8]; foreach(bit b; arr) printf("%d\n",cast(int)b); printf("\n"); if (a & B.BB) // Hm. Is this normal? printf("Yo!\n"); } void main() { bar(A.AA|B.BB); // Why B.BB is compiled??? bar(A.AB|A.AC); getch(); }
Aug 20 2004
parent reply Regan Heath <regan netwin.co.nz> writes:
On Fri, 20 Aug 2004 07:16:22 +0000 (UTC), nail <nail_member pathlink.com> 
wrote:

<snip>

 Gi-gi-gi. Following code compiles and runs normaly, while I expected
 compile-time error:

Yeah.. I am not 100% certain, but, from my tests, which are: bar(1); //cannot implicitly convert expression 1 of type int to A bar(1|A.AA); //cannot implicitly convert expression 1 | cast(A)1 of type int to A bar(B.BB|A.AA); //cannot implicitly convert expression cast(B)4 | cast(A)1 of type B to A bar(A.AA|1); //ok bar(A.AA|B.BB); //ok it appears | causes each operand to be converted to 'int' then the result is converted back to the first operands type. So the following: bar(A.AA|<anything that can implicitly be converted to int>); will work. Walter can tell us for sure.
 import std.c.stdio;

 enum A {AA = 0x1, AB = 0x2, AC = 0x4}
 enum B {BA = 0x8, BB = 0x4, BC = 0x0}
 const uint CA = 0x80;

 void bar(A a)
 {
 bit[] arr;

 arr = (cast(bit *)&a)[0..8];
 foreach(bit b; arr)
 printf("%d\n",cast(int)b);
 printf("\n");

 if (a & B.BB) // Hm. Is this normal?

I believe so, & probably does the same as | i.e. it tries to implicitly convert each operand to 'int', and the result back to the type of the first operand, in this case an A.
 printf("Yo!\n");
 }

 void main()
 {
 bar(A.AA|B.BB); // Why B.BB is compiled???

See my reasoning/assumption/conclusion above.
 bar(A.AB|A.AC);
 getch();
 }

Regards, Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Aug 20 2004
parent reply nail <nail_member pathlink.com> writes:
bar(1);         //cannot implicitly convert expression 1 of type int to A
bar(1|A.AA);    //cannot implicitly convert expression 1 | cast(A)1 of 
type int to A
bar(B.BB|A.AA); //cannot implicitly convert expression cast(B)4 | cast(A)1 
of type B to A
bar(A.AA|1);    //ok
bar(A.AA|B.BB); //ok

it appears | causes each operand to be converted to 'int' then the result 
is converted back to the first operands type. So the following:

bar(A.AA|<anything that can implicitly be converted to int>);

will work. Walter can tell us for sure.

I'm not sure, that this behaviour of compiler is the best case. First: how can I in a language with strong types use combination of different types?! No mater that implicitly they are integers. I even can't understand how I can use combination of constants of one type while function accepts value of enumerated, enum(!), enumeration(!), case value(!), one value(!) in other words. Predecoration looks little ugly. I prefer use <enum value> instead of <enum type> enum value. IMHO the best case for D if the following code will compile as expected in comments: enum Color { RED = 1, GREEN = 2, ORANGE = 4, } enum Fruit { ORANGE = 1, APPLE = 8, } void xxx(Color color) {...} void yyy(bitfield Color colors) {...} void zzz(Fruit f) {...} void main() { xxx(RED); // ok. xxx(RED | GREEN); // error: bitfield not accepted xxx(ORANGE); // ok. Value 4 (not 1) used yyy(RED | ORANGE); // ok. Value 4 for ORANGE used yyy(RED | APPLE); // error: APPLE not fount in Color enum zzz(ORANGE); // ok. Value 1 (not 4) used }
Aug 20 2004
next sibling parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <cg4tdf$ado$1 digitaldaemon.com>, nail says...

enum Color
{
RED = 1,
GREEN = 2,
ORANGE = 4,
}

void yyy(bitfield Color colors) {...}

yyy(RED | ORANGE); // ok. Value 4 for ORANGE used

Ignoring for now the whole enums being annoyingly verbose thing, already explored in another thread a few days ago, I have to say I don't like your bitfield idea, the main reason being that there is no compile-time check that the enum values are powers of two. You /could/ suggest that Walter do something like: # bitfield enum Color { RED, GREEN, BLUE }; // no initializer values needed to ask the compiler to choose the constants for you - that would be better. But before you start exploring that idea, consider that we already have bit[] arrays (aka bool[] arrays). So you can already do: # enum Color { RED, GREEN, BLUE }; // no initializer values needed then you can just declare your "bitfield variables" like this: # bit[Color.max+1] xxx; and subsequently refer to: # (xxx[Color.RED] || xxx[Color.BLUE]) Will that not do? Jill
Aug 20 2004
parent reply nail <nail_member pathlink.com> writes:
Ignoring for now the whole enums being annoyingly verbose thing, already
explored in another thread a few days ago, I have to say I don't like your
bitfield idea, the main reason being that there is no compile-time check that
the enum values are powers of two. 

It can be not power of two. Either 0 or 3 for example. Remember window's function MessageBox. Last parameter is bitfield that indicates what set of buttons and what icon to use. MB_OK is equal to 0, so I can write MB_ICONWARNING | MB_OK for readability only. Or remember window styles, imagine WS_POPUP = 0x1 and WS_CAPTION = 0x2, then I want to have const WS_OVERLAPED_WINDOW = WS_POPUP | WS_CAPTION == 3.
You /could/ suggest that Walter do something like:

# bitfield enum Color { RED, GREEN, BLUE }; // no initializer values needed

to ask the compiler to choose the constants for you - that would be better. But
before you start exploring that idea, consider that we already have bit[] arrays
(aka bool[] arrays). So you can already do:

# enum Color { RED, GREEN, BLUE }; // no initializer values needed

then you can just declare your "bitfield variables" like this:

# bit[Color.max+1] xxx;

and subsequently refer to:

# (xxx[Color.RED] || xxx[Color.BLUE])

Will that not do?

This is not type safe and bulky
Jill

Aug 20 2004
parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <cg54eu$e1t$1 digitaldaemon.com>, nail says...

It can be not power of two.

Good point. That's more like the old struct { int x : n }; objects you got in C. But in the D overview, Walter lists among the things to drop: "Bit fields of arbitrary size. Bit fields are a complex, inefficient feature rarely used." So that takes us back to ANDing and ORing constants together, which is back where we started.
# (xxx[Color.RED] || xxx[Color.BLUE])

Will that not do?

This is not type safe

You'll get no argument from me there! But then, D is not a typesafe language. If it were, then bool, int, char and enum would not be mutually implicitly convertible.
and bulky

I've never been particularly bothered by that. Still, I'm quite happy to accept syntactic sugar so long as things stay readable. Arcane Jill
Aug 20 2004
parent reply "Carlos Santander B." <carlos8294 msn.com> writes:
"Arcane Jill" <Arcane_member pathlink.com> escribió en el mensaje
news:cg5jte$m33$1 digitaldaemon.com
| In article <cg54eu$e1t$1 digitaldaemon.com>, nail says...
|
|| It can be not power of two.
|
| Good point. That's more like the old struct { int x : n }; objects you got in
C.
| But in the D overview, Walter lists among the things to drop: "Bit fields of
| arbitrary size. Bit fields are a complex, inefficient feature rarely used." So
| that takes us back to ANDing and ORing constants together, which is back where
| we started.
|
|
||| # (xxx[Color.RED] || xxx[Color.BLUE])
|||
||| Will that not do?
||
|| This is not type safe
|
| You'll get no argument from me there! But then, D is not a typesafe language.
If
| it were, then bool, int, char and enum would not be mutually implicitly
| convertible.
|
|
|| and bulky
|
| I've never been particularly bothered by that. Still, I'm quite happy to
accept
| syntactic sugar so long as things stay readable.
|
| Arcane Jill

IMHO, the way Delphi deals with it is better: they're not enums, they're sets.
So you don't check if a certaing bit is set, but if a certain element is
present. They /could/ be implemented as bitfields, but for the user they look
like sets. IMHO

-----------------------
Carlos Santander Bernal
Aug 20 2004
parent nail <nail_member pathlink.com> writes:
In article <cg6dun$145f$1 digitaldaemon.com>, Carlos Santander B. says...

IMHO, the way Delphi deals with it is better: they're not enums, they're sets.
So you don't check if a certaing bit is set, but if a certain element is
present. They /could/ be implemented as bitfields, but for the user they look
like sets. IMHO

Yop. Maybe this is most elegant and perfect way
-----------------------
Carlos Santander Bernal

Aug 21 2004
prev sibling parent Regan Heath <regan netwin.co.nz> writes:
On Fri, 20 Aug 2004 13:13:20 +0000 (UTC), nail <nail_member pathlink.com> 
wrote:
 bar(1);         //cannot implicitly convert expression 1 of type int to 
 A
 bar(1|A.AA);    //cannot implicitly convert expression 1 | cast(A)1 of
 type int to A
 bar(B.BB|A.AA); //cannot implicitly convert expression cast(B)4 | 
 cast(A)1
 of type B to A
 bar(A.AA|1);    //ok
 bar(A.AA|B.BB); //ok

 it appears | causes each operand to be converted to 'int' then the 
 result
 is converted back to the first operands type. So the following:

 bar(A.AA|<anything that can implicitly be converted to int>);

 will work. Walter can tell us for sure.

I'm not sure, that this behaviour of compiler is the best case. First: how can I in a language with strong types use combination of different types?!

There are degrees of "strong" in D's case, it's not that strongly typed.
 No mater
 that implicitly they are integers. I even can't understand how I can use
 combination of constants of one type while function accepts value of 
 enumerated,
 enum(!), enumeration(!), case value(!), one value(!) in other words.

Enum's can take a base type eg. enum A : ubyte {A} But Enums *must* be integral types you cannot say: enum A : double {A} As they are all integral types you can implicitly convert them. It does still check things like.. enum A : ubyte {A} enum B : {A} void foo(A a){} void main() { foo(A.A|B.A); } test4.d(8): function foo (A a) does not match argument types (int) test4.d(8): cannot implicitly convert expression cast(int)(0) | cast(B)0 of type int to A
 Predecoration looks little ugly. I prefer use <enum value> instead of 
 <enum
 type> enum value. IMHO the best case for D if the following code will 
 compile as
 expected in comments:

 enum Color
 {
 RED = 1,
 GREEN = 2,
 ORANGE = 4,
 }

 enum Fruit
 {
 ORANGE = 1,
 APPLE  = 8,
 }

 void xxx(Color color) {...}
 void yyy(bitfield Color colors) {...}
 void zzz(Fruit f) {...}

 void main()
 {
 xxx(RED); // ok.
 xxx(RED | GREEN); // error: bitfield not accepted
 xxx(ORANGE); // ok. Value 4 (not 1) used

 yyy(RED | ORANGE); // ok. Value 4 for ORANGE used
 yyy(RED | APPLE); // error: APPLE not fount in Color enum

 zzz(ORANGE); // ok. Value 1 (not 4) used
 }

See my other thread entitled 'Enums are annoyingly verbose'. I think some times the 'decoration' is superfluous, there are several occasions when it is not however. (the other thread has some examples) Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Aug 20 2004
prev sibling parent Mark T <Mark_member pathlink.com> writes:
In article <cg1id4$lou$1 digitaldaemon.com>, nail says...
What about integrated bitfields in D?

I guess you could use C functions underneath to do the bitfield stuff. Of course that adds overhead.
Aug 21 2004