I am trying to convert the code below to ZIg. I have left C-style casts in the code to illustrate what I am trying to achieve.
SInce I am not using standard types, but rather Zig “typedefs” declared at the top of the file, I can’t get my head around how to cast from one user defined type directly to another user defined type in a simple way. Any recommendations on how to structure this?
const Real_t = f32; // global datatype definitions for large source file
const Index_t = usize;
pub fn main() !void
{
// ...
// lots of stuff above this line
var tz: Real_t = ZERO;
var plane: Index_t = 0;
while ( plane < edgeNodes ) : ( plane += 1 ) {
var ty: Real_t = ZERO;
var row: Index_t = 0;
while ( row < edgeNodes ) : ( row += 1 ) {
var tx: Real_t = ZERO;
var col: Index_t = 0;
while ( col < edgeNodes ) : ( col += 1 ) {
// computational stuff happens in here on (tx, ty, tz) tuples
tx = 1.125*(Real_t)(col+1)/(Real_t)(edgeElems);
}
ty = 1.125*(Real_t)(row+1)/(Real_t)(edgeElems);
}
tz = 1.125*(Real_t)(plane+1)/(Real_t)(edgeElems);
}
// lots of stuff below this line
// ...
}
Check if @floatFromInt() works in the places where you’re casting to Real_t. E.g.: 1.125*@floatFromInt(col+1)/@floatFromInt(edgeElems)
In general, mixing types in expressions can get unreadable quickly because of the ‘@-noise’, sometimes it helps moving the casts out of the expression and into a separate assignment or into a small helper function.
PS: in general, check the casting builtins @floatFromInt, @intFromFloat, @floatCast and @intCast.
The problem is that I don’t know what the source and destinations types will be, a priori.
In my example above, it is straightforward. On the other hand, users could potentially create “typedefs” in one line at the top of a Zig file that someone could plug many different types into (int, float, u8, bool) just to trade off compiler performance for the different cases. The C-style cast handles this case extremely efficiently and unambiguously in terms of source code representation.
I don’t see a “clean” way to handle user-defined type casting in Zig unless it is very ugly, and requires a rewrite of lots of source code with any type change. I can accept that, but being new to the language, I am looking for ways to structure/encapsulate my source code with the least possible pain.
Creating small helper functions might indeed be the best solution. I am just gathering input to make sure.
Hello,
since you can use types in Zig as first class citizens, it should be possible to do it cleanly in a generic way.
Can you share more of the code, or tell us what kind of types is it supposed to support? Just so we know what we are dealing with, current description is a bit too apaque.
Thank you! I am using helper functions for now as @floooh suggested, because they are very clean for my use case.
My plan is to put a first cut of my 3000 line science app on GitHub on July 4 along with a showcase post on Ziggit.dev . Hopefully, people will make suggestions in that post how the first cut should be improved, and I will do my best to make some improvements.
Even better, people should feel free to submit pull requests highlighting their particular slant on how the ZIg language should be applied to achieve the best balance of Zig language features and performance.
std.math does a lot of type genericism at function level
Ziglings extracted usefull information about the language overall into small excersises (target are newcomers)
This shows ways you can do type generic code that for example accepts any float / signed / unsigned numerical type and safely works on that, if you try to do something that is not allowed for a type, you get mostly nice compiler error.
This is one of the harder parts of the Zig, especially if you want to accept really different kinds of types, like pointers, structs, enums, arrays, etc. and have specific code paths for them. (if that interests you look at @typeInfo() and std.builtin.Type)