【中文版】

Introduction

MFP language introduction

operators

function

variable

if

while do for

break continue

select

try catch

class

call

citingspace

help

@compulsory_link

@execution_entry

@build_asset

MFP functions

deploy user functions

call MFP in your app

build Android APK

game programming

chart plotting

MFP math analysis

MFP file procession

number string and array

time date and system

Introduction of SCP

MFP language class and endclass statements:

* Class declaration

The class and endclass statements define the boundary of an MFP class. The class statement is the beginning of an MFP class. If there is no super class included in the declaration, which means the class is derived from object, basic-most MFP type, the class statement is like:

class Class_Name

. If, on the other hand, the class is directly derived from one or multiple super classes, the class statement is like:

class Class_Name: Super_Class1, Super_Class2, ..., Super_ClassN

. Here, directly derived means this class is child but not grand child of the super class(es). Also, super class name could include part or full citingspace path. For example,

class Class_Name: aaa::bbb::Super_Class1, ::ccc::Super_Class2, ..., Super_ClassN

is totally valid. MFP will find out super class definitions by parsing the citingspace path based on the citingspace context, i.e. in which citingspace this class statement is and how many using citingspace statements have been declared.

* Nested class and class members

Inside a class body are nested classes and/or class members, i.e. variables and functions. To nested class, the host class is just a citingspace. For example, if class A is defined in citingspace ::AAA::bbb and a nested class B is defined in class A, then the full citingspace path of class A is ::AAA::bbb::A and the full citingspace path of class B is ::AAA::bbb::A::B. Except the similarity of citingspace path, a nested class is independent of host class and is always visible inside and outside of the host class.

Class members are parts of class definition. There are two access modes for class members, i.e. private and public. Private members can only be accessed by same class member functions while public members can be accessed by any functions inside and outside the class. For example:

public variable self memberA = 7, memberB = "Hello", memberC
private function memberFunc(a, b, c)
...
endf

. If no access mode keyword exists, the class memeber is public.

Class member variable is declared a bit different from variable declaration statements in function. First, an access mode keyword, i.e. public or private, may exist in the beginning of the statement. Second, a self keyword must follow the variable keyword, which means the variable(s) declared in this statement are NOT static. At this moment, MFP does NOT support static member variable so that without a self keyword, the whole variable declaration statement is ignored. Last, a class member variable can be initialized in declaration statement. However, its initializer has to be a pure value, and cannot be a function. For example,

variable self varA = [[1,2]]

is right while

variable self varA = func(3,4)

is wrong. More examples of member variable declaration are:

variable self varA, varB = "Hello", varC = [[1,2],[3,4]]
private variable self varD

Like member variables, class member function declaration may have an access mode keyword, i.e. public or private in front. Also, if its parameter list starts with self, this function is not static. Inside the member function, MFP uses keyword self followed by a dot to access other members in the class. For example:

public function memberFunc(self, a, b, c)
	self.MemberA = a
	self.MemberB = b
	return self.MemberA * self.MemberB * self.memberFunc(c)
endf

. Without self in the parameter list, the member function is static. Clearly, a static member function cannot access non-static members in the same class. An example for static member function is shown below:

public function memberStaticFunc(a, b, c)
	return a+b+c
endf

.

The self keyword can access members in the class and public members in the super class(es). However, if the class and one of its super classes have the same name member, self keyword can only access the member in the class not the super class. For example:

class SuperClassA
	public function memberFunc(self, a)
		return a
	endf
endclass
class SuperClassB
	public function memberFunc(self, a)
		return 2*a
	endf
endclass
class ChildClass : SuperClassA, SuperClassB
	public function memberFunc(self, a)
		return 3*a
	endf
	public function memberFunc1(self)
		return self.memberFunc(3)	// call memberFunc in ChildClass, not SuperClassA or SuperClassB
	endf
endclass

. To access same name super class member, super member variable is provided. This member variable is an array whose first element is the object of the class's first super, second element is the object of its second super, etc. Here, an object of its super class is a sliced object, which means the super class object is actually a part of the object of the class. As such, in the above example, if developer wants to call memberFunc in the super classes, the code should be:

class SuperClassA
	public function memberFunc(self, a)
		return a
	endf
endclass
class SuperClassB
	public function memberFunc(self, a)
		return 2*a
	endf
endclass
class ChildClass : SuperClassA, SuperClassB
	public function memberFunc(self, a)
		return 3*a
	endf
	public function memberFunc1(self)
		variable x = self.super[0].memberFunc(3)	// call memberFunc in SuperClassA
		variable y = self.super[1].memberFunc(4)	// call memberFunc in SuperClassB
		return x + y
	endf
endclass

. If a class is not declared with any super class, its only super class is object. In this case self.super[0] returns a sliced object of object type.

Both member variables and member functions can be overridden in MFP. In this way, when MFP refers to an object's member (variable or function), it always calls the most "bottom" member. For example, if class A is derived from class B, and both A and B have a member variable named C, and we have defined a function as

function func(objOfClass)
	print(objOfClass.C)
endf

, then if an object of A is passed into this function, A's C value is printed. If an object of B is passed into this function, B's C value is printed.

However, the above rule sometimes causes confusions. Consider, for example, class B in the above example has a public member function reading member variable C's value. The developer of class B doesn't know another developer will derive class A from B. Thus he assumes that self.C in class B's memeber function always refers to B's member variable C. However, if the third developer creates an object from class A and calls the member function of class B (which is super class of class A), class A's C value is read.

