www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Immutable struct fields

reply bearophile <bearophileHUGS lycos.com> writes:
To answer the recent D.learn thread "How would I optimize this parser?", I have
tried to find a more efficient way to build the parse tree, so I have used
tagged structs, something like this:


enum NodeType { node, text, tag }

struct Node {
    /*immutable*/ NodeType type = NodeType.node;
    Node* parent;
}

struct TextNode {
    /*immutable*/ NodeType type = NodeType.text;
    Node* parent;
    string content;

    public nothrow this(string content) {
        this.content = content;
    }
}

struct TagNode {
    /*immutable*/ NodeType type = NodeType.tag;
    Node* parent;
    string name;
    Node*[] children;

    public nothrow this(string name) {
        this.name = name;
    }

    public nothrow void addChild(Node* newChild) {
        children ~= newChild;
        newChild.parent = cast(Node*)&this;
    }
}


Each struct instance contains a "type" tag that at runtime tells what kind of
node it is. This tag never changes in the life of a node, so it's better for it
to be immutable, to use the type system to avoid changing it by mistake.

But unfortunately it doesn't work, this is a reduced example:


struct Foo {
    immutable int x = 1;
}
struct Bar {
    immutable int x = 2;
}
static assert(Foo.sizeof == 4);
void main() {
    Foo f;
    assert(f.x == 1);
    assert((cast(Bar)f).x == 1);
}


It seems that immutable fields act like static const fields or enum fields,
they have no storage. This seems a little weird to me. Do you know a way to put
immutable storage in a struct instance?

Using this doesn't work:

this(int) { type = NodeType.tag; }

And a static this() doesn't seem to work. So in the program I have just used a
mutable type field, but it looks silly...

Bye,
bearophile
Nov 01 2010
parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 02.11.2010 0:24, bearophile wrote:
 To answer the recent D.learn thread "How would I optimize this parser?", I
have tried to find a more efficient way to build the parse tree, so I have used
tagged structs, something like this:


 enum NodeType { node, text, tag }

 struct Node {
      /*immutable*/ NodeType type = NodeType.node;
      Node* parent;
 }

 struct TextNode {
      /*immutable*/ NodeType type = NodeType.text;
      Node* parent;
      string content;

      public nothrow this(string content) {
          this.content = content;
      }
 }

 struct TagNode {
      /*immutable*/ NodeType type = NodeType.tag;
      Node* parent;
      string name;
      Node*[] children;

      public nothrow this(string name) {
          this.name = name;
      }

      public nothrow void addChild(Node* newChild) {
          children ~= newChild;
          newChild.parent = cast(Node*)&this;
      }
 }


 Each struct instance contains a "type" tag that at runtime tells what kind of
node it is. This tag never changes in the life of a node, so it's better for it
to be immutable, to use the type system to avoid changing it by mistake.

 But unfortunately it doesn't work, this is a reduced example:


 struct Foo {
      immutable int x = 1;
 }
 struct Bar {
      immutable int x = 2;
 }
 static assert(Foo.sizeof == 4);
 void main() {
      Foo f;
      assert(f.x == 1);
      assert((cast(Bar)f).x == 1);
 }


 It seems that immutable fields act like static const fields or enum fields,
they have no storage. This seems a little weird to me. Do you know a way to put
immutable storage in a struct instance?

 Using this doesn't work:

 this(int) { type = NodeType.tag; }

 And a static this() doesn't seem to work. So in the program I have just used a
mutable type field, but it looks silly...

 Bye,
 bearophile
Wierd indeed, best workaround I can come up with: struct Foo { immutable int x; private this(int val){ x = val; } public static Foo opCall(){ return Foo(1); } } struct Bar { immutable int x; private this(int val){ x = val; } public static Bar opCall(){ return Bar(2); } } static assert(Foo.sizeof == 4); void main() { Foo f = Foo(); assert(f.x == 1); assert((cast(Bar*)&f).x == 1); } works, and the fact that static opCall is deprecated(?) makes me wish it's a compiler bug. And you still could shoot yourself in the leg, but only explicitly (that is "Foo f = Foo(42)" ) and only in the same module. -- Dmitry Olshansky
Nov 01 2010