#variadic iterators

1 messages · Page 1 of 1 (latest)

outer lion
#

I need to iterate over a rect's tiles in different ways based on a passed render_dir variable. First thought was

fn SanRect2D_ViewIter(render_dir: bapi_types.EnumDir) type {
    return struct {
        const Self = @This();
        const InitRet = struct { tile_iter: Self, started_on_border: bool };
        const NextRet = ?struct { xyz: geometry.SanXYZ, clear_mask: bool };

        inner: ?geometry.SanRect2D,
        iter: u10,

        pub fn init(rect: geometry.SanRect2D, render_dir: bapi_types.EnumDir) InitRet {
            return switch (render_dir) {
                .North => ...,
                .South => ...,
                .East => ...,
                .West => ...,
                },
            };
        }

        pub fn next(self: Self) NextRet {
            var inner = &(self.inner orelse return null);
            std.debug.assert(inner.x1 <= inner.x2);
            std.debug.assert(inner.y1 <= inner.y2);

            return switch (render_dir) {
                .North => {...},
                .South => {...},
                .East => {...},
                .West => {...},
            };
        }
    };
}

...but render_dir is sadly not known at comptime.

#

And storing render_dir and checking it every next() call feels suboptimal.

tiny jacinth
#

are the iteration actions for the directions really that different that they need to be completely separated into a switch?

warm seal
#

you are trying to switch from a dynamic dispatch to a static dispatch for a runtime variable, you either make your runtime know at comptime at an upwards layer and allow zig to make copies of the structure and call the correct one or you keep it that way
It's not actually that slow, it's a simple enum/numeric check, you cpu is very good at these

but if you want the static dispatch one, at least 1 check has to happen in runtime and to be able to reuse it you will have to keep all next stacks within the stack call under a switch inline, ex:

switch (value) {
    inline else => |comptime_value| {
        // create the struct here, you can then make render_dir comptime
    }, 
} 

but again this only saves checks if you can run more than 1 next inside the switch block and because of the chain of need-to-be comptime values, you cant move the struct/comptime value outside of this loop (not including further stacking, stacking is fine)

tiny jacinth
#

i mean ig if you could optimize that anyway you'd still be checking the direction every call

outer lion
#

would be a nicer drawing but im on a trackpad

tiny jacinth
#

what plankton said makes sense to me

outer lion
outer lion
#

or something of that nature

warm seal
#

but thats slower than a simple int check if llvm doesnt do elision for the ptr

outer lion
warm seal
#

you can abuse anytype too and just do ducktyping

outer lion
#

also since youre here, why does it not like me

warm seal
#

which is similar to the static dispatch i mentioned but the compiler does the heavy lifting for you

#

but you would need different implementations with a common method

outer lion
#

var tile_iter: SanRect2D_ViewIter, const started_on_border: bool = SanRect2D_ViewIter.init(rect, render_dir);

warm seal
#

idk how destructuring works, i gave up using it, it largely does make the code more readable or better to write

outer lion
#

out parameter it is then

warm seal
#

you shouldn't have to type the variables for destructuring i think, but i dont get the error

#

doesnt your sanrecr2d etc take an arg before you can call init?

outer lion
warm seal
#

ah

outer lion
warm seal
#

yeah no clue

outer lion
#

out seems to work fine so ill just keep using that

#

tho it is annoying that destructure doesnt owrk

tiny jacinth
#

not structs

warm seal
#

oh idk why i kept reading that as a tuple

tiny jacinth
tiny jacinth
#

if you mean like what you pass to print functions for the format args then yes

outer lion
#

anyways thanks and also this feels like a weird restriction

tiny jacinth
# outer lion anyways thanks and also this feels like a weird restriction

i was thinking about that and my theory is that they allow it for indexable types because that way you can give the items names to be more explicit, but if it's a struct then the members already have names so taking them out of the struct like that would make it easier to be less explicit? idk

#

ask on the zulip if you're curious

heavy vortex
#

hey guys, quick question, when should I use pub fn myFunction(self: MyStruct) void { ... } instead of pub fn myFunction(self: *const MyStruct) void { ... }?

slate coral
#

when MyStruct is relatively small and you do not care about having stable reference to it, for say taking a reference of some child field

#

for context remember that *const MyStruct will be 8 bytes on most current systems. That's a lot of bytes if MyStruct contains smaller fields

outer lion
warm seal