In daScript, classes are an extension of structures designed to provide OOP capabilities. Classes provides single parent inheritance, abstract and virtual methods, initializers, and finalizers.
The basic class declaration is similar to that of a structure, but with the
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
The initializer is a function with a name matching that of a class. Classes 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)
Finalizers can be defined explicitly as void functions named
class Foo ... def finalize // custom finalizer delFoo ++
There are no guarantees that a finalizer is called implicitly (see Finalizers).
Derived classes need to override methods explicitly, using the
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
Classes can define abstract methods using the
class FooAbstract def abstract set(X,Y:int) : void // inline method
Abstract functions need to be fully qualified, including their return type. Class member functions are inferred in the same manner as regular functions.
Sealed functions cannot be overridden. The
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. The
sealed keyword is used to prevent inheritance:
class sealed Foo3D : Foo // Foo3D can no longer be inherited from ...
A pointer named
self is available inside any class method.
Classes can be created via the
var f = new Foo()
Local class variables are unsafe:
unsafe var f = Foo() // unsafe
Class methods can be invoked using
A specific version of the method can also be called explicitly:
2.18.1. Implementation details¶
Class initializers are generated by adding a local
self variable with construct syntax.
The body of the method is prefixed via a
with self expression.
The 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 the extra argument
The body of the method is prefixed with a
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 methods is implemented via invoke:
Every base class gets an
__rtti pointer, and a
__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 the rtti module to access class_info safely:
def class_info ( cl ) : StructInfo const?
finalize pointer is invoked when the finalizer is called for the class pointer.
That way, when delete is called on the base class pointer, the correct version of the derived finalizer is called.