Global i:ClassName = new ClassName() not working

Monkey Forums/Monkey Programming/Global i:ClassName = new ClassName() not working

AaronK(Posted 2012) [#1]
Pretty much summed up in the title, but if I have a line above somewhere in a .monkey file, then the constructor (New()) of ClassName is not called at program start up and therefore i is not initialised.

Essentially I want some self initialising objects that I don't have to actually explicitly call with something like

ClassName.Init()

Cheers
Aaron


AaronK(Posted 2012) [#2]
.


Raz(Posted 2012) [#3]
What makes you think it's not called?

Maybe you need to init the objects from within Main() (or the App class that gets run from there)

From a HTML5 test, until something happens from within Main() you will not see any output

Import mojo

Class ClassName

	Method New()
		Print "New() called"
	End
	
End

Global i:ClassName = New ClassName()

Function Main()
	Print "After Main() called"	' REMOVE ME
	i = New ClassName()		' REMOVE ME
	Error "Done"
End


The above gives the output
New() called
After Main() called
New() called


but if you remove the highlighted lines you get no output at all


AaronK(Posted 2012) [#4]
What makes me think it is NULL is that in my New method I'm registering the object with another object but that second object is not getting anything registered with it. It seems that even with an expression assigned to a global, the expression (allocation) is not actually done until the first access of the object and because I don't actually references the globalTrackee anywhere, it's never created.

Here's a better example:


Class Trackee
	Method New()
		GetTracker().Register(Self)
	End
End

Function GetTrackee:Trackee()
	Return globalTrackee
End
Global globalTrackee:Trackee = New Trackee()	' I was expecting this to call new at beginning of program, register itself with Tracker()


Class Tracker

	Method New()
		m_trackees = New List<Trackee>
	End
	
	Method Register(trackee:Trackee)
		m_trackees.AddLast(trackee)
	End
	
	Method NumberOfTrackees:Int()
		Return m_trackees.Count()
	End
	
	
Private
	Field m_trackees:List<Trackee>
End

Function GetTracker:Tracker()
	Return globalTracker
End
Global globalTracker:Tracker = New Tracker()


Function Main()
	'GetTrackee()		' Uncomment this line and the print below will print 1. With comment it prints 0
	
	Print "Number of trackees = " + GetTracker().NumberOfTrackees()	
End



As you can see I had two globals that I expected to be new'ed on startup and then one registering itself with the other. However, because I didn't actually references globalTrackee in code for some reason it looks like the new() isn't happening and therefore not registering with the Tracker.


Raz(Posted 2012) [#5]
Maybe it's because you're setting globalTracker = New Tracker() after you first call globalTrackee = New Trackee()

?


therevills(Posted 2012) [#6]
I've seen something a bit simlar to this with Global members too:

http://www.monkeycoder.co.nz/Community/posts.php?topic=2665

Here Amon had this:
Global sm:Image = LoadImage("graphics/16bg.png", 1, Image.MidHandle)


And Monkey didn't like it at all - although it did compile.


jpoag(Posted 2012) [#7]
Ok, so I tore apart trans to figure this out.

The reason is that if a Declaration isn't referenced somewhere down the line from Main(), then it isn't Semanted. Unsemanted code doesn't get emitted.

This is a good thing as I have discovered, there are a TON of declarations that aren't referenced and dropped completely from the program. This makes the output smaller.

There is a way to trick your program into semanting the expressions, and it comes from the Reflection module.

#REFLECTION_FILTER="nameofmodule"

Import reflection


Make sure 'nameofmodule' is all lowercase and it reflects the name of the monkey file with the class you want semanted.

Now, this design is very similar to a Factory, where classes automatically register themselves with a global factory to be instantiated by proxy.

However, the concept is very similar to wiebow's UnitTesting framework I use. In that, I have a module that imports all of the tests I want and then I turn the reflection filter on that module.

Anyways, you could Import all of your concrete classes into a single module and use reflection to import that module "widget*". E.g. I have all of my tests imported with "tests.monkey". I import tests.monkey into my main app, and tell the reflection filter to filter on "tests*" which imports all of the sub modules.

Hope that helps


Samah(Posted 2012) [#8]
Your problem is that the instantiation and assigning of globals happens at startup, in order. This means that the Trackee is being instantiated before the Tracker. Calling GetTracker() works fine because that function has already been compiled, but it returns Null because Tracker hasn't yet been instantiated and assigned to globalTracker.

The solution is to instantiate and assign the Tracker object first. Personally I would do the instantiation/assignment in Main() rather than in the globals' declarations.


jpoag(Posted 2012) [#9]
Maybe I'm missing something, but when I tried changing the orders around, it still doesn't function properly:
Class Tracker

	Method New()
		m_trackees = New List<Trackee>
	End
	
	Method Register(trackee:Trackee)
		m_trackees.AddLast(trackee)
	End
	
	Method NumberOfTrackees:Int()
		Return m_trackees.Count()
	End
	
	
Private
	Field m_trackees:List<Trackee>
End

Global globalTracker:Tracker = New Tracker()
Function GetTracker:Tracker()
	Return globalTracker
End

Class Trackee
	Method New()
		GetTracker().Register(Self)
	End
End

Global globalTrackee:Trackee = New Trackee()	' I was expecting this ...
Function GetTrackee:Trackee()
	Return globalTrackee
End


Function Main()
	'GetTrackee()		' Uncomment this line and the print below will print 1...
	
	Print "Number of trackees = " + GetTracker().NumberOfTrackees()	
End


That's everything in the proper order, right? The output is still 0.

No matter what order, the reflection fix still works.


AaronK(Posted 2012) [#10]
Thanks guys. Even if order would fix it, it's not a sufficient solution for me because the classes are in different files and I don't want to have this order dependency with import.

Call it "suffering from baby brain" still but looking at the way I'm doing it, it's essentially like statics in C++ and with those, order is undefined. Therefore I think I should probably just move to a more traditional Singleton pattern and put up with an "init" function on one that will access the Singleton of the other. Oh well, would have been nice as I do like the idea of just assigning a new() expression to a global singleton static.

I might take a look at the reflection "hack" to get them instantiated. And also, where do I get this Unit Testing framework from?


jpoag(Posted 2012) [#11]
http://code.google.com/p/mutated-monkey/wiki/unittest (written by wiebow)

I don't consider the reflection bit a hack, I intend on doing something very similar to what you've described in order to implement a Factory.