Use parameter or @fieldParentPtr?

I have a nested structure like this in my chessprogram:

const Searcher = struct {
    nodes: [128]Node,
    hist: History,

    fn Search(self: *Searcher) void {
        self.hist.update(&self.nodes); // version 1
        self.hist.update_using_field_parent(); // version 2
    }
}

const History = struct {
    fn update(self: *History, nodes: []const Node) void {
        // do my thing using nodes as parameter
    }

    fn update_using_field_parent(self: *History) void {
        // do my thing using self.get_searcher_nodes()
    }

    fn owner(self: *History) *Searcher {
        return  @fieldParentPtr("hist", self);
    }

    fn get_searcher_nodes(self: *History) []const Node {
        return &self.owner().nodes;
    }
}

From the Searcher I call an update functon of History (and only there).
Because History is always embedded in Searcher I am contemplating accessing the nodes directly using the @fieldParentPtr trick.

  1. Is that “done”? Like ok, neat, readable :slight_smile:
  2. Any knowledge on if there is speed difference? I would guess not so much.

I would probably be asking myself whether there is even a good reason to have History separate like so. If it is indeed always embedded in the Searcher, and is a zero-size type, why wouldn’t I just put all of its methods inside of Searcher in the first place?

As for point 2, I would expect no significant speed difference, since History is an empty struct, and as such the offset between History and Searcher could easily in theory always be zero.

5 Likes

The separation is because in History some complicated math is executed and my Searcher is already quite big.
History is simplified in my example: History itself contains QuietHistory, CaptureHistory, ContinuationHistory, CorrectionHistory :slight_smile:

It is mainly to separate the history math code from the search code and let the searcher simply call hist.update()

Right, in that case it might a bit too simplified for me to give an educated opinion.

Maybe a few pointers, but do note that I’m kind of blind here:

Does it even make sense for History to be embedded in Searcher? How do you work with the multiple history types? Are they all embedded in Searcher as individual fields and used as-needed? Do any of the history types have any retained state of their own, or do they work exclusively with the state of the Searcher? Wouldn’t it make more sense to have the histories be just namespaces of functions that take []const Node or *const Searcher as a parameter?

2 Likes

No. Just arrays / tables with values.

1 Like