Comptime initialization of a struct hierarchy

a somewhat contrived example in which both ModA and ModB expose an incH() method intended to be used at comptime only…

const std = @import("std");

const ModA_M = struct {
    const Self = @This();
    ModB: ModB_M = undefined,
    ctr: u8 = 0,
    pub fn incH(comptime self: *Self, comptime v: u8) void {
        comptime self.ctr += v;
        comptime self.ModB.incH(1);
    }
    pub fn get(self: Self) u8 {
        return self.ctr;
    }
};

const ModB_M = struct {
    const Self = @This();
    ctr: u8 = 0,
    pub fn incH(comptime self: *Self, comptime v: u8) void {
        comptime self.ctr += v;
    }
    pub fn get(self: Self) u8 {
        return self.ctr;
    }
};

pub fn main() void {
    comptime var ModA = ModA_M{};
    const ModB = ModB_M{};
    comptime {
        ModA.ModB = ModB;
        ModA.incH(5);
        ModA.incH(3);
    }
    std.log.debug("\nModA = {d}, ModB = {d}\n", .{ ModA.get(), ModB.get() });
}

when run, the value from ModA correctly prints as 8; but the value from ModB is 0 (and not 1, as i would have expected)…

in my use-case, i “know” the struct hierarchy; and i can even wire-up each ModX with its dependencies…

is this sort of comptime initialization of a struct hierarchy possible???

This is making a copy:

ModA.ModB = ModB;

If you print ModA.ModB.get() you should get the correct value.

ModB itself is never modified, and can’t even be modified, since it’s const.

sorry @IntegratedQuantum – my bad… for sure, this works… but here’s what i’m really trying to achieve…

suppose i had multiple modules that want to share a common ModB… is there a way to make ModA.ModB a reference to the ModB instance created in main???

everything i tried failed…

I had a solution but then I saw you wanted to share a common ModB in your follow up

You need to make ModB a pointer:

const std = @import("std");

const ModA_M = struct {
    const Self = @This();
    ModB: *ModB_M = undefined,
    ctr: u8 = 0,
    pub fn incH(comptime self: *Self, comptime v: u8) void {
        comptime self.ctr += v;
        comptime self.ModB.incH(1);
    }
    pub fn get(self: Self) u8 {
        return self.ctr;
    }
};

const ModB_M = struct {
    const Self = @This();
    ctr: u8 = 0,
    pub fn incH(comptime self: *Self, comptime v: u8) void {
        comptime self.ctr += v;
    }
    pub fn get(self: Self) u8 {
        return self.ctr;
    }
};

pub fn main() void {
    comptime var ModA = ModA_M{};
    comptime var ModB = ModB_M{};
    comptime {
        ModA.ModB = &ModB;
        ModA.incH(5);
        ModA.incH(3);
        @compileLog(ModA.get(), ModB.get());
    }
}
test.zig:34:9: error: found compile log statement
        @compileLog(ModA.get(), ModB.get());
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Compile Log Output:
@as(u8, 8), @as(u8, 2)

Any struct containing a comptime pointer can only be used in a pure comptime context. You cannot intermix it with runtime code. That’s why I had to replace the call to std.debug.print() with @compileLog(). Even then it’s necessary to keep it inside the comptime block.

In order to anything useful–that is, have this comptime data structure affect your runtime code in some way–you’d have to generate a entirely separate tree that mirrors this one but which has no comptime pointers.

1 Like

thanks, @chung-leong … i know you’ve posted on similar idioms with comptime

this is a key point, in that many of the comptime var examples would “freeze” the results into some runtime variable… i’ve seen initializers implemented as comptime blocks, for instance…

i’m thinking out loud here, but perhaps there is a way to have the “comptime-only” state declared as an inner-struct within my ModX… taking this one step further, imagine if there were a finalizeComptime defined by each ModX that would indicate that the receiving struct should “capture” any comptime state such that it can truly be usable at runtime…

any ideas on how to best express this???

A way to freeze a comptime variable would certainly be very useful given the restrictions in place currently. I don’t know if these will be there forever though. Once incremental compilation has been implemented, I think we can start thinking about restoring the sort of comptime capability that we had with 0.11.0.