What is the name of the automatic destructor?

Monkey Forums/Monkey Beginners/What is the name of the automatic destructor?

Danilo(Posted 2014) [#1]
Hi!

Somebody knows the method name of the destructor
that is called when the object gets (automatically) destroyed?

Can't find it in the documentation nor in the forums.

How can I put code into this simple forum? Trying code tags...
Strict

Class myClass
	Public
		Method New()
			Print("Constructor")
		End Method
		
		Method Destructor:Void()
			Print("Destructor")
		End Method

		Method Delete:Void()
			Print("Destructor")
		End Method

		Method Dispose:Void()
			Print("Destructor")
		End Method

End Class

Function Main:Int()
    Local c1:myClass = New myClass()

    Return 0
End Function


No preview function before I submit, hope it works.


Nobuyuki(Posted 2014) [#2]
The default constructor is New(). There is no built-in finalizing method for object destruction -- the garbage collector will handle this for you once an object reference goes out of scope. You may, however, flag resources for disposal by setting them to Null. Some mojo and brl functions have dispose methods which handle this.


Danilo(Posted 2014) [#3]
Thanks Nobuyuki!

So we need to call object.Destroy() ourself every time. Hmm.

Do you know why the following simple code does not work?
Strict

Function DoIt:Void()
	Print("DoIt")
End Function


Function Main:Int()
    Print("In Main()")
    DoIt()

    Return 0
End Function

I am on Mac OS X.

With HTML target I get no output. If I out-comment the call to DoIt(), I get the output "In Main()",
with the call I get no output at all.

Same code compiled as target "C++ Tool" is compiled OK by Monkey, but it generates wrong C++ code:
"/Users/danilo/Monkey/bin/transcc_macos" -target=C++_Tool -config=Release -run "/Users/danilo/Projects/Monkey/Tests/Test 0002.monkey"
TRANS monkey compiler V1.60
Parsing...
Semanting...
Translating...
Building...
main.cpp:2045:6: error: variable has incomplete type 'void'
void bb_Test 0002_DoIt();
     ^
main.cpp:2045:13: error: expected ';' after top level declarator
void bb_Test 0002_DoIt();
            ^
            ;
main.cpp:2047:6: error: variable has incomplete type 'void'
void bb_Test 0002_DoIt(){
     ^
main.cpp:2047:13: error: expected ';' after top level declarator
void bb_Test 0002_DoIt(){
            ^
            ;
4 errors generated.
TRANS FAILED: Error executing 'g++ -Wno-parentheses -Wno-dangling-else -arch i386 -read_only_relocs suppress -mmacosx-version-min=10.3 -O3 -DNDEBUG -o main_macos main.cpp', return code=256
Done.

I can't see the error in this code. Monkey compiler does not complain, but generated code is wrong or does not work.

Thanks in advance!


Halfdan(Posted 2014) [#4]
The code is valid monkey code, it runs fine on my machine on both HTML5 and GLFW (OSX 10.9, MonkeyPro75d).

Maybe something with your setup, can you run the examples that come with Monkey?


Danilo(Posted 2014) [#5]
I'm using latest final, Monkey Pro 76d. I will check going back to 75d...


Danilo(Posted 2014) [#6]
With 75d I got:
"/Users/danilo/Monkey/bin/transcc_macos" -target=Html5_Game -config=Release -run "/Users/danilo/Projects/Monkey/Tests/Test 0002.monkey"
TRANS monkey compiler V1.56
Parsing...
/Users/danilo/Projects/Monkey/Tests/Test 0002.monkey<1> : Error : Invalid module identifier 'Test 0002'.
Done.

OK, renamed my file "Test 0002.monkey" to "Test_0002.monkey" and it works now with 75d.
Tried again with 76d, and it works now, too. Problem was the space in the filename.

Thanks guys!


dawlane(Posted 2014) [#7]
Danilo: You will find a few other things that Monkey Doesn't like in file names.


Danilo(Posted 2014) [#8]
I understand. Only english characters, underscore and numbers are allowed in filenames.
I guess filenames also can't start with numbers then.

If Monkey takes the filename in the code generator, couldn't it replace unknown characters with
underscores automatically, or something like that?
Filename "01 Test 0001!" could become module name "_01_Test_0001_" internally.


Goodlookinguy(Posted 2014) [#9]
Think Java. In java your class name and file name must be the same. Except in Monkey's case if you dare to have a file name that has the same casing as a class name, expect issues.


Danilo(Posted 2014) [#10]
Is it allowed to make feature requests here? (and if so, what's the best forum category for it?)

I would like to see destructors added to the language. It should inherit from 'Object',
maybe as a virtual/abstract function. Every class in a hierarchy can have it's own
destructor. The highest destructor in the hierarchy gets called when the object gets released
and can call super.Destructor() after it has done it's own cleanup stuff.
Or all destructors in the hierarchy get called automatically, starting at the highest level.

Sometimes you have to cleanup something before the object gets destroyed, especially if
you do API programming. Or a file class could automatically close the file if it is still opened
because the user forgot to call f.close().
Doing this automatically can prevent errors and is also nice to use. No need to call obj.Destroy()
for every object by hand, and if you forget such a call, it is done automatically and no leaks appear...


Danilo(Posted 2014) [#11]
Made a test for collecting objects and cleanup at program end. Works only as targets "C++ Tool" and "GLFW" for me, here on Mac OS X Mavericks with Safari 7.0.1.
(EDIT: Works as HTML target on Windows)
(EDIT 2: Works as HTML target on Mac OS X now, after restarting Safari/Computer)

Strict

Class Exception Extends Throwable

	Field exception:String
	
	Method New( msg:String )
		Self.exception = msg
	End Method

End Class


Class BaseObj Abstract

	Private

		Global objList:List<BaseObj> = New List<BaseObj>
		Field className:String

		Method _reg:Void(s:String)
			objList.AddFirst(Self)
			className = s
			Print(">>> Registered object of type: "+s)
		End Method

		Method New()
			Print(">>> BaseObj::Constructor")
			className = "Class BaseObj"
		End Method

	Public

		Method ToString:String()
			Return className
		End Method

		Method Delete:Void()
			objList.RemoveFirst(Self)
			'Print("Removed object of type: "+className)
			Print(">>> BaseObj::Destructor ("+className+")")
		End Method

		Function Cleanup:Void()
			For Local o:BaseObj = Eachin objList
				o.Delete()
			Next
		End Function

End Class


Class MyClass Extends BaseObj

	Public

		Method New()
			Print(">>> MyClass::Constructor")
			_reg("Class MyClass")
		End Method
		
		Method Delete:Void()
			Print(">>> MyClass::Destructor")
			Super.Delete()
		End Method

End Class


Class Address Extends BaseObj

	Private

		Field _name:String, _street:String, _zip:String, _town:String
		
		Method New()
			Throw New Exception(">>> >>> Address Constructor with 0 arguments is private")
		End Method

	Public

		Method New(name:String, street:String, zip:String, town:String)
			Print(">>> Address::Constructor")
			_reg("Class Address")
			_name   = name
			_street = street
			_zip = zip
			_town = town
		End Method
		
		Method ToString:String()
			Return _name + ", " + _street + ", " + _zip + ", " + _town
		End Method

End Class




Function DoIt:Void()
	Local c2:MyClass = New MyClass()
	'c2.Delete()
End Function


Function Main:Int()
	Try
		Local c1:MyClass = New MyClass()
	
		Print( c1.ToString() )
		
		'c1.Delete()
	
		Local adr:Address = New Address("John S.","The Street","12345","N.Y.")
		
		Print( adr.ToString() )
		
		'adr.Delete()
	
	
		DoIt()

		'Local adr2:Address = New Address() ' why private constructor can be called?
	
		Print("------------")
		Print("Program End.")
		Print("------------")
		
	Catch ex:Exception

		Print( "PROGRAM EXCEPTION: " + ex.exception )

	End Try


	BaseObj.Cleanup()
	Return 0
End Function


Is it possible to make the default constructor New() private somehow, so it can't be called?
Like in the "Class Address", where I wanted to disallow the default New() and force the use of New(name, street, ...)


Goodlookinguy(Posted 2014) [#12]
Is it allowed to make feature requests here? (and if so, what's the best forum category for it?)


I recommend you post it in the 'Monkey Programming' forum. It's sort of the general go-to forum for requests and such. You can also alternatively post in the 'Bug Reports' area as a request. I've seen a few people do it, although I discourage it because it clutters the already cluttered 'Bug Reports'.

Also, regarding your cleanup code, it looks like you're trying to write Jimmy-proof code. While it might be good for your purposes, it does not necessarily fit every single case. Which is why Monkey will likely never get this. If it does, then good, if it doesn't, that doesn't bother me either. The neat part about Monkey is the fair bit of flexibility it gives to develop a system you want.

Is it possible to make the default constructor New() private somehow, so it can't be called?
This is probably the best solution to the private new issue.
Function Main:Int()
	Local show := New Example(10)
	Print show.value
	show = New Example()
End

Class Example
	Method New()
		Error("You cannot initialize Example this way")
	End
	
	Method New( value:Int )
		Self.value = value
	End
	
	Field value:Int
End



Danilo(Posted 2014) [#13]
Thanks Goodlookinguy! I will read the complete documentation and the book "Monkey Game Development" now
to learn some of the basics.


NoOdle(Posted 2014) [#14]
As a rule I try stick with to this convention and I haven't had any problems:

Monkey File Names: all lowercase letters, no spaces or special chars
Monkey Class Names: same as the filename except with first letters capitalised.

e.g. mymonkeyfile.monkey Class MyMonkeyFile


dawlane(Posted 2014) [#15]
I will read the complete documentation and the book "Monkey Game Development" now
It would also be worth your while having a look at invaderJim's tutorials.
Like NoOdle. I also try and stick to a naming convention.
Monkey File names: Just like NoOdle are all lower case, no spaces or special characters. But I do try to give them a descriptive name. Any file that is a class or contains classes that are closely tied to one an other will start with a c and the name of the primary class. Files that need to be seen by any other module tend to go in a file called global or if this file gets too big it can be broken down and each section then starts with a g. I use a file that is named exactly the same as the application. It is here that I place the primary modules to import and the program entry Main function. The file for the extended App class when using mojo usually gets named either main, cgame or the application name starting with a c depending on the applications name.
Monkey Class Names: Just like NoOdle does. But all Classes start with a capital C e.g. cmysprites.monkey Class CMySprites.
Constants: are all capitals
Functions: First letters of each part always capital e.g Function MyFunction:Void()
Variables: Lower case. Underscores allowed where necessary to improve readability. And certain structures such as lists prefixed with an abbreviation of three letters and a underscore. e.g List prefix lst_
One more thing when naming file names don't use names that are reserved words. e.g import as it confuses the compiler.


Danilo(Posted 2014) [#16]
I thought I could prevent the problem when I write my classes with C++, but it looks like
Monkey and its garbage collector do not release imported C++ classes correctly.

class_test.cpp
#include <iostream>

class Console {
    public:
        Console(void) {
            std::cout << "Console::Constructor" << std::endl;
        }
        ~Console(void) {
            std::cout << "Console::Destructor" << std::endl;
        }
        void Out(String _s) {
            std::wstring s( _s.Data() );
            std::wcout << s << std::endl;
        }
        void Delete(void) {
            delete this;
        }
};

class_test.monkey
Import "class_test.cpp"

Extern

Class Console
	Method Out:Void(s:String)
	Method Delete:Void()
End

Public

Function DoIt:Void()
	Local c1:Console = New Console
	c1.Out("Hello from Monkey::DoIt()")
	'c1.Delete()
End Function

Function Main:Int()
	Local c2:Console = New Console
	c2.Out("Hello from Monkey::Main()")
	'c2.Delete()
	
	DoIt()

	Return 0
End

(compiled as target "C++ Tool" on Mac OS X)

Output code for the function DoIt():
void bb_class_test_DoIt(){
	Console* t_c1=(new Console);
	t_c1->Out(String(L"Hello from Monkey::DoIt()",25));
}


Shouldn't Monkey add a 'delete' to the generated C++ code, because the
pointer is going out of scope?
The local variable "Local c2:Console = New Console" is not released correctly here.

Without using pointers for local class variables, C++ takes care of the cleanup automatically:

class_test_cpp.cpp
//
// g++ class_test_cpp.cpp
// ./a.out
//
#include <iostream>

class Console {
    public:
        Console(void) {
            std::cout << "Console::Constructor" << std::endl;
        }
        ~Console(void) {
            std::cout << "Console::Destructor" << std::endl;
        }
        void Out(std::string s) {
            std::cout << s << std::endl;
        }
        void Delete(void) {
            delete this;
        }
};

void DoIt() {
    Console c1;
    c1.Out("Hello from DoIt()");
}

int main() {
    Console c2;
    DoIt();
    c2.Out("Hello from main()");
    return 0;
}

I expected Monkey to do the same for local class variables.

With the test code above, imported C++ classes seem to get never released by Monkey.
What did I do wrong? When I call DoIt() a few million times at runtime, and the objects get never
released, this could lead to out-of-memory problems.
Is it possible to add C++ objects to the garbage collector somehow, so it takes care of it?