class B
	variable self C = 1
	function printC(self)
		print("self.C = " + self.C + "\n")
	endf
endclass

class A : B
	variable self C = 2
endclass

function printABC()
	variable bObj = B(), aObj = A()
	bObj.printC()	// self.C = 1
	aObj.printC()	// self.C = 2
endf

If the first developer wants to ensure that self.C only refers to class B's C member variable in class B's member function(s), this variable should be used. Basically this variable returns a (sliced) object of current class where the calling function is located, as shown in the following code:

class B
	variable self C = 1
	function printC(self)
		print("self.this.C = " + self.this.C + "\n")
	endf
endclass

class A : B
	variable self C = 2
endclass

function printABC()
	variable bObj = B(), aObj = A()
	bObj.printC()	// self.this.C = 1
	aObj.printC()	// self.this.C = 1
endf

. Different from super member variable, which is public, this member variable is private.

* Constructor and magic functions

When creating an object from an MFP class, constructor function should be called. Different from other programming languages, MFP class constructor is a built-in function with no parameter. Developer CANNOT define, override or overload constructor. For example, if a class Abcd is defined in citingspace ::AAA::bbb, then the class constructor is function Abcd() and the full citingspace path of the function is ::AAA::bbb::Abcd(), as shown in the following code:

citingspace ::AAA::bbb
class Abcd
	variable self a = 1, b = "Hello", c
	public function printMembers(self)
		print("self.a = " + self.a + " self.b = " + self.b + " self.c = " + self.c)
	endf
endclass

endcs

function printABC()
	variable obj = ::AAA::bbb::abcd()
	obj.printMembers()	// self.a = 1 self.b = Hello self.c = NULL
endf

What a constructor does is simply setting member variables to be the values assigned to them in the variable statement. If no initializing value in the variable statement, the member variable is set as NULL. Constructor returns an object of the class.

Since user cannot define or overload a constructor, customized initialization work has to be done in a member function. The name, return type and parameter list of an initialization member function is up the the developer. However, MFP recommends to use __init__ as the function name and return the object itself. Keep in mind that __init__ is just a normal user defined function with no magic. It can be called multiple times at anytime and can be overloaded. For example:

citingspace ::AAA::bbb
class Abcd
	variable self a = 1, b = "Hello", c
	public function printMembers(self)
		print("self.a = " + self.a + " self.b = " + self.b + " self.c = " + self.c)
	endf
	public function __init__(self)
		self.a = 7
		self.c = (3-i) * self.a
		return self
	endf
	public function __init__(self, a, b, c)	// like any user defined member function, __init__ can be overloaded.
		self.a = a
		self.b = b
		self.c = c
		return self
	endf
endclass

endcs

function printABC()
	using citingspace ::AAA::bbb
	variable obj = abcd().__init__()
	obj.__init__(3, 2, 1)
	obj.__init__([5,4],[2,3],"WWW").printMembers()	// self.a = [5, 4] self.b = [2, 3] self.c = WWW
endf

MFP class has a number of magic built-in member functions which can be overridden. Function __to_string__ converts the object to a string. It is called when adding the object to a string or when MFP built-in function to_string is called with the object as the only parameter.

Function __deep_copy__ returns a deep copy of the object. It is called when MFP built-in function clone is called with the object as the only parameter.

Function __equals__ identifies if the object equals to the value of a variable. It is called when operator == is used to identify the equality of two variables.

Function __hash__ returns hash code of the object. It is called when MFP built-in function hash_code is called with the object as the only parameter.

Function __copy__ returns a shallow copy of the object. Its default behavior is to create a new object but all the member variables refer to the corresponding member variables of the old object.

Function __is_same__ identifies if the object is the same as another object referred by the parameter variable. This function simply compares reference so that its action is the same as the default behavior of operator ==. This function is useful when developer overrides __equals__ and wants to compare reference inside the overridden __equals__ function. MFP does NOT recommend to override this function.

The following example demonstrates the usage of the above functions:

class SampleClass
	variable self a = 1, b = 2
	public function __equals__(self, o)
		print("User defined __equals__\n")
		if self.__is_same__(o) // identify if self and o refer to the same object.
			return true
		elseif null == o
			return false
		elseif get_type_fullname(self) != get_type_fullname(o)
			return false
		elseif or(self.a != o.a, self.b != o.b)
			return false
		else
			return true
		endif
	endf
	public function __to_string__(self)
		return "Class SampleClass, a = " + a + " b = " + b
	endf
	public function __hash__(self)
		print("User defined __hash__\n")
		return a + b * 19
	endf
	public function __copy__(self)
		print("User defined __copy__\n")
		return self
	endf
	public function __deep_copy__(self)
		print("User defined __deep_copy__\n")
		variable o = SampleClass()
		o.a = self.a
		o.b = self.b
		return o
	endf
endclass

function testOverriddenMagicFunctions()
	variable obj1 = SampleClass()
	print(obj1)	// will output Class SampleClass, a = 1 b = 2
	variable obj2 = clone(obj1)	// will output User defined __deep_copy__
	print("obj1 == obj2 is " + (obj1 == obj2))	// will first output User defined __equals__, then output obj1 == obj2 is true
	print(hash_code(obj2)) // will first output User defined __hash__, then output 39
endf