Help with reflections

BlitzMax Forums/BlitzMax Beginners Area/Help with reflections

Hi Everybody,

I'm trying to write a function that goes through every object in a scene and saves its state (basically, it will be a project file for a game editor). Since I don't want to hard-code which fields get saved according to which kind of entity, I decided to learn to new reflection features.

It works well for simple fields (int, float, string...) but I'm having trouble when the object contains a field that asks another type, like this:


SuperStrict

'Creates a list containing all root objects
Global Rootlist:TList = CreateList() 

'Defines the types
Type TEntity
	Field Name:String
	Field X:Float, Y:Float
	Field Shape:TShape		'<-------Here's the problem!
End Type

Type TShape
	Field Name:String
	Method DoStuff() 
	End Method
End Type

'Creates the entities using the pre-defined types
Local MyShape:TShape = New TShape
MyShape.Name = "MyShape"
Local MyEntity:TEntity = New TEntity
MyEntity.Name = "MyEntity"
MyEntity.X = 100
MyEntity.Y = 50
MyEntity.Shape = MyShape
Rootlist.AddLast(MyEntity) 

'Main program, prints the name and fields of every entity in the scene
Graphics (800, 600, 0, 30) 

While Not AppTerminate() And Not KeyHit(KEY_ESCAPE) 

	Local Y:Int = 0
	
	For Local Obj:Object = EachIn Rootlist
		Local id:TTypeId = TTypeId.ForObject(Obj)   	'Creates a temporaty "Type Id" object, necessary to inspect other objects
		For Local Fld:TField = EachIn id.EnumFields() 
			Y:+16
			DrawText(Fld.Name(), 16, Y)  						'prints the field name
			DrawText (String(Fld.Get(Obj)), 112, Y)  		'Prints the field contents... but is empty when the field contains another object
		Next
		Y:+16
	Next
	
	Flip
	
Wend



The big problem is: if I run into a field that contains a different type (in this example, a "TShape" object) how do I:

1 - Identify that type?
2 - Identify a specific field within that type? In my example, If I could extract the "name" field from the shape I could easily reconnect the two objects when I load the project file in the editor.

Any help is appreciated! My brain is kinda numb right now...

Cheers,
Leo.

Why not check Brucey's Bah.Persistence mod?

Thank you, the serializer mod looks great!

But... I fell that I'm already close to getting it working, and that I would learn more if solved that little problem. So far, I made everything in my framework from scratch, and learned a ton from doing so! So all that I really need is...how do I identify/access the type that's in a field?

Thanks!

The general approach is via recursion. You handle all the base types (int, string etc), then if you've got an object type left, recurse.

I mentioned Brucey's mod as the code is all there - and it's one of the few recursive Reflection code samples I've seen that properly handles circular references.

TTypeId has a Name() method. Some snippets:
Local object_id:TTypeId=TTypeId.ForObject(_object:Object)

'	Type name - float, int, string etc	
Local s_name:String = object_id.Name() 

If object_id.ExtendsType(ArrayTypeId) Then...
If object_id.ExtendsType(TTypeId.ForName("TList")) Then...



I tried downloading BAH.Persistence to take a look at the code, but can't find it... It's not on Brucey's page, or on the GoogleCode project page (there's no link on that one). Where can I find it?

http://maxmods.googlecode.com/svn/trunk/

post deleted - was same as David.

BTW. what is reflection?

http://en.wikipedia.org/wiki/Reflection_%28computer_science%29
basically its just a way for a program to do stuff with its own code (functions, types and methods etc) at runtime, so from user interaction along side a scripting language etc.

I started with the code from Mark:
Type TObjectStream

	Method ReadObject:Object()
		'read type name
		Local name$=_stream.ReadLine()
		
		'find type
		Local id:TTypeId=TTypeId.ForName( name )
		
		'create new object!
		Local obj:Object=id.NewObject()
		
		'restore it's fields...
		For Local fld:TField=EachIn id.EnumFields()

			'ignore non-serializable fields
			If Not fld.MetaData( "serializable" ) Continue
			
			'get type of field
			Local fldType:TTypeId=fld.TypeId()
			
			'is field an object or int/float etc?
			If fldType.ExtendsType( ObjectTypeId )
				'field is an object - recurse
				fld.Set obj,ReadObject()
			Else
				'field must be int/float etc...a 'leaf'
				fld.Set obj,_stream.ReadLine()
			EndIf
		Next

		Return obj
	End Method
	
	Method WriteObject( obj:Object )
		'what type of object is it?
		Local id:TTypeId=TTypeId.ForObject( obj )
		
		'write it's type name, eg: "TMyType"
		_stream.WriteLine id.Name()
		
		'scan it's fields...
		For Local fld:TField=EachIn id.EnumFields()

			'ignore non-serializable fields
			If Not fld.MetaData( "serializable" ) Continue
			
			'get type of field
			Local fldType:TTypeId=fld.TypeId()
			
			'is field an object or int/float etc?
			If fldType.ExtendsType( ObjectTypeId )
				'field is an object - recurse
				WriteObject fld.Get( obj )
			Else
				'field must be int/float etc...a 'leaf'
				_stream.WriteLine fld.Get( obj ).ToString()
			EndIf
		Next
	End Method
	
	Field _stream:TStream 'underlying stream
	
End Type


Thanks for the help and the code (and for making it so easy to read!). I'm putting it to good use right now.

And Tin, another way of explaining reflection is: it's a feature that allows you to identify all the fields and methods inside an object. You can, for instance, expose the fields of an object that is selected by the user (some programs call that kind of view an "inspector" or "explorer"), etc.

I don't blame you for not knowing it: the documentation in Bmax seems to explain it for people who already know what it does... :-P ...like many other things...those docs need a major overhaul for beginners (specially considering that Bmax is kinda marketed as "easy for beginners").

And where's the Wiki?!?!?

Cheers!
Leo.