Zig’s recent (and sudden) removal of usingnamespace completely destroyed my nD vector types library…
The basic setup so far was to have a single comptime type generator which accepts a vector size and component value type, and then depending on these params conditionally includes various features/functions in the returned vector type. This was elegantly solved by splitting out different aspects and (sometimes nested) conditions into functions returning groups of functionality as structs and then merging them all in the returned main type, like so (in principle):
pub fn Vec(comptime N: u32, comptime T: type) type {
return struct {
pub usingnamespace Vec2Swizzles(N, T);
pub usingnamespace Vec3Swizzles(N, T);
pub usingnamespace Vec4Swizzles(N, T);
pub usingnamespace VecSizeSpecific(N, T);
pub usingnamespace VecTypeSpecific(N, T);
// ...
};
}
// swizzle functions for 3D, 4D and higher-dim vectors only
pub fn Vec3Swizzles(comptime N: u32, comptime T: type) type {
const V = @Vector(N, T);
const V2 = @Vector(2, T);
const V3 = @Vector(3, T);
const V4 = @Vector(4, T);
return if (N < 3) struct {} else struct {
pub fn z(a: V) T {
return a[2];
}
pub fn xz(a: V) V2 {
return [_]T{ a[0], a[2] };
}
pub fn yz(a: V) V2 {
return [_]T{ a[1], a[2] };
}
pub fn zx(a: V) V2 {
return [_]T{ a[2], a[0] };
}
pub fn zy(a: V) V2 {
return [_]T{ a[2], a[1] };
}
pub fn zz(a: V) V2 {
return [_]T{ a[2], a[2] };
}
pub fn xxz(a: V) V3 {
return [_]T{ a[0], a[0], a[2] };
}
// plus hundreds of similar functions...
};
}
Now I must find a way to rewrite and expose hundreds of swizzle functions (many of them conditional & dependent on different vector sizes) as well as other size-specific and value type-specific functionality, all whilst trying to keep the existing API intact and not to pollute or complicate it with additional sub-namespaces, forced by the removal of this syntax. This readme gives an overview of supported operations for different sizes & component types.
The only way I can see (so far) involves a great amount of code duplication and/or avoidance of comptime type generation. Even though I agree with some of the reasons & explanations given in the release notes, they’re feeling somewhat handpicked and maybe are not considering many other use cases for which this struct-merging feature was actually very helpful…
usingnamespace was such a great tool to split up defintions of complex types over several functions/files and then merge the returned structs via a single comptime wrapper into a single exposed type… Maybe I’m just not privy to better approaches, but right now I feel a great language feature has been lost and this decision might have simplified some Zig internals, but is pushing complexity into userland code bases.
I’d very much appreciate any ideas/approaches how to update/rework/redesign this current architecture whilst keeping duplication and API breakage to a minimum. For example, in this case I’d find it unacceptable having to introduce sub-namespaces, which would then turn callsites from vec3.zyx(a) into vec3.swizzle.zyx(a) (for example). Also sub-namespace do not work for conditional functionality which only exists for certain value types (e.g. integer vectors) or certain sizes (e.g. the cross product is only defined in 3D)…