How to properly coerce from enum(u8) to u8?

I have a bunch of functions, that takes u8, but I feel, that I’d better pass an coerced enum(u8) to them.
I know about @intFromEnum, but is there a proper way, to do it, instead of this:

pub fn init() void {
    // Write to registers their initial values
    writeReg(@intFromEnum(Reg.config), 0x08);
    writeReg(@intFromEnum(Reg.en_aa), 0x3F);
    writeReg(@intFromEnum(Reg.en_rx_addr), 0x03);
    writeReg(@intFromEnum(Reg.setup_aw), 0x03);
    writeReg(@intFromEnum(Reg.setup_retr), 0x03);
    writeReg(@intFromEnum(Reg.rf_ch), 0x02);
    writeReg(@intFromEnum(Reg.rf_setup), 0x0E);
    writeReg(@intFromEnum(Reg.status), 0x00);
    writeReg(@intFromEnum(Reg.rx_pw_p0), 0x00);
    writeReg(@intFromEnum(Reg.rx_pw_p1), 0x00);
    writeReg(@intFromEnum(Reg.rx_pw_p2), 0x00);
    writeReg(@intFromEnum(Reg.rx_pw_p3), 0x00);
    writeReg(@intFromEnum(Reg.rx_pw_p4), 0x00);
    writeReg(@intFromEnum(Reg.rx_pw_p5), 0x00);
    writeReg(@intFromEnum(Reg.dynpd), 0x00);
    writeReg(@intFromEnum(Reg.feature), 0x00);

    ...

EDIT: If I would do unions, it’s creates no beauty, at least it’s shorter.

writeReg(.{.reg = Reg.feature}, 0x00);

But I dk how to pass same way multiple enum types properly…

I don’t fully understand what you are asking, do you just want a shorter thing to write?

You could use a helper method that expects a Reg:

pub fn setReg(reg:Reg, value:u8) void {
   writeReg(@intFromEnum(reg), value);
}
pub fn init() void {
    // Write to registers their initial values
    setReg(.config, 0x08);
    setReg(.en_aa, 0x3F);
    setReg(.en_rx_addr, 0x03);
    setReg(.setup_aw, 0x03);
    setReg(.setup_retr, 0x03);
    setReg(.rf_c), 0x02);
    setReg(.rf_setup, 0x0E);
    setReg(.status, 0x00);
    setReg(.rx_pw_p0, 0x00);
    setReg(.rx_pw_p1, 0x00);
    setReg(.rx_pw_p2, 0x00);
    setReg(.rx_pw_p3, 0x00);
    setReg(.rx_pw_p4, 0x00);
    setReg(.rx_pw_p5, 0x00);
    setReg(.dynpd, 0x00);
    setReg(.feature, 0x00);
    ...

Or if you want to you could do something like this:

const std = @import("std");

pub fn setRegisters(values: anytype) void {
    inline for (std.meta.fields(@TypeOf(values))) |f| {
        writeReg(@intFromEnum(@field(Reg, f.name)), @field(values, f.name));
    }
}

pub fn init() void {
    // Write to registers their initial values
    setRegisters(.{
        .config = 0x08,
        .en_aa = 0x3F,
        .en_rx_addr = 0x03,
        .setup_aw = 0x03,
        .setup_retr = 0x03,
        .rf_c = 0x02,
        .rf_setup = 0x0E,
        .status = 0x00,
        .rx_pw_p0 = 0x00,
        .rx_pw_p1 = 0x00,
        .rx_pw_p2 = 0x00,
        .rx_pw_p3 = 0x00,
        .rx_pw_p4 = 0x00,
        .rx_pw_p5 = 0x00,
        .dynpd = 0x00,
        .feature = 0x00,
    });
}

const Reg = enum {
    config,
    en_aa,
    en_rx_addr,
    setup_aw,
    setup_retr,
    rf_c,
    rf_setup,
    status,
    rx_pw_p0,
    rx_pw_p1,
    rx_pw_p2,
    rx_pw_p3,
    rx_pw_p4,
    rx_pw_p5,
    dynpd,
    feature,
};

fn writeReg(reg: u8, val: u8) void {
    std.debug.print("reg: {} val: {}\n", .{ reg, val });
}

pub fn main() !void {
    init();
}
1 Like

You know, maybe an enum isn’t the right tool here? Enums have a lot of features behind them (allowing you to create member functions, . access syntax…), but are you using them in any way other than integers?

You right, but what should I use then?
I don’t think it’s right to create about 20 const u8s with a prefix instead (which is what I was thinking too)
EDIT: I have 10 enums like it right now, I want to group them somehow.

Well, if you have 20 u8’s under the guise of an enum that’s getting used only as u8’s… if it quacks like a duck lol…

I’m trying to get where you’re coming from here, so help me understand your position here a bit better.

Essentially, we’re doing this:

const std = @import("std");

const KeyPair = struct {
    gid: usize,
    wid: usize    
};

const MyEnum = enum(u8) {
    a = 50,
    b = 60,

    pub fn int(self: MyEnum) u8 {
        return @intFromEnum(self);
    }
};

pub fn main() !void {
    std.log.info("value: {}, {}", .{ MyEnum.a.int(), MyEnum.b.int() });
}

Now that shortens things up, but it doesn’t really accomplish anything meaningful.

If you want to group them, you could just make them class level values:

const A = struct {
const something: u8 = 0;
const something_else: u8 = 1;
};

const B = struct {
const something: u8 = 0;
const something_else: u8 = 1;
}

Now your access looks like an enum and you can group things… plus you get that nice . completion syntax if you’re using an lsp.

A.something // gives you a u8 directly
2 Likes

The data can be an array of pairs:

const data = [_]struct{Reg,u8}{
    .{Reg.config, 0x08},
    .{Reg.en_aa, 0x3F},
    ...
};

for (data) |reg| {
    writeReg(@intFromEnum(reg[0]), reg[1]);
}
1 Like

I think now, using class level values would be my solution, thank you!
Also, there were idea in my mind, to separate enums into different files, so while I import them, I would get completion syntax, and get the values as const. But I think this is the same, and would be easier to change enums to structs, then file hierarchy.

1 Like