Next: , Previous: Files, Up: Programming


5.7 Structures

Users may also define their own data types as structures, along with user-defined operators, much as in C++. By default, structure members are public (may be read and modified anywhere in the code), but may be optionally declared restricted (readable anywhere but writeable only inside the structure where they are defined) or private (readable and writable only inside the structure). The virtual structure this refers to the enclosing structure. Any code at the top-level scope within the structure is executed on initialization. The implicit initializer for a structure S is new S (which creates a new anonymous instance of S). A null instance of a structure results on assigning it the value null.

This implicit initializer can be overwritten with the operator init() to allow for a custom initialization of each instance of S:

S operator init() {
  S s=new S;
  return s;
}

Here is a simple example that illustrates the use of structures:

struct S {
  real a=1;
  real f(real a) {return a+this.a;}
}

S s;                            // Initializes s with new S;

write(s.f(2));                  // Outputs 3

S operator + (S s1, S s2)
{
  S result;
  result.a=s1.a+s2.a;
  return result;
}

write((s+s).f(0));              // Outputs 2

In the following example, the static function T.T(real x) is a constructor that initializes and returns a new instance of T:

struct T {
  real x;
  static T T(real x) {T t=new T; t.x=x; return t;}
}

T a;
T b=T.T(1);

write(a.x);                     // Outputs 0
write(b.x);                     // Outputs 1
The name of the constructor need not be identical to the name of the structure; for example, see triangle.SAS in geometry.asy.

Structure assignment does a shallow copy; a deep copy requires writing an explicit copy() member. The function bool alias(T,T) checks to see if two instances of the structure T are identical. The boolean operators == and != are by default equivalent to alias and !alias respectively, but may be overwritten for a particular type do a deep comparison.

When a is defined both as a variable and a type, the qualified name a.b refers to the variable instead of the type.

Much like in C++, casting (see Casts) provides for an elegant implementation of structure inheritance, including virtual functions:

struct parent {
  real x=1;
  void virtual(int) {write (0);}
  void f() {virtual(1);}
}

void write(parent p) {write(p.x);}
  
struct child {
  parent parent;
  real y=2;
  void virtual(int x) {write (x);}
  parent.virtual=virtual;
  void f()=parent.f;
}

parent operator cast(child child) {return child.parent;}
  
parent p;
child c;

write(c);                       // Outputs 1;

p.f();                          // Outputs 0;
c.f();                          // Outputs 1;

write(c.parent.x);              // Outputs 1;
write(c.y);                     // Outputs 2;

Further examples of structures are Legend and picture in the Asymptote base module plain.