On Tue Mar 12 17:03:58 2024 +0000, Zebediah Figura wrote:
Why make it a pointer?
When I have "complex" structs, that have members via pointers, I have the predisposition to declare them as pointers instead of values.
I tried to rationalize some reasons for it:
- copy-by-value shouldn't be used on them (lest we want "cojoined twins" copies), having them as pointers ensures that I am willingly doing a copy-by-reference or that, if I want a deep copy, I have implemented a function that copies members recursively. - If I ever put values of these structs in an array, I will probably want to make it an array of pointers to these values instead of storing them directly. Two reasons for this: - The cost of moving a pointer is less than the cost of moving a struct that also contains pointers. - If it is a dynamic array, I will have to move them around, breaking external references to these elements if I don't have this extra level of indirection. - Similar to the last one, working with pointers to these values and always allocating these in heap ensures that they will preserve the same position in memory during their whole lifetime, ensuring that external references to it remain valid even if they are passed around. - It feels nice to have a "free" or "cleanup" function that also frees the whole object instead of leaving it with dangling pointers, in case we neglect setting them to NULL. - If makes clear that the owner of the members (and who is responsible their memory) is the object itself and not the object that contains it:
Given ``` struct bar { struct apple *a; struct banana *b; }; ``` consider ``` struct foo { struct bar smol; // Not clear whether "smol" or "big" is the owner of "a" and "b". } big; ``` versus ``` struct foo { // it is clear that only "smol" is the owner of "a" and "b" and it should free their memory. struct bar *smol; } big; ``` Which I think also discourages a little the practice of implicitly transferring ownership.
Even if there are cases where applying this heuristic is not justified, I feel it makes the code more robust for changes that may appear in the future.
Of course, I am willing to revert it when the reviewers don't like it! :)
---
Now I realize that if I were consistent with this rule, I should also have applied this rule to `struct hlsl_state_block_entry` inside `struct hlsl_state_block`, because it contains the `*lhs_name` and `*rhs_instr` pointers, so it is also a "complex" struct. Which may actually make sense if we reuse the struct for technique passes and make it more complex.