-
-
Notifications
You must be signed in to change notification settings - Fork 9
constructor
Constructors can be defined using the constructor
keyword.
import basics
import random
pragma ignore_unused
struct Person (name String, uid int) {
constructor(name String){
this.name = name
this.uid = randomInt(100)
}
}
func toString(person Person) String {
return person.name + " : id=" + person.uid
}
func main {
randomize()
john Person("John") // constructed (living on stack)
james *Person = new Person("John") // constructed (living on heap)
defer delete james
print("Constructed people:")
print(john)
print(*james)
// --------- vs ---------
uninitialized_guy Person = undef // undefined (living on stack)
uninitialized_heap_guy *Person = new undef Person // undefined (living on heap)
defer delete uninitialized_heap_guy
unconstructed_guy Person // zero-initialized (living on stack)
unconstructed_heap_guy *Person = new Person // zero-initialized (living on heap)
defer delete unconstructed_heap_guy
print("Unconstructed people")
print(unconstructed_guy)
print(*unconstructed_heap_guy)
print("Uninitialized people")
print("(cannot print these, since their names would be garbage memory)")
}
You can also define multiple constructors on the same type.
struct Thing (a, b, c String) {
constructor(value String){
this.a = value
this.b = value
this.c = value
}
constructor(int value){
this.a = toString(value)
this.b = toString(value + 1)
this.c = toString(value + 2)
}
constructor(a String = "a", b String = "b", c String = "c"){
this.a = a
this.b = b
this.c = c
}
}
The primary use case of constructors is to construct values on their creation. You can easily invoke constructors on value creation by using parentheses immediately after. For example:
import basics
struct Thing (value double, owner String) {
constructor(initial_value double){
this.value = initial_value
this.owner = "nobody"
}
}
func main {
one Thing(1.0)
two *Thing = new Thing(2.0)
defer {
two.__defer__() // destroy
delete two // delete
}
print(one.value)
print(two.value)
}
You can also call a constructor like any other method, using value.__constructor__(arg1, arg2, argN)
. However, you are responsible for making sure the memory of the value is zero-initialized before-hand, since constructors are allowed to assume zero-initialization.
import basics
struct MyInteger (value int) {
constructor(){
this.value = 12435
}
}
func main {
my_integer MyInteger
my_integer.__constructor__()
// which is equivalent to
/*
my_integer MyInteger()
*/
// -----------------------------
print(my_integer.value)
}
Calling __constructor__
on memory that is not zero-initialized is type-dependant. Types are NOT REQUIRED to be able to be constructed on non zero-initialized memory. If uncertain, zero-initialize the memory before manual construction using something like memset(memory, 0, size_in_bytes)
!
import basics
struct Person (name String, level int) {
constructor(name String){
this.name = name
this.level = 1
}
}
func main(){
// Example of manually constructing values from garbage memory
count usize = 100
raw_memory *Person = new undef Person * count // equivalent to `malloc(sizeof Person * count)`
repeat count {
person *Person = &raw_memory[idx]
memset(person, 0, sizeof Person)
person.__constructor__("Abby")
}
each Person in [raw_memory, count] {
printf("[%d] Hello %S\n", idx, it.name)
}
defer {
each Person in [raw_memory, count], it.__defer__() // destroy each person (necessary because it contains `String` and so is not POD (plain-old-data))
delete raw_memory // equivalent to `free(raw_memory)`
}
}
Constructors have an additional behavior when defined on classes. They are responsible for initializing the __vtable__
field used for dynamic dispatch.
This is why instances of classes must be constructed before using dynamic dispatch.
class MyClass () {
constructor {}
virtual func doTheThing {}
}
func main {
one MyClass()
one.doTheThing() // ok - `one` is constructed
two *MyClass = new MyClass()
defer delete two
two.doTheThing() // ok - the value pointed to by `two` is constructed
three MyClass
three.doTheThing() // this will crash!!! The value `three` must be constructed in order to use dynamic dispatch!!!
}