MG Mud User | 88f1247 | 2016-06-24 23:31:02 +0200 | [diff] [blame^] | 1 | CONCEPT |
| 2 | structs |
| 3 | |
| 4 | INTRODUCTION |
| 5 | structs are, next to arrays and mappings, a way to group a |
| 6 | collection of value together. |
| 7 | |
| 8 | A struct holds a fixed number of values, called 'members', and |
| 9 | allows to access them by their given name. The name is resolved |
| 10 | when the LPC code is compiled, making struct member access as fast |
| 11 | as array member access. |
| 12 | |
| 13 | structs are passed by reference. |
| 14 | |
| 15 | |
| 16 | DEFINITION |
| 17 | A new struct type has to be defined at the top level of an |
| 18 | object. For example |
| 19 | |
| 20 | struct Foo { |
| 21 | int one, *two; |
| 22 | struct Bar three; |
| 23 | }; |
| 24 | |
| 25 | defines the new struct 'Foo' with three members: integer 'one', |
| 26 | integer array 'two', and struct Bar 'three' |
| 27 | |
| 28 | It is possible to 'inherit' structs from each other. Given above |
| 29 | definition of struct Foo, the following definition |
| 30 | |
| 31 | struct Quux (Foo) { |
| 32 | int four; |
| 33 | }; |
| 34 | |
| 35 | is equivalent to the definition |
| 36 | |
| 37 | struct Quux { |
| 38 | int one, *two; |
| 39 | struct Bar three; |
| 40 | int four; |
| 41 | }; |
| 42 | |
| 43 | |
| 44 | The usual visibility modifiers apply, e.g. |
| 45 | |
| 46 | protected struct Bang {...}; |
| 47 | |
| 48 | |
| 49 | struct definitions are promoted through inheritance like functions, |
| 50 | with the difference that all structs live in the same flat namespace. |
| 51 | This means: a struct defined in a program is visible in _all_ |
| 52 | inherited programs, regardless of how deep the inheritance is |
| 53 | nested. This also means that in one program there must not be |
| 54 | two structs, inherited or not, with the same name. |
| 55 | |
| 56 | |
| 57 | To declare a struct without defining it, write: |
| 58 | |
| 59 | struct Quux; |
| 60 | |
| 61 | This notation is useful if you have two structs referencing |
| 62 | each other: |
| 63 | |
| 64 | struct Quux; |
| 65 | |
| 66 | struct Bar { |
| 67 | struct Quux quux; |
| 68 | }; |
| 69 | struct Quux { |
| 70 | struct Bar bar; |
| 71 | }; |
| 72 | |
| 73 | |
| 74 | USAGE |
| 75 | To use a struct, its definition must be visible - either because it |
| 76 | is defined in the object compiled, or it has been inherited. |
| 77 | (Note: #include'ing structs does not what you think it does: in |
| 78 | LPC it constructs a new struct type whereever it is included). |
| 79 | |
| 80 | |
| 81 | A variable to hold a struct is defined like this: |
| 82 | |
| 83 | struct Foo var; |
| 84 | |
| 85 | and similar for function arguments: |
| 86 | |
| 87 | void fun (struct Foo arg) |
| 88 | |
| 89 | |
| 90 | Just writing 'struct Foo var' however does not _create_ a struct, |
| 91 | it just creates a variable capable of holding one. To assign a value |
| 92 | to the variable upon creation, assign it with a struct value, either |
| 93 | from another variable or from a literal struct: |
| 94 | |
| 95 | struct Foo var = (<Foo>); |
| 96 | |
| 97 | |
| 98 | Literal structs are written using (<>) as delimiters: |
| 99 | |
| 100 | (<Foo>) |
| 101 | creates an empty instance of struct Foo |
| 102 | |
| 103 | (<Foo> 1, ({ 2 }), bar) |
| 104 | creates an instance of struct Foo, and assigns 1 to member |
| 105 | 'one', ({ 2 }) to member 'two', and the content of variable |
| 106 | bar to member 'three'. |
| 107 | |
| 108 | (<Foo> two: ({ 2 }) ) |
| 109 | creates an instance of struct Foo which is all empty except |
| 110 | for member 'two' which is assigned the value ({ 2 }). |
| 111 | |
| 112 | It is not possible to use both named and unnamed initializers |
| 113 | in the same literal. |
| 114 | |
| 115 | |
| 116 | A struct member is accessed using the -> operator: |
| 117 | |
| 118 | struct Foo var = ...; |
| 119 | |
| 120 | var->one = 1; |
| 121 | |
| 122 | |
| 123 | It is possible to compute struct lookups at runtime: |
| 124 | |
| 125 | struct Foo bar = ...; |
| 126 | string member = "one"; |
| 127 | |
| 128 | bar->(member) = 1; // sets bar->one to 1 |
| 129 | bar->(0) = 1; // sets bar->one to 1 |
| 130 | |
| 131 | |
| 132 | When using struct values held in variables/expressions of type |
| 133 | 'mixed', the 'mixed' value should to be casted to the struct |
| 134 | value. The cast can be omitted if the looked-up member exists |
| 135 | in only one struct (and its children) known to the compiler: |
| 136 | |
| 137 | struct Foo { int one; }; |
| 138 | struct Bar { int two; }; |
| 139 | struct Baz { int two; }; |
| 140 | mixed var; |
| 141 | |
| 142 | var->one // looks up Foo->one |
| 143 | (struct Foo)var->one // looks up Foo->one |
| 144 | var->two // ERROR: ambiguous lookup |
| 145 | (struct Bar)var->one // looks up Bar->one |
| 146 | |
| 147 | |
| 148 | USAGE IN CLOSURES |
| 149 | The #'(< operator can be used in lambda closures to create a |
| 150 | struct; the type of the struct is given by the 'template' |
| 151 | struct passed as first argument. The content of the template |
| 152 | struct is irrelevant, so an empty struct suffices. For |
| 153 | example, to create an instance of struct Foo: |
| 154 | |
| 155 | ({ #'(<, (<Foo>), 1, ({ 2 }), (<Bar>) }) |
| 156 | |
| 157 | The order of the member values is the order in which they |
| 158 | appear in the struct definition. |
| 159 | |
| 160 | To access a struct member in a lambda closure, use the #'-> |
| 161 | operator with the name of the member as double-quoted symbol |
| 162 | or literal string: |
| 163 | |
| 164 | ({ #'->, struct-expression, ''one }) |
| 165 | ({ #'->, struct-expression, "one" }) |
| 166 | |
| 167 | |
| 168 | MISCELLANEOUS |
| 169 | Internally structs can be identified by the ID string |
| 170 | returned from get_type_info(). This string contains the name |
| 171 | of the struct, the name of the program its type was defined in, |
| 172 | and the ID number of the program. However, do not rely on |
| 173 | a particular format of this string! |
| 174 | |
| 175 | Support for structs is signaled by the macro __LPC_STRUCTS__. |
| 176 | |
| 177 | Since structs are tied to the program they are defined in, |
| 178 | re-compiling a program creates new struct types which are |
| 179 | in principle incompatible to the old ones. However, the LPC |
| 180 | compiler checks if the newly compiled structs have the same |
| 181 | structure as their older counterparts of the same name |
| 182 | (and defining program). If the structures conform, the existing |
| 183 | older struct types are used instead of the new ones. This way |
| 184 | an accidental of for example /std/types.c doesn't break |
| 185 | the whole mud. |
| 186 | |
| 187 | |
| 188 | EXAMPLES |
| 189 | Suppose we have two objects: a monster, and a monster |
| 190 | coordinate tracker, and we want to use a struct to store the |
| 191 | coordinate: |
| 192 | |
| 193 | -- monster_coordinate.c -- |
| 194 | struct Coordinate { int x; int y; }; |
| 195 | |
| 196 | -- monster_tracker.c -- |
| 197 | inherit "monster_coordinate"; |
| 198 | |
| 199 | void track (struct Coordinate coord) { ... } |
| 200 | |
| 201 | -- monster.c -- |
| 202 | inherit "monster_coordinate"; |
| 203 | |
| 204 | int move (..) { |
| 205 | ... |
| 206 | "monster_tracker"->track( (<Coordinate> my_x, my_y) ); |
| 207 | } |
| 208 | |
| 209 | Note that using '#include "monster_coordinate.c"' instead of inherit |
| 210 | won't work. While the objects would compile, the first call to |
| 211 | track() would cause a runtime error of the type |
| 212 | |
| 213 | Illegal type to struct->(): struct Coordinate (/monster.c #234), |
| 214 | expected struct Coordinate |
| 215 | (/monster_tracker.c #552) |
| 216 | |
| 217 | |
| 218 | HISTORY |
| 219 | structs were fully implemented first in LDMud 3.3.246. |
| 220 | The implementation was revised in LDMud 3.3.344. |
| 221 | The reactivation of unchanged structs in object updates was |
| 222 | implemented in LDMud 3.3.417. |
| 223 | |
| 224 | |
| 225 | SEE ALSO |
| 226 | mappings(LPC), get_type_info(E), structp(E), to_mapping(E), |
| 227 | to_struct(E), struct_info(E), baseof(E) |