ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
34.92k stars 2.55k forks source link

Improving bitmask/flags #21657

Open Darkfllame opened 1 month ago

Darkfllame commented 1 month ago

Haven't found other issues for this, but as of today, the only way to substitute flags/bitmask in zig are packed structs. They do work but often introduce some impracticallities, such as:

Now a solution I propose is to allow simple enum-like fields when constructing a packed struct value:

const BitMask = packed struct {
  bit1: bool = false,
  bit2: bool = false,
  bit3: bool = false,
};

// like this
const value = BitMask{ .bit1, .bit3};

Bit operations on packed structs:

// same type as above

const a = BitMask{.bit1, .bit3};
const b = a | .{.bit2};
// this wont set the remaining 5 bits
const c = ~b;
const is3rdBit = a.bit3;
// optimizations could include bit-and op
const is2ndAnd3rd = a.bit2 or a.bit3;

Some rules concerning this:

This is only if we expand on the syntax, we could also add a "bitmask" type or alike, which would natively include those features along of a backing integer, for example:

const BitMask = bitmask {
  /// these are lsb to hsb, like packed structs
  bit1,
  /// possible documentation
  bit2,
  bit2,
};

Which would introduce a clearer separation for new-comers to the language, while requiring more work to implement a whole new type of data in the compiler.

rohlem commented 1 month ago

For bit operations that are sensible for a particular type, you can already declare methods on the type.

For your second initial point, for common combinations you may consider decl literals, or again methods (f.e. in the "builder style" of fn setX(Self, x) Self to get const s = (S{}).setX(.x).setY(2) [...] ;). Or maybe you'd prefer a factory method with a field enum fn of([]const FieldEnum) Self and use it as const s = S.of(&.{.visible, .fullscreen, .foo [, ...] });.

I personally don't think that all packed struct types (should) represent bit masks, therefore I think I prefer the explicitness of status-quo. For repeated semantic operations, write a method so callsites don't repeat themselves. I think I wouldn't mind a distinct bitmask construct, but also haven't personally experienced the need for it.

(Tangentially related: https://github.com/ziglang/zig/issues/18882 )