Live data from Hacker News

Packed structs in Zig make bit/flag sets trivial

devlog.hexops.com

121–130 of 147 posts

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

#121
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.

This is why bit sets are far better at this and should be used, i.e.:

    if WGPUColorWriteMask.Alpha in mask and WGPUColorWriteMask.Blue in mask: ...

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

#122
post #89

Earlier quoted context omitted.

D just went ahead and implemented bit fields. They work, and it's hard to find any way to improve on them.

It's hard but they aren't perfect. You shouldn't rely on them for packing memory at all if you want to talk to other programs. Similarly bitfields mean you end up with "ints" that are actually 3 bits wide and so on.

> You shouldn't rely on them for packing memory at all if you want to talk to other programs.

While the layout is indeed implementation dependent, pragmatically if you stick to using ints the layouts are portable as far as I can tell. Just like the size of ints is implementation dependent, but is reliably 32 bits on 32 and 64 bit machines.

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

#123

So `unsigned flag:1`?

After you set all the pragmas or attributes to not add any additional padding/alignment, and that you really, really mean it. Maybe add a few static asserts so you know at compile time if you got the size right.

These are not standard, so you need some preprocessor magic to choose the right thing. And so on...

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

#124
post #57

Earlier quoted context omitted.

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?

(6): error C7582: 't': default member initializers for bit-fields requires at least '/std:c++20' It is c++20 apparently https://godbolt.org/z/qvso544dr

Ah, I misunderstood / didn't look properly and failed to notice this is a default value in the struct definition. My bad.

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

#125

In my opinion, syntactic sugar is, in fine, more useful and positive than "powerful" or "expressive" features that can lead to shorter but harder to decipher code. Syntactic sugar neve hurts.

I wouldn't say this is syntactic sugar here. packed structs are the most valuable tool in the language to handle interop with other languages and building protocols. The fact that you can use that tool to build bitfields feels like an interesting, cool and useful side effect. However, it's not their purpose.

Note that the main difference between packed structs and regular structs is not the dense bit packing, rather that regular structs are allowed to reorder the fields however they wish in memory. The compiler is free to optimize your struct. It's not free to do so in other languages (like C for example). Thus you get a way to define structs exactly, with bit level precision, to build complex protocols where you can decide what every bit means.

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

#126
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.

If you are willing to assume that the masks each only have 1 bit set, this would be less of a mouthful although it would draw other objections:

  if (@popCount(mask & (WGPUColorWriteMask_Alpha|WGPUColorWriteMask_Blue)) == 2)
I do not know Zig, so the syntax might not be right. I did check to see that it has popcount [1].

If it has some concise way to flip all the bits, then this would be another possibility that isn't too verbose, but might raise other objections. Let fmask be mask with all the bits flipped (how would one do that in Zig?).

  if ((fmask & (WGPUColorWriteMask_Alpha|WGPUColorWriteMask_Blue)) == 0)
[1] https://ziglang.org/documentation/master/#popCount

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

#127
post #126

Earlier quoted context omitted.

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

If you are willing to assume that the masks each only have 1 bit set, this would be less of a mouthful although it would draw other objections: if (@popCount(mask & (WGPUColorWriteMask_Alpha|WGPUColorWriteMask_Blue)) == 2) I do not know Zig, so the syntax might not be right. I did check to see that it has popcount [1]. If it has some concise way to flip all the bits, then this would be another possibility that isn't…

I think the point of the example is that in zig you'd just do

  if (mask.alpha and mask.blue) {

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

#128
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.

You don't need syntactical sugar. Just write a function.

    static bool all_bits_set(i32 value, i32 mask) { return (value & mask) == mask; }
Here I'm assuming 32-bit values. In C, with relatively little support for generics, you can consider making multiple versions, possibly using _Generic (note, I haven't evaluated the sanity of using _Generic).

Alternatively you can use a #define. However, you need to use "mask" twice, so that gets tricky - either it requires care to keep the corresponding expression at the call site side-effect free. Or the macro needs to be written using compiler extensions like statement expressions and typeof() variable declarations à la Linux kernel.

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

#129
post #30

Earlier quoted context omitted.

>always convert to big endian when sending data over a wire What is this awful advice. Only convert to big-endian where legacy demands it.

Meh. Network byte order is big endian. Otoh close to every device uses little endian internally and the conversion is pointless except for convention. Otoh it doesn't really matter, because the conversion is extremely fast (including hardware support in common processors). Endianness is really perfectly named: a meaningless difference that generations of people fight holy wars over.

Network only applies to information in packet headers. How you actually pack bytes into your packets is entirely up to you and/or the protocol you're using.

It was a flip of a coin choice.

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

#130
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.

You don't need syntactical sugar. Just write a function. static bool all_bits_set(i32 value, i32 mask) { return (value & mask) == mask; } Here I'm assuming 32-bit values. In C, with relatively little support for generics, you can consider making multiple versions, possibly using _Generic (note, I haven't evaluated the sanity of using _Generic). Alternatively you can use a #define. However, you need to use "mask" twic…

The nice thing about this is that it's simple and works pretty much in any language so there's very little cognitive strain if you work with multiple languages at the same time.
Post reply on HN