Local static class fields

Simen Kjærås simen.kjaras at gmail.com
Tue Aug 13 12:22:45 UTC 2019


On Tuesday, 13 August 2019 at 08:41:02 UTC, Bert wrote:
> On Tuesday, 13 August 2019 at 04:43:29 UTC, Paul Backus wrote:
>> It seems to me like the obvious solution is to use two 
>> different classes, one to store the global state, and one to 
>> store the individual objects in your structure. For example:
>>
>> class Tree {
>>     State state;
>>     Node root;
>> }
>>
>> class Node {
>>     Node[] children;
>> }
>
> Yes, I forgot to mention this as a solution.
>
> I was going to go in to more detail. This is not a great 
> solution because it requires a base class simply to hold the 
> "global" state and it requires nodes to have an accessor(they 
> won't know which tree they come from).

What you're essentially looking at here is my solution 4):

class Node {
     Node parent;
     Node[] children;
     @property State state() {
         return parent.state;
     }
}

class Root : Node {
     State treeState;
     override @property State state() {
         return treeState;
     }
}

class State {}



> What I was thinking though, with D's capabilities, if a 
> constructor could somehow return a "Voldemort" type that makes 
> it all work out.
>
> e.g.,
>
> class Tree {
>     State state;
>     Node root;
>     alias root this;
> }
>
> then the constructor just returns tree which acts as an node.
>
> It doesn't have to be a voldemort but it at least hides some of 
> the details...
>
> Ultimately it is not too different from using a dictionary 
> though, effectively does the same thing.

Something like this may be possible, but it will still need to 
keep around a pointer to the context (likely the root), costing 
you the exact same as having a pointer to either the root or the 
state in each node:

class Root {
     State state;
     this() { state = new State(); }
     auto newNode() {
         class Node {
             State getState() { return state; }
         }
         return new Node();
     }
}

class State { }

unittest {
     auto root1 = new Root();
     auto node = root1.newNode();

     assert(node.getState() == root1.state);
     // Note that Node keeps track of its context, thus taking up 
more space:
     assert(__traits(classInstanceSize, typeof(node)) > 
__traits(classInstanceSize, State));
}


> There may be no way around such a problem in that these might 
> be "optimal". Your's, which added parent in node, might be 
> better since a dictionary doesn't have to be directly 
> maintained.

Also, if space is a concern, the dictionary is actually worse 
than adding a pointer to the state in each node, as the internal 
book-keeping of the dictionary is more than one pointer's worth 
per entry (at the very least it needs to keep both the hash of 
the Node and the pointer to the state).


> I'd like to get away from actually having a different "root" 
> type though. Mainly because it reduces uniformity and 
> complicates the design I already have. If I could hide all 
> these things and it is not too complicated then it would work 
> better for me.

The solution of having a Root class that derives from Node causes 
complications in three situations: When you create a new tree, 
when you move a subtree to become a new tree, and when you move a 
tree to become a subtree. The last two may or may not actually 
occur in your code, but without knowing more, I have no idea if 
that's the case. I'm fairly certain you will at some point be 
creating new trees, though. If the creation of new trees is 
confined to one or two functions while the creation of new nodes 
is widespread, this special-casing of roots should be easily 
manageable.

However, since you specifically mention in this post that Paul 
'added parent in node', you should definitely go with a pointer 
to the state in the Node, as per my earlier post. It's faster, 
you pay the memory cost anyway, and it's easy for maintainers 
(that's you in 6 months cursing your young-and-careless self) to 
understand what's going on.

Actually, there's one complication with having a pointer to the 
state in each node that we haven't mentioned: is the state often 
replaced wholesale? That is, not just its contents changed, but 
can the state of a tree suddenly point to a wholly different 
instance of the same class? If each node holds a pointer to it, 
then this operation is costly. However, this is generally easy to 
fix/avoid by a) favor mutation over reassignment, or b) another 
layer of indirection.

--
   Simen


More information about the Digitalmars-d-learn mailing list