forward

tape
import forward from "forward";

struct Engine { sent: i64; }
fn Engine.send(*self, n: i64) { self.sent = self.sent + n; }
fn Engine.total(*const self) -> i64 { return self.sent; }

struct Client { engine: Engine; name: string; }
@tape { forward.gen_forward(Client, "engine"); }

var c: Client = Client { engine: Engine { sent: 0; }; name: "a"; };
c.send(3);
let t = c.total();          // 3: Client.total calls c.engine.total()

forward writes forwarding methods for you. A type that holds another in a field, such as a facade over an engine or a wrapper that adds a field or two, gets one method for each method of the inner type that takes a receiver, with the same name and parameters, calling it on the field. When the inner type gains a method, the wrapper has it too.

gen_forward

tape
pub @tape fn gen_forward(T: type, field: []const u8, names: []const []const u8 = [],
                         visible: bool = false)

For each method m of the type of T’s field field:

  • m(*self, ...) becomes fn T.m(*self, ...) calling self.field.m(...);
  • m(self, ...) and m(*const self, ...) become fn T.m(*const self, ...);
  • the result is m’s: nothing, a value, or T or E.

A method without a receiver (fn Engine.new()) is not forwarded, and neither is one T already has: write a method yourself and it wins. names, when not empty, chooses which methods are forwarded:

tape
@tape { forward.gen_forward(Client, "engine", ["send"]); }   // only send

The methods are private to your module; visible: true makes them pub, for a module that exports the wrapper (its types must be pub then too).

It reads the inner type’s methods with @typeof(T).methods (reflection), so it is also an example of a generator that writes one declaration per member of a type.

Last modified: