www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Why do bitfields throw exceptions instead of wrapping?

reply "ixid" <nuaccount gmail.com> writes:
In C++ this works:

struct test
{
	unsigned int h : 2;
};

int main()
{
	test b;
	b.h = 0;
	for(int i = 0;i < 10;i++)
		++b.h;
	return 0;
}

In D this throws an exception as soon as it wraps:

struct test
{
	mixin(bitfields!(
					 uint, "h", 2,
					 uint, "", 30));
};

int main()
{
	test b;
	b.h = 0;
	for(int i = 0;i < 10;i++)
		b.h = b.h + 1;
	return 0;
}

Is there any way to make it work directly like the C++ example 
without doing something like
if(bitfield is at max)
     wrap it by hand;

It also seems odd to leave out the various shortcut operators 
like ++, *= etc fr bitfields.
Feb 29 2012
parent James Miller <james aatch.net> writes:
On 1 March 2012 16:15, ixid <nuaccount gmail.com> wrote:
 In C++ this works:

 struct test
 {
 =C2=A0 =C2=A0 =C2=A0 =C2=A0unsigned int h : 2;
 };

 int main()
 {
 =C2=A0 =C2=A0 =C2=A0 =C2=A0test b;
 =C2=A0 =C2=A0 =C2=A0 =C2=A0b.h =3D 0;
 =C2=A0 =C2=A0 =C2=A0 =C2=A0for(int i =3D 0;i < 10;i++)
 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0++b.h;
 =C2=A0 =C2=A0 =C2=A0 =C2=A0return 0;
 }

 In D this throws an exception as soon as it wraps:

 struct test
 {
 =C2=A0 =C2=A0 =C2=A0 =C2=A0mixin(bitfields!(
 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 uint, "h= ", 2,
 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 uint, ""= , 30));
 };

 int main()
 {
 =C2=A0 =C2=A0 =C2=A0 =C2=A0test b;
 =C2=A0 =C2=A0 =C2=A0 =C2=A0b.h =3D 0;
 =C2=A0 =C2=A0 =C2=A0 =C2=A0for(int i =3D 0;i < 10;i++)
 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0b.h =3D b.h + 1;
 =C2=A0 =C2=A0 =C2=A0 =C2=A0return 0;
 }

 Is there any way to make it work directly like the C++ example without do=
ing
 something like
 if(bitfield is at max)
 =C2=A0 =C2=A0wrap it by hand;

 It also seems odd to leave out the various shortcut operators like ++, *=
=3D
 etc fr bitfields.
Looking through the bitmanip code, what happens is that the mixin creates properties, including the setters and getters for those properties, in the struct. Due to the way properties work, b.h is an rvalue, this is because b.h -> b.h(), the property declaration just makes it so you can omit the parenthesis. Thinking of it like this, you wouldn't try this.getInt()++ would you? Some experimentation shows that a ref getter allows for this kind of modification, however, it is difficult to have both ref T and T getters, due to the compiler not being able to choose which is which when resolving the calls in most cases. Unfortunately it would be unwise to just change the getter to a ref type, since that would mean that any changes to it after retrieval would affect the original. It is difficult to create a bit field that allows for all of the features described, but still prevents stupid code from prevailing. e.g. a 2-bit uint a: a =3D 4, if it wrapped rather than errored, then that could cause all sorts of errors down the line, and would be incredibly hard to diagnose. If you want wrapping however, during the increment you can do this: b.h =3D (b.h + 1) % b.h_max; since all fields define a *_max field. Not as elegant as doing it internally, but better than using a condition. Bitfields are for tightly packed data, and therefore expecting all language features to be available is missing the point. Hell bitfields are provided by templates, so they aren't even a part of the language, they are just a library feature. Hope that helps -- James Miller
Feb 29 2012