second draft: add Bitfields to D
Walter Bright
newshound2 at digitalmars.com
Mon Apr 29 06:44:08 UTC 2024
On 4/28/2024 3:30 PM, Timon Gehr wrote:
> However, I would still much prefer a solution that explicitly introduces the
> underlying `int`, `uint`, `long` and `ulong` fields, which would be the ones
> visible to introspection in a portable way, so that introspection code does not
> really need to concern itself with bitfields at all if it is not important and
> we do not break existing introspection libraries, such as all serialization
> libraries.
I doubt introspection libraries would break. If they are not checking for
bitfields, but are just looking at .offsetof and the type, they'll interpret the
bitfields as a union (which, in a way, is accurate).
>> Symbolic Debug Info
>
> This does not seem like a strong argument. I am pretty confident debug info can
> work pretty well regardless of how D lays out the bits.
I'm not. I'd follow the dwarf spec and it didn't work, because the only thing
that was ever tested was apparently what the C compiler actually did. In order
to get gdb to work, I wound up ignoring the spec and doing what gcc did. It's
the same with object file formats. The spec is somewhat of a fairy tale, it's
what the associated C compiler actually does that matters.
> I like that the members are not as cluttered. I guess maybe some people still
> would like to access the underlying data (e.g., to implement a pointer to
> bitfield as a struct with a pointer plus bit offset and bit length, or
> something), so perhaps you could add a note that explains how to do that.
Pointer to bitfields will work just the same as they do in C. I don't understand
what you're asking for.
> You forgot to say what `.tupleof` will do for a struct with bitfields in it.
They do exactly what you'd expect them to do:
```
import std.stdio;
struct S { int a:4, b:5; }
void main()
{
S s;
s.a = 7;
s.b = 9;
writeln(s.tupleof);
}
```
prints:
```
79
```
It's not necessary to specify this, because this behavior does not diverge from
field access semantics. Only things that differ need to be specified. Specifying
"it works like X except for A,B,C" is a lot more reliable and compact than
reiterating everything X does.
> I think it would be better to have such a `__traits` even just for
> discoverability when people look at the `__traits` page to implement some
> introspection code.
There isn't for other members, it's just "allMembers".
>> testing to see if the address of a field can be taken, enables discovery of a
>> bitfield.
>
> Not really, a field could be an `enum` field, and you cannot take the address of
> that either. And if we ever add another feature that has fields whose address
> can be taken, existing introspection code may break. It is better to be explicit.
An enum is distinguished by it not being possible to use .offsetof with it.
>> The values of .max or .min enable determining the number of bits in a bitfield.
> I do not like this a lot, it does not seem like the canonical way to determine
> it. `.bitlength`?
I agree it's a bit(!) jarring at first blush, but it's easy and perfectly
reliable. 7 and 15 are always going to be a 4 bit field. We do a lot of
introspection via indirect things like this.
>> The bit offset can be introspected by summing the number of bits in each
>> preceding bitfield that has the same value of .offsetof.
>
> I think it would be much better to just add a `__trait` for this or add
> something like `.bitoffsetof`. This is a) much more user friendly and b) is a
> bit more likely to work reliably in practice. D currently does not give any
> guarantees on the order you will see members when using `__traits(allMembers,
> ...)`.
I overlooked that bitfields can have holes in them, so probably something like
.bitoffsetof is probably necessary.
More information about the dip.development
mailing list