Live data from Hacker News

Packed structs in Zig make bit/flag sets trivial

devlog.hexops.com

41–50 of 147 posts

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

#41
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

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.

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

#42
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)?

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

#43

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.

In C I'm using a macro for stuff like that (improving readability, avoiding errors). In Zig I could use a `comptime` function, I presume?

It depends on what else you’re doing. In this situation normal function would suffice in either C or Zig.

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

#44

> This all works, people have been doing it for years in C, C++, Java, Rust, and more. In Zig, we can do better. We can also do better in those other languages, too. For example, in Rust, I can use a crate like `bitfield` which gives me a macro with which I can write bitfield! { pub struct Color(u32); red, set_red: 0; green, set_green: 1; blue, set_blue: 2; alpha, set_alpha: 3; } Don't get me wrong: it's cool that fu…

Is it something I'd ever want to rely on third-party functionality for?

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

#45
post #44

> This all works, people have been doing it for years in C, C++, Java, Rust, and more. In Zig, we can do better. We can also do better in those other languages, too. For example, in Rust, I can use a crate like `bitfield` which gives me a macro with which I can write bitfield! { pub struct Color(u32); red, set_red: 0; green, set_green: 1; blue, set_blue: 2; alpha, set_alpha: 3; } Don't get me wrong: it's cool that fu…

Is it something I'd ever want to rely on third-party functionality for?

Yes? If you have lots of bitfields and the convenience / readability is worth it, why wouldn’t you?

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

#46
post #30
post #24

Earlier quoted context omitted.

Far as I can tell 'don't use bit fields' is something students learn from their CS professors along with don't use goto, multiple returns, continue statements, pass structs by value, and always convert to big endian when sending data over a wire. My experience is C compilers have ways packing and defining the order of bitfields and structs.

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

The LoRaWAN standard uses big endian.

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

#47
post #44

Earlier quoted context omitted.

Is it something I'd ever want to rely on third-party functionality for?

Yes? If you have lots of bitfields and the convenience / readability is worth it, why wouldn’t you?

I meant as opposed to having it built in.

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

#48

Earlier quoted context omitted.

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)?

That's the point of a packed struct: to explicitly declare the physical layout (instead of letting the compiler optimize it). If a packed struct ends up being non-portable between Zig implementations, then my impression is that one and/or the other must have a bug that must be fixed in order to actually comply with the language spec.

It is not possible to specify bitfields in a way that makes sense, is consistent with a bytewise view of memory, and is portable between big endian and little endian processors.

Say you have, for instance (using C notation)

  struct {
    unsigned one : 8;
    unsigned two : 8;
  };
The fields are supposed to be represented in memory in the same order they are declared, so one is the first byte and two is the second byte. This should have the same representation as if I declared it as two uint8_t fields. If I type pun it and load it into a register as a uint16_t then it depends on the hardware whether the low and high bytes are one and two or two and one.

It gets more tricky when you consider arbitrary bit widths.

  struct {
    unsigned u4 : 4;
    unsigned uC : 12;
  };
If the fields are allocated in order, is u4 the low bits of the first byte or the high bits? If you require it to be the low bits, then it works ok on a little endian machine, but on a big endian machine the uC field ends up split, so the 16 bit view looks like:

  CCCC4444CCCCCCCC

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

#49
post #6

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

Admittedly I didn't know C had bit fields like that, so I didn't cover them. I've never seen them used in practice and I've used quite a lot of C libraries. I wonder why that is? One major difference appears to be that C bitfields memory layout is compiler-dependant. The other major difference is Zig's arbitrary-bit-width integer types just leading to less footguns I would speculate

cf K&R 6.9 "Bit-fields"

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

#50

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

The in-memory representation of bit fields is implementation-defined. Therefore, if you're calling into an external API that takes a uint32_t like in the example without an explicit remapping, you may or may not like the results.

In practice, everything you're likely to come across will be little endian nowadays, and the ABI you're using will most likely order your struct from top to bottom in memory, so they will look the same most of the time. However, it's still technically not portable.

Post reply on HN