Bitwise operations on packed structs

hello

what’s the idiomatic way to write this (non compiling) code.

const F = packed struct(u32) {
	first: bool = false,
	second: bool = false,
	_0: u30 = 0,
};

var f: F = .{ .first = true };
const m: u32 = 0x3;

f |= @bitCast(m);

// use f

This is the current idea

const t: F = @bitCast(m);
inline for (std.meta.fields(F)) |field| {
	@field(f, field.name) |= @field(t, field.name);
}
const std = @import("std");

pub fn main() !void {
    const F = packed struct(u32) {
        first: bool = false,
        second: bool = false,
        _0: u30 = 0,
    };

    var f: F = .{ .first = true };
    const m: u32 = 0x3;
    
    f = @bitCast(m | @as(u32, @bitCast(f)));
    
    std.debug.print("{}", .{f.first});
}

Works for me

5 Likes

You also could do this:

const std = @import("std");

pub fn main() !void {
    const F = packed struct(u32) {
        first: bool = false,
        second: bool = false,
        _0: u30 = 0,
    };

    var f: F = .{ .first = true };
    const m: u32 = 0x3;

    const f_u32_view: *u32 = @ptrCast(&f);
    f_u32_view.* |= m;

    std.debug.print("{}\n", .{f});
}

Using the backing int directly.

8 Likes

If the intent is to set first and second to true. Why not just do that?

const F = packed struct(u32) {
	first: bool = false,
	second: bool = false,
	_0: u30 = 0,
};

var f: F = .{ .first = true };
f.second = true;

I’d guess your use case is more complicated than the example, but the direct assignment is explicit and the compiler can figure out how to jiggle the bits. Also, there is an implicit assumption about how bool is encoded (yes, we all know it’s a u1 and false is zero and true is one – but you’re making me remember that.). Honestly, I’m trying to put all that “C” bit encoded junk out of my head. There are better alternatives.

1 Like
pub const Op = packed struct(u8) {
    a: u1,
    b: u1,
    _: u6 = 0,

    inline fn binOp(lhs: Op, rhs: Op, comptime op_str: []const u8) Op {
        const Backing = @typeInfo(@This()).@"struct".backing_integer.?;
        const l_raw: Backing = @bitCast(lhs);
        const r_raw: Backing = @bitCast(rhs);
        return @bitCast(switch (@field(
            enum { bitOr, bitAnd, bitXor },
            op_str,
        )) {
            .bitOr => l_raw | r_raw,
            .bitAnd => l_raw & r_raw,
            .bitXor => l_raw ^ r_raw,
        });
    }

    pub fn bitOr(lhs: Op, rhs: Op) Op {
        return Op.binOp(lhs, rhs, @src().fn_name);
    }
    pub fn bitAnd(lhs: Op, rhs: Op) Op {
        return Op.binOp(lhs, rhs, @src().fn_name);
    }
    pub fn bitXor(lhs: Op, rhs: Op) Op {
        return Op.binOp(lhs, rhs, @src().fn_name);
    }
};