Live data from Hacker News

Packed structs in Zig make bit/flag sets trivial

devlog.hexops.com

51–60 of 147 posts

Re: Packed structs in Zig make bit/flag sets trivial

#51
post #22

Earlier quoted context omitted.

Here is a sample of how you do this in C++ https://godbolt.org/z/W3bMcqM5P

Wow... I thought I had seen a lot of strange C++ code but I have _never_ seen packed structs using this syntax: struct X { type alias : numeric_value = false; ... } I love the power of C++ but there is _so much_ to the language. I'm sure there would also be some template meta programming solution even if this syntax was available.

Uh, that's standard C89. You know, the bit fields? Except for the "false" keyword, that's from stdbool.h and you need C99 for that to reliably exist.

Re: Packed structs in Zig make bit/flag sets trivial

#52
post #40

The language I'm working on has the following syntax bitfield ColorWriteMaskFlags : u32 { red 1 bool, green 1 bool, blue 1 bool, alpha 1 bool, }; The number after the field name is the bit size. An (offset, size) pair can be used instead (offsets start at 0 when not explicit). After that can be nothing (the bit field is an unsigned number), or the word 'bool' (the bit field is a boolean) or the word 'signed' (the bit…

The one thing that would worry me here is the endianness e.g. is red the LSB or the MSB? Also what happens to the padding (bits not covered by subfields)? How’s that going to look when shoved into a file or over a socket? how does the langage handle overflow (more bits in the bit fields than there are in the parent field)?

Red is the least significant bit, because offsets start at bit #0. Endianness is not a concern because you have to specify the underlying integer type, and bit endianness is not a thing, so it's just the native endianness (if you, e.g., need a network format with specific endianness and a 32-bit bitfield, you can instead use 4 consecutive 8-bit bitfields laid out in a consistent way). Unspecified bits are ignored; they'll be zero if the bitfield is initialized through conventional means but if they can be accessed directly through the raw value (or e.g. through memcpy, pointer casting, and any other direct memory access means), but when reading a field only its specified bits are read so unspecified bits doesn't change that result. When writing to a field, the source value is truncated to the field size, so you never end up writing to other bits.

Re: Packed structs in Zig make bit/flag sets trivial

#53
post #22

This is a nice syntax, but the functionality seems essentially the same as bit fields in C. Does this provide anything additional?

Here is a sample of how you do this in C++ https://godbolt.org/z/W3bMcqM5P

I thought you were not able to initialize bitfield members like that in struct. Am I misremembering, was that something else or is it compiler/c++ release dependent?

Re: Packed structs in Zig make bit/flag sets trivial

#54

Earlier quoted context omitted.

