Global i:ClassName = new ClassName() not working
Monkey Forums/Monkey Programming/Global i:ClassName = new ClassName() not working
| ||
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 |
| ||
. |
| ||
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 |
| ||
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. |
| ||
Maybe it's because you're setting globalTracker = New Tracker() after you first call globalTrackee = New Trackee() ? |
| ||
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. |
| ||
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 |
| ||
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. |
| ||
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. |
| ||
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? |
| ||
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. |