About Monkey 2 › Forums › Monkey 2 Programming Help › Basic reflection question
Tagged: reflection
This topic contains 8 replies, has 2 voices, and was last updated by
Mark Sibly
2 years, 1 month ago.
-
AuthorPosts
-
March 9, 2017 at 7:55 am #7428
If I have a string that contains the name of the class I want (let’s say, retrieved from a text file), how do I create an instance of that class?
Something along the lines of:
[/crayon]Monkey1234[crayon-5cba87021f6c2083155071 inline="true" ]Local className := "MyClass"Local classInstance := CreateObjectFromString( className ) 'This is obviously a made up functionCheers.
March 9, 2017 at 9:56 pm #7443Have you looked at the reflectiontest banana? There’s an exampole dynamically creating an object there…
March 9, 2017 at 10:15 pm #7444Ah, I had not looked into the examples.
Looking at it from Github now, seems like it will work like a charm.Thanks!
March 10, 2017 at 12:23 am #7445There is still some work to do on reflection, mostly ‘selective’ reflection. Currently reflection is ‘all or nothing’, and adds considerable size overhead to builds.
But this will happen eventually and apart from that I reckon it works well.
March 11, 2017 at 5:26 am #7465One thing I also noticed is that the Reflection module doesn’t seem to be included at all in the “Modules Reference” page, so it’s hard to figure out which methods, properties, etc. are available.
Thanks Mark!
March 11, 2017 at 9:08 am #7469Ok, still having some trouble, this time when trying to read the fields from a .json file and assign the values to the class fields.
It works fine if I just print the values and don’t actually assign them, but if you uncomment the lines that do the assignment I keep getting Memory access violations.
Here’s the json file. The class names are set for each Json object, in this format: “objectName:namespace.Class”
Monkey1234567891011{"bob:test.Entity":{"sort":0,"layer":1,"animation":"idle","category":"npc","position:test.Vec":{ "x":100, "y":50 },"size:test.Vec":{ "x":30, "y":20 }}}And here’s the Monkey2 code. Forgive me if it’s a little messy, I was in the middle of experimenting with this.
Monkey1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889Namespace test#Import "<std>"#Import "<reflection>"#Import "test.json"Using std..Function Main()LoadFromFile( "asset::test.json" )End'************************************************************************************************************************'Function LoadFromFile( path:String )Local json := JsonObject.Load( path )If jsonFor Local entry := EachIn json.ToObject().KeysLoadFromMap( json[ entry ].ToObject(), entry )NextEndEndFunction LoadFromMap:Variant( obj:StringMap<JsonValue>, objType:String )Local inst:VariantLocal entry := objType.Split(":")If entry.Length <> 2 Then Print( "Error: Invalid format, entries should be 'name:Class'")Local newObj := TypeInfo.GetType( entry[1] )If newObjPrint( " ~n" + "New " + objType )Local ctor := newObj.GetDecl( "New" )'This is the instance we'll assign the field values toinst = ctor.Invoke( Null, Null )For Local key := EachIn obj.KeysLocal d:= newObj.GetDecl( key )'If the field is an object, recursively call LoadFromMapIf obj[key].IsObject'Lets create the object, and return it so we can assign it to the appropriate fieldLocal o := LoadFromMap( obj[key].ToObject(), key )If inst = Null Then Print( "Error: no instance" )If o = Null Then Print( "Error: no object" )'If o Then d.Set( inst, o ) '<--- This fails, even though 'inst' and 'o' exist'If o Then d.Set( Cast<Entity>( inst ), Cast<Vec>( o ) )'<--- Just testing , didn't work eitherEnd'Sweet! Now let's try to set the other fields!If obj[key].IsStringPrint( entry[0] + "." + key + ":String = '" + obj[key].ToString() + "'" )d.Set( inst, obj[key].ToString() )EndIf obj[key].IsNumberPrint( entry[0] + "." + key + ":Number = " + obj[key].ToNumber() )'d.Set( inst, obj[key].ToNumber() ) '<------------------------ This fails unless there are no other fieldsEndNextElsePrint( "Error: Class " + objType + " not found" )EndIf inst <> NullReturn instElsePrint( "Error: Nothing to return" )Return NullEndEnd'************************************************************************************************************************Class EntityField name:StringField animation:StringField category:StringField layer:DoubleField sort:DoubleField size:VecField position:VecEndClass VecField X:FloatField Y:FloatEndAny help appreciated!
Cheers.March 12, 2017 at 10:08 pm #7476Ok, haven’t actually tried it but I think the problem is here:
Local d:= newObj.GetDecl( key )
In the case of objects, ‘key’ will contain a ‘:’ char so GetDecl will be returning an ‘invalid’ decl as fields don’t have ‘:’ in their names.
This isn’t easy to spot, as the debugger is struggling a bit with ‘internal’ types like Decl, Variant etc!
A better approach may be to use a ‘class’ key in the json and leave field names ‘unmangled’.
March 13, 2017 at 5:17 am #7479Yay, that worked! (after I cleaned it up and fixed another issue, the Vec class had X and Y fields capitalized, while the json file didn’t… took me forever to catch that one…
)
Here’s the cleaned up json file:
[/crayon]Monkey1234567891011121314[crayon-5cba8702345ae242368566 inline="true" ]{"bob":{"name":"Bob","class":"test.Entity","sort":0,"layer":1,"animation":"idle","category":"npc","position":{ "class":"test.Vec", "x":100, "y":50 },"size":{ "class":"test.Vec", "x":30, "y":20 }}}And here’s the example that uses it:
[/crayon]Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293[crayon-5cba8702345b3587439640 inline="true" ]Namespace test#Import "<std>"#Import "<reflection>"#Import "test.json"Using std..Function Main()LoadFromFile( "asset::test.json" )For Local e := Eachin Entity.alle.List()NextEnd'************************************************************************************************************************'Function LoadFromFile( path:String )Local json := JsonObject.Load( path )If jsonFor Local entry := EachIn json.ToObject().KeysLoadFromMap( json[ entry ].ToObject(), entry )NextEndEndFunction LoadFromMap:Variant( obj:StringMap<JsonValue>, objName:String )Local inst:VariantLocal objClass:= obj["class"].ToString()Local newObj := TypeInfo.GetType( objClass )If newObjLocal ctor := newObj.GetDecl( "New" )inst = ctor.Invoke( Null, Null ) 'This is the instance we'll assign the field values to.For Local key := EachIn obj.KeysIf key = "class" Then ContinueLocal d:= newObj.GetDecl( key )'If the field is an object, recursively call LoadFromMap.If obj[key].IsObjectLocal o := LoadFromMap( obj[key].ToObject(), key )If o Then d.Set( inst, o )End'Now let's try to set the other fields.If obj[key].IsString Then d.Set( inst, obj[key].ToString() )If obj[key].IsNumber Then d.Set( inst, obj[key].ToNumber() )If obj[key].IsBool Then d.Set( inst, obj[key].ToBool() )NextElsePrint( "Error: Class " + objClass + " not found" )EndIf inst = Null Then Print( "Error: Nothing to return" )Return instEnd'************************************************************************************************************************Class EntityGlobal all:= New Stack<Entity>Field name:StringField animation:StringField category:StringField layer:DoubleField sort:DoubleField size:VecField position:VecMethod New()all.Push( Self )EndMethod List()Print "Entity:"Print "name = " + namePrint "animation = " + animationPrint "category = " + categoryPrint "layer = " + layerPrint "sort = " + sortPrint "size = " + size.x + "," + size.yPrint "position = " + position.x + "," + position.yEndEndClass VecField x:DoubleField y:DoubleEndMark, if you think this is good enough, feel free to use it as an example for the Reflection module (or for the Json module).
Do you think Reflection will ever allow generics? I’ll make special non-generic classes for now.
Thanks!March 13, 2017 at 9:24 pm #7487Nice!
Do you think Reflection will ever allow generics?
Not sure yet, possibly, but it’ll never be able to ‘instantiate’ types that haven’t already been instantiated at compile time, so it’ll be more limited. eg: You wont be able to use Stack<Vec> if there isn’t a ‘compiled in’ one to begin with. But this is probably OK for many uses.
-
AuthorPosts
You must be logged in to reply to this topic.