#Build configuration for pair of enums

1 messages · Page 1 of 1 (latest)

lunar bloom
#

Say I've two enums:

const Foo = enum {
   One,
   Two,
};

const Bar = enum {
   Red,
   Green,
};

Now suppose I want to create some further configuration for each pair combination, like: (One, Red) = 10, (One, Green) = 11, (Two, Red)=22, and (Two, Green)=83 [Values are arbitrary and there isn't a formula for them]
What is the best way to build this?

I could try doing some [2][2]u8 at comptime? But not sure if there's a good way to be sure thing won't compile if some elements gets added to Foo or Bar.
I could make 2 be at least calculated at comptime, but I also want to make sure that element is properly assigned.

acoustic hull
#

[@typeInfo(Foo).Enum.fields.len will compile error if the number of initializers don't match, but there's a better solution if I can find it

lunar bloom
#

I saw there's an std.enum.EnumMap which is somewhat nice but I don't know if "nesting it" would result in something interesting

acoustic hull
#

close, but not quite, that's for non-exhaustive mappings

lunar bloom
#

oh, true

acoustic hull
#
const std = @import("std");

const Foo = enum {
    One,
    Two,
};

const Bar = enum {
    Red,
    Green,
};

fn config1(foo: Foo, bar: Bar) u8 {
    return switch (foo) {
        .One => switch (bar) {
            .Red => 10,
            .Green => 11,
        },
        .Two => switch (bar) {
            .Red => 22,
            .Green => 83,
        },
    };
}

fn config2(foo: Foo, bar: Bar) u8 {
    const lut2 = std.enums.directEnumArray(Foo, [std.enums.directEnumArrayLen(Bar, 0)]u8, 0, .{
        .One = std.enums.directEnumArray(Bar, u8, 0, .{
            .Red = 10,
            .Green = 11,
        }),
        .Two = std.enums.directEnumArray(Bar, u8, 0, .{
            .Red = 22,
            .Green = 83,
        }),
    });
    return lut2[@intFromEnum(foo)][@intFromEnum(bar)];
}

fn config3(foo: Foo, bar: Bar) u8 {
    const lut3 = std.enums.EnumArray(Foo, std.enums.EnumArray(Bar, u8)).init(.{
        .One = std.enums.EnumArray(Bar, u8).init(.{
            .Red = 10,
            .Green = 11,
        }),
        .Two = std.enums.EnumArray(Bar, u8).init(.{
            .Red = 22,
            .Green = 83,
        }),
    });
    return lut3.get(foo).get(bar);
}

pub fn main() void {
    for (std.enums.values(Foo)) |foo| {
        for (std.enums.values(Bar)) |bar| {
            std.debug.print("config1({}, {}) = {}\n", .{ foo, bar, config1(foo, bar) });
            std.debug.print("config2({}, {}) = {}\n", .{ foo, bar, config2(foo, bar) });
            std.debug.print("config3({}, {}) = {}\n", .{ foo, bar, config3(foo, bar) });
        }
    }
}
#

const BarArray = std.enums.EnumArray(Bar, u8); would clean up the third one a bit, but nothing really beats the succinctness of the switches

lunar bloom
#

Definitely. In any case, those other options looks interesting to learn what's possible in other scenarios maybe.

#

Prob config1 calls are always inlined?

acoustic hull
#

llvm will have no trouble optimizing it to a lut, if that's what you mean

lunar bloom
#

lut = lookup table?

acoustic hull
#

yes