2.18. Class

In daScript class is an extension of structure designed to provide OOP capabilities. Class provides single parent inheritance, abstract and virtual methods, initializers, and finalizers.

Basic class declaration is similar to that of a structure, but with the class keyword:

class Foo
    x, y : int = 0
    def Foo                             // custom initializer
        Foo`set(self,1,1)
    def set(X,Y:int)                    // inline method
        x = X
        y = Y

Initializer is a function with the name matching that of a class. Class can have multiple initializer with different arguments:

class Foo
    ...
    def Foo(T:int)                      // custom initializer
        self->set(T,T)
    def Foo(X,Y:int)                    // custom initializer
        Foo`set(self,X,Y)

Finalizer can be defined explicitly as a void function named finalize:

class Foo
    ...
    def finalize                        // custom finalizer
        delFoo ++

There are no guarantees that finalizer is called implicitly (see Finalizers).

Derived classes need to override methods explicitly, using override keyword:

class Foo3D : Foo
    z : int = 13
    def Foo3D                           // overriding default initializer
        Foo`Foo(self)                   // call parents initializer explicitly
        z = 3
    def override set(X,Y:int)           // overriding method variable
        Foo`set(self,X,Y)               // calling generated method function directly
        z = 0

Class can define abstract methods using abstract keyword:

class FooAbstract
    def abstract set(X,Y:int) : void             // inline method

Abstract functions need to be fully qualified, including return type. Class member functions will be inferred in the same manner as regular functions.

Sealed functions can not be overridden. ‘sealed’ keyword is used to prevent overriding:

class Foo3D : Foo
    def sealed set(X,Y:int )    // subclasses of Foo3D can no longer override this method
        xyz = X + Y

Sealed classes can not be inherited from. ‘sealed’ keyword is used to prevent inheritance:

class sealed Foo3D : Foo        // Foo3D can no longer be inherited from
    ...

Pointer named self is available inside any class method.

Classes can be created via new operator:

var f = new Foo()

Local class variables are unsafe:

unsafe
    var f = Foo()       // unsafe

Class methods can be invoked using -> syntax:

f->set(1,2)

Specific version of the method can also be called explicitly:

Foo`set(*f,1,2)

2.18.1. Implementation details

Class initializers are generated by adding local self variable with construct syntax. Body of the method is prefixed via with self expression. Final expression is a return <- self:

def Foo ( X:int const; Y:int const ) : Foo
    var self:Foo <- [[Foo()]]
    with self
        Foo`Foo(self,X,Y)
    return <- self

Class methods and finalizers are generated by providing extra argument self. Body of the method is prefixed via with self expression:

def Foo3D`set ( var self:Foo3D; X:int const; Y:int const )
    with self
        Foo`set(self,X,Y)
        z = 0

Calling virtual method is implemented via invoke:

invoke(f3d.set,cast<Foo> f3d,1,2)

Every base class gets an __rtti poiunter, and __finalize function pointer. Additionally a function pointer is added for each member function:

class Foo
        __rtti : void? = typeinfo(rtti_classinfo type<Foo>)
        __finalize : function<(self:Foo):void> = @@_::Foo'__finalize
        x : int = 0
        y : int = 0
        set : function<(self:Foo;X:int const;Y:int const):void> = @@_::Foo`set

__rtti contains rtti::TypeInfo for the specific class instance. There is helper function in rtti module to access class_info safely:

def class_info ( cl ) : StructInfo const?

finalize pointer is invoked when finalizer is called for the class pointer. That way when delete is called on the base class pointer, correct version of the derived finalizer is called.