Looping on []MyType backed by []MySuperType

Hello,

In a painting software defining TabletPoint which contains .position (a vector field).
I have []TabletPoint.

I have a transform library which acts on vectors and doesn’t bother with TabletPoint. It’s not coupled.

I am writing transform.alignPoints([]@Vector(2, f32), other_params...) which only needs to act on the .position field by reference.
However, that would mean allocating that list of references.

I wonder if there is a way to go for accessing the .position field of my existing []TabletPoint… yet without having to import TabletPoint…

Even if I abstract it as an anytype or so, I would still enforce it has a field that is named .position I guess? That would feel half-way done…

Separating fields in a MultiArrayList would be a cumbersome solution, as the fields of the TabletPoint are all unique to each point.

I think there is not a standard way to do it. However you can take inspiration from Fortran arrays.

A “slice” in fortran is defined by base pointer, stride and length. (It’s a bit more complicated but that’s the gist of it.)

You could implement a similar structure + getters and initializers to make the function work with different strides.

(post deleted by author)

Something like this:

fn ObesePointer(comptime T: type) type {
	return struct {
	    const Obese = @This();
	    
		ptr: [*]u8,
		stride: usize, // in bytes
		len: usize,
		
		const empty: Obese = .{
			.ptr = undefined,
			.stride = undefined,
			.len = 0,
		};
		
		
		fn init(comptime Container: type, comptime accessor: []const u8, slice: []Container) Obese {
			if (slice.len == 0) return .empty;
			return .{
				.ptr = @ptrCast(&@field(slice[0], accessor)),
				.stride = @sizeOf(Container),
				.len = slice.len,
			};
			
		}
		
		fn getPtr(self: Obese, i: usize) *T {
			std.debug.assert(i <= self.len);
			return @alignCast(@ptrCast(self.ptr + self.stride*i));
		}
		
		fn get(self: Obese, i: usize) T {
			return self.getPtr(i).*;
		}
		
	};
}


const Big = struct {
	point: [2]u8,
	id: usize,
};

pub fn main() void {
	var slices: [4]Big = @splat(.{.point = .{1,5}, .id = 3});
	
	const points: ObesePointer([2]u8) = .init(Big, "point", &slices);
	
	for (0..points.len) |i| {
		std.debug.print("{any}\n", .{points.get(i)});
	}
}

const std = @import("std");

Thank you this is great!
I’ll tag you when I can show the result in action!