Reducing boilerplate for comptime anonymous array literal

I’m trying to simplify a set of chained if-elses

if( time < duration_1 ) {
  return computation( time, factor_1, offset_1 );
}
else if( time < duration_1 + duration_2 ) {
  return computation( time - duration_1, factor_2, offset_2 );
}
# ...
else {
  return final;
}

By using a macro-like function to turn a list of constraints into the above logic:

const Step = struct { duration: i32, factor: i32, offset: i32, ... };

fn Compute( time: i32, final: i32, steps: []const Step ) i32 {
  var base_time = time;

  for (steps) |step| {
    if( base_time < step.duration ) {
      return computation( base_time, step.duration, step.offset );
    }
    base_time -= step.duration;
  }

  return final;
}

fn run(...) {
   # ... 
   result = Compute( time, final, ([_]Step {
      Step.init( duration_1, factor_1, offset_1 ),
      Step.init( duration_2, factor_2, offset_2 ),
    })[0..] );
}

Which works, but the complexity of the ([_]Step { ... })[0..] makes me think I’m doing something wrong. I think it should be possible to do something like fmt since all I want is a comptime macro-like expansion. But I can’t figure out to tell the compiler that “steps” is a variable-length comptime-known list of Step structures.

I.e. simplify the appearance at time of use to something like

   result = Compute( time, final, .{
      Step.init( duration_1, factor_1, offset_1 ),
      Step.init( duration_2, factor_2, offset_2 ),
    });

fn Compute( time: i32, final: i32, steps: anytype) i32

Iterate through steps just like an array. Another option is to pass the length of steps as a comptime parameter:

fn Compute( time: i32, final: i32, comptime count: comptime_int, steps: [count]Steps) i32

Thanks, that did it.

fn Compute( time: i32, final: i32, steps: anytype ) i32 {
  var base_time = time;

  inline for ( steps ) |step| {
    # ...
  }

  return final;
}

I was also missing the “inline” on the “for”.

2 Likes