| CONCEPT |
| structs |
| |
| INTRODUCTION |
| structs are, next to arrays and mappings, a way to group a |
| collection of value together. |
| |
| A struct holds a fixed number of values, called 'members', and |
| allows to access them by their given name. The name is resolved |
| when the LPC code is compiled, making struct member access as fast |
| as array member access. |
| |
| structs are passed by reference. |
| |
| |
| DEFINITION |
| A new struct type has to be defined at the top level of an |
| object. For example |
| |
| struct Foo { |
| int one, *two; |
| struct Bar three; |
| }; |
| |
| defines the new struct 'Foo' with three members: integer 'one', |
| integer array 'two', and struct Bar 'three' |
| |
| It is possible to 'inherit' structs from each other. Given above |
| definition of struct Foo, the following definition |
| |
| struct Quux (Foo) { |
| int four; |
| }; |
| |
| is equivalent to the definition |
| |
| struct Quux { |
| int one, *two; |
| struct Bar three; |
| int four; |
| }; |
| |
| |
| The usual visibility modifiers apply, e.g. |
| |
| protected struct Bang {...}; |
| |
| |
| struct definitions are promoted through inheritance like functions, |
| with the difference that all structs live in the same flat namespace. |
| This means: a struct defined in a program is visible in _all_ |
| inherited programs, regardless of how deep the inheritance is |
| nested. This also means that in one program there must not be |
| two structs, inherited or not, with the same name. |
| |
| |
| To declare a struct without defining it, write: |
| |
| struct Quux; |
| |
| This notation is useful if you have two structs referencing |
| each other: |
| |
| struct Quux; |
| |
| struct Bar { |
| struct Quux quux; |
| }; |
| struct Quux { |
| struct Bar bar; |
| }; |
| |
| |
| USAGE |
| To use a struct, its definition must be visible - either because it |
| is defined in the object compiled, or it has been inherited. |
| (Note: #include'ing structs does not what you think it does: in |
| LPC it constructs a new struct type whereever it is included). |
| |
| |
| A variable to hold a struct is defined like this: |
| |
| struct Foo var; |
| |
| and similar for function arguments: |
| |
| void fun (struct Foo arg) |
| |
| |
| Just writing 'struct Foo var' however does not _create_ a struct, |
| it just creates a variable capable of holding one. To assign a value |
| to the variable upon creation, assign it with a struct value, either |
| from another variable or from a literal struct: |
| |
| struct Foo var = (<Foo>); |
| |
| |
| Literal structs are written using (<>) as delimiters: |
| |
| (<Foo>) |
| creates an empty instance of struct Foo |
| |
| (<Foo> 1, ({ 2 }), bar) |
| creates an instance of struct Foo, and assigns 1 to member |
| 'one', ({ 2 }) to member 'two', and the content of variable |
| bar to member 'three'. |
| |
| (<Foo> two: ({ 2 }) ) |
| creates an instance of struct Foo which is all empty except |
| for member 'two' which is assigned the value ({ 2 }). |
| |
| It is not possible to use both named and unnamed initializers |
| in the same literal. |
| |
| |
| A struct member is accessed using the -> operator: |
| |
| struct Foo var = ...; |
| |
| var->one = 1; |
| |
| |
| It is possible to compute struct lookups at runtime: |
| |
| struct Foo bar = ...; |
| string member = "one"; |
| |
| bar->(member) = 1; // sets bar->one to 1 |
| bar->(0) = 1; // sets bar->one to 1 |
| |
| |
| When using struct values held in variables/expressions of type |
| 'mixed', the 'mixed' value should to be casted to the struct |
| value. The cast can be omitted if the looked-up member exists |
| in only one struct (and its children) known to the compiler: |
| |
| struct Foo { int one; }; |
| struct Bar { int two; }; |
| struct Baz { int two; }; |
| mixed var; |
| |
| var->one // looks up Foo->one |
| (struct Foo)var->one // looks up Foo->one |
| var->two // ERROR: ambiguous lookup |
| (struct Bar)var->one // looks up Bar->one |
| |
| |
| USAGE IN CLOSURES |
| The #'(< operator can be used in lambda closures to create a |
| struct; the type of the struct is given by the 'template' |
| struct passed as first argument. The content of the template |
| struct is irrelevant, so an empty struct suffices. For |
| example, to create an instance of struct Foo: |
| |
| ({ #'(<, (<Foo>), 1, ({ 2 }), (<Bar>) }) |
| |
| The order of the member values is the order in which they |
| appear in the struct definition. |
| |
| To access a struct member in a lambda closure, use the #'-> |
| operator with the name of the member as double-quoted symbol |
| or literal string: |
| |
| ({ #'->, struct-expression, ''one }) |
| ({ #'->, struct-expression, "one" }) |
| |
| |
| MISCELLANEOUS |
| Internally structs can be identified by the ID string |
| returned from get_type_info(). This string contains the name |
| of the struct, the name of the program its type was defined in, |
| and the ID number of the program. However, do not rely on |
| a particular format of this string! |
| |
| Support for structs is signaled by the macro __LPC_STRUCTS__. |
| |
| Since structs are tied to the program they are defined in, |
| re-compiling a program creates new struct types which are |
| in principle incompatible to the old ones. However, the LPC |
| compiler checks if the newly compiled structs have the same |
| structure as their older counterparts of the same name |
| (and defining program). If the structures conform, the existing |
| older struct types are used instead of the new ones. This way |
| an accidental of for example /std/types.c doesn't break |
| the whole mud. |
| |
| |
| EXAMPLES |
| Suppose we have two objects: a monster, and a monster |
| coordinate tracker, and we want to use a struct to store the |
| coordinate: |
| |
| -- monster_coordinate.c -- |
| struct Coordinate { int x; int y; }; |
| |
| -- monster_tracker.c -- |
| inherit "monster_coordinate"; |
| |
| void track (struct Coordinate coord) { ... } |
| |
| -- monster.c -- |
| inherit "monster_coordinate"; |
| |
| int move (..) { |
| ... |
| "monster_tracker"->track( (<Coordinate> my_x, my_y) ); |
| } |
| |
| Note that using '#include "monster_coordinate.c"' instead of inherit |
| won't work. While the objects would compile, the first call to |
| track() would cause a runtime error of the type |
| |
| Illegal type to struct->(): struct Coordinate (/monster.c #234), |
| expected struct Coordinate |
| (/monster_tracker.c #552) |
| |
| |
| HISTORY |
| structs were fully implemented first in LDMud 3.3.246. |
| The implementation was revised in LDMud 3.3.344. |
| The reactivation of unchanged structs in object updates was |
| implemented in LDMud 3.3.417. |
| |
| |
| SEE ALSO |
| mappings(LPC), get_type_info(E), structp(E), to_mapping(E), |
| to_struct(E), struct_info(E), baseof(E) |