> You can have arbitrary bit width integers as well. This is news to me, how? AFAIK they'll only eventually make it into C23 with _BitInt(N). ...and apart from building a C++ wrapper class of course, but how would this pack with data outside the class - like the 4 + 28 bits example in the blog post. Also IIRC when I tinkered with C/C++ bitfields, some compilers (at least MSVC I think?) didn't properly pack the bits (…

The order of a bitfield is explicitly implementation defined, at least in C++ [0] and C99. I have been bitten by this when compiling bit-fiddling code with a different compiler (for the same platform). [0] section "Notes" in https://en.cppreference.com/w/cpp/language/bit_field

Interesting, I saw some low level code that assumes that bits are neatly packed.

Some C decisions really confuses me. What would be the point of this one?

Re: Packed structs in Zig make bit/flag sets trivial

#55

Earlier quoted context omitted.

Wow... I thought I had seen a lot of strange C++ code but I have _never_ seen packed structs using this syntax: struct X { type alias : numeric_value = false; ... } I love the power of C++ but there is _so much_ to the language. I'm sure there would also be some template meta programming solution even if this syntax was available.

Uh, that's standard C89. You know, the bit fields? Except for the "false" keyword, that's from stdbool.h and you need C99 for that to reliably exist.

The rough C equivalent looks like https://godbolt.org/z/YqGPa7ecd , using _Static_assert ( available C11 on wards and as a GCC built-in from 4.6 ) with no default values for the struct members

Re: Packed structs in Zig make bit/flag sets trivial

#56
post #10

This is a nice syntax, but the functionality seems essentially the same as bit fields in C. Does this provide anything additional?

C bitfields are problematic and often aren't used in places where you'd think they'd be useful. Zig seems to be doing its best to get bitfields right. Just one thing that Zig improves: in C if you need the bit offsets of the fields you'll still need lots of defines with the offsets/masks and such. In Zig it can be extracted from the packed struct at comptime

Don’t get me wrong, it’s a cool feature, the only problem I see here is zero readability, what if I’m looking at this code or trying to figure out the value of a register mangled by such a call, “I” find it very hard to read and understand what is happening there. I can read hex offsets and masks to know what’s what is happening to bitfield at any given time… just MHO.

Re: Packed structs in Zig make bit/flag sets trivial

#57
post #22

Earlier quoted context omitted.

Here is a sample of how you do this in C++ https://godbolt.org/z/W3bMcqM5P

I thought you were not able to initialize bitfield members like that in struct. Am I misremembering, was that something else or is it compiler/c++ release dependent?

You can absolutely initialize bitfield members in structs, not sure where your memory is from… maybe it was broken on some specific compiler vendors/versions?

Re: Packed structs in Zig make bit/flag sets trivial

#58
post #7

if (mask & (WGPUColorWriteMask_Alpha|WGPUColorWriteMask_Blue)) { // alpha and blue are set.. } Doesn't this give you if alpha OR blue is set? Errors like this are another reason syntactical sugar for readability is important.

Yes it would need to be: if ((mask & (WGPUColorWriteMask_Alpha|WGPUColorWriteMask_Blue)) == (WGPUColorWriteMask_Alpha|WGPUColorWriteMask_Blue)) { ... } ...which is quite a mouthful.

I’ve definitely desired an operator that combines these before. I’ve settled on &== as the best syntax in C-style languages, with a &== b being equivalent to a & b == b (but with a single evaluation of b).

  if (mask &== WGPUColorWriteMask_Alpha|WGPUColorWriteMask_Blue) { … }
I find only two very minor problems with it: firstly, that it’s not commutative (that is, a &== b is not equivalent to b &== a). Secondly, that it can be confused with the bitwise-and assignment operator &= (a &= b being equivalent to a = a & b for singly-evaluated lvalue a).

I’d then add |== for consistency and because it is conceivably useful. ^== is tempting, but since a ^== b would be just another spelling of a == 0, I’d skip it.

Re: Packed structs in Zig make bit/flag sets trivial

#59

This is a nice syntax, but the functionality seems essentially the same as bit fields in C. Does this provide anything additional?

Bit fields in C are notoriously non-portable (exact physical layout depends on the compiler). Is Zig any better (apart from the fact that there is only one Zig compiler for now)?

Notoriously? I can't name any architecture/platform/compiler where bits aren't allocated consecutively in order of definition, without holes, in the same direction as system endianness...

Re: Packed structs in Zig make bit/flag sets trivial

#60

Earlier quoted context omitted.

I don't know why you haven't come across them but personally I've used bitfields a lot over my career as a video game programmer at different companies. You can have arbitrary bit width integers as well. It's not really very different to zig.

> You can have arbitrary bit width integers as well. This is news to me, how? AFAIK they'll only eventually make it into C23 with _BitInt(N). ...and apart from building a C++ wrapper class of course, but how would this pack with data outside the class - like the 4 + 28 bits example in the blog post. Also IIRC when I tinkered with C/C++ bitfields, some compilers (at least MSVC I think?) didn't properly pack the bits (…

>when I tinkered with C/C++ bitfields, some compilers (at least MSVC I think?) didn't properly pack the bits

There's #pragma pack for that.

Post reply on HN