January 15, 2021, 06:13:51 PM

Author Topic: [bmx] Object Loader by Otus [ 1+ years ago ]  (Read 733 times)

Offline BlitzBot

  • Jr. Member
  • **
  • Posts: 1
[bmx] Object Loader by Otus [ 1+ years ago ]
« on: June 29, 2017, 12:28:40 AM »
Title : Object Loader
Author : Otus
Posted : 1+ years ago

Description : Allows you to read or write an arbitrary object to a stream (eg. a file or over the network). Uses the reflection module to get runtime information about type structure (fields). May be of use when implementing saved games or multiplayer over the network.

For an example program and a module version, please download the zip file from here: <a href="http://jan.varho.org/blog/programming/blitz/blitzmax-object-loader-module/" target="_blank">http://jan.varho.org/blog/programming/blitz/blitzmax-object-loader-module/[/url]

Bug reports (and patches) appreciated!

UPDATE: Added checks for saving TList and TMap objects properly.


Code :
Code: BlitzMax
  1. ' Loads and saves any objects to a stream using the reflection module.
  2. ' Recursive - any and all objects referenced are also saved!
  3. ' Use the metadata key "no_save" to mark fields not to be saved.
  4.  
  5. SuperStrict
  6.  
  7.  
  8. Import BRL.Reflection
  9.  
  10.  
  11. 'Saves an object to the stream specified
  12. Function SaveObject(o:Object, s:TStream, tid:TTypeId = Null)
  13.        
  14.         'Null?
  15.         If o
  16.                 s.WriteByte True
  17.         Else
  18.                 s.WriteByte False
  19.                 Return
  20.         End If
  21.        
  22.         'Get type Id
  23.         If tid = ObjectTypeId Then tid = TTypeId.ForObject(o)
  24.         If Not tid Then tid = TTypeId.ForObject(o)
  25.        
  26.         'Save type name
  27.         s.WriteInt tid.Name().length
  28.         s.WriteString tid.Name()
  29.        
  30.         'DebugLog "Saving "+tid.Name()
  31.        
  32.         'Primitive type?
  33.         Select tid
  34.         'Integers
  35.         Case ByteTypeId
  36.                 s.WriteByte     Byte(String(o) )
  37.                 Return
  38.         Case ShortTypeId
  39.                 s.WriteShort    Short(String(o) )
  40.                 Return
  41.         Case IntTypeId
  42.                 s.WriteInt      Int(String(o) )
  43.                 Return
  44.         Case LongTypeId
  45.                 s.WriteLong     Long(String(o) )
  46.                 Return
  47.         'Floating Points
  48.         Case FloatTypeId
  49.                 s.WriteFloat    Float(String(o) )
  50.                 Return
  51.         Case DoubleTypeId
  52.                 s.WriteDouble   Double(String(o) )
  53.                 Return
  54.         'Strings
  55.         Case StringTypeId
  56.                 s.WriteInt      String(o).length
  57.                 s.WriteString   String(o)
  58.                 Return
  59.         End Select
  60.        
  61.         'Array?
  62.         If tid.Name().EndsWith("[]")
  63.                 'Save length
  64.                 s.WriteInt tid.ArrayLength(o)
  65.                
  66.                 'Element type
  67.                 Local etid:TTypeId = TTypeId.ForName(tid.Name()[..tid.Name().length - 2])
  68.                
  69.                 'Save elements
  70.                 For Local i:Int = 0 To tid.ArrayLength(o)-1
  71.                         SaveObject tid.GetArrayElement(o, i), s, etid
  72.                 Next
  73.                 Return
  74.         End If
  75.        
  76.         'Map?
  77.         If tid.Name()="TMap"
  78.                 Local m:TMap = TMap(o)
  79.                
  80.                 'Save length
  81.                 Local l:Int = 0
  82.                 For Local key:Object = EachIn m.Keys()
  83.                         l:+1
  84.                 Next
  85.                 s.WriteInt l
  86.                
  87.                 'Save key-value pairs
  88.                 For Local node:TNode = EachIn m
  89.                         SaveObject node.Key(), s
  90.                         SaveObject node.Value(), s
  91.                 Next
  92.                 Return
  93.         End If
  94.        
  95.         'List?
  96.         If tid.Name()="TList"
  97.                 Local l:TList = TList(o)
  98.                
  99.                 'Save length
  100.                 s.WriteInt l.Count()
  101.                
  102.                 'Save contents
  103.                 For Local obj:Object = EachIn l
  104.                         SaveObject obj, s
  105.                 Next
  106.                 Return
  107.         End If
  108.        
  109.         'Save fields
  110.         For Local f:TField = EachIn tid.EnumFields()
  111.                 If f.MetaData("no_save") Then Continue
  112.                 SaveObject f.Get(o), s, f.TypeId()
  113.         Next
  114.        
  115. End Function
  116.  
  117. 'Reads an object to the stream specified
  118. Function LoadObject:Object(s:TStream)
  119.        
  120.         'Null?
  121.         Select s.ReadByte()
  122.         Case False
  123.                 Return Null
  124.         Case True
  125.         Default
  126.                 RuntimeError "Not an object"
  127.         End Select
  128.        
  129.         'Get type Id
  130.         Local tid:TTypeId = TTypeId.ForName(s.ReadString(s.ReadInt() ) )
  131.        
  132.         'DebugLog "Loading "+tid.Name()
  133.        
  134.         'Primitive type?
  135.         Select tid
  136.         'Integers
  137.         Case ByteTypeId
  138.                 Return String.FromInt(s.ReadByte())
  139.         Case ShortTypeId
  140.                 Return String.FromInt(s.ReadShort())
  141.         Case IntTypeId
  142.                 Return String.FromInt(s.ReadInt())
  143.         Case LongTypeId
  144.                 Return String.FromLong(s.ReadLong() )
  145.         'Floating point numbers
  146.         Case FloatTypeId
  147.                 Return String.FromFloat(s.ReadFloat())
  148.         Case DoubleTypeId
  149.                 Return String.FromDouble(s.ReadDouble() )
  150.         'Strings
  151.         Case StringTypeId
  152.                 Return s.ReadString(s.ReadInt())
  153.         End Select
  154.        
  155.         Local o:Object
  156.        
  157.         'Array?
  158.         If tid.ElementType()
  159.                 'Get length
  160.                 Local l:Int = s.ReadInt()
  161.                
  162.                 'Create array
  163.                 o = tid.NewArray(l)
  164.                
  165.                 'Load elements
  166.                 For Local i:Int = 0 To l - 1
  167.                         Local obj:Object = LoadObject(s)
  168.                         If obj Then tid.SetArrayElement o, i, obj
  169.                 Next
  170.                 Return o
  171.         End If
  172.        
  173.         'Map?
  174.         If tid.Name()="TMap"
  175.                 Local m:TMap = New TMap
  176.                
  177.                 'Get length
  178.                 Local l:Int = s.ReadInt()
  179.                
  180.                 'Load key-value pairs
  181.                 For Local i:Int = 0 To l - 1
  182.                         Local key:Object = LoadObject(s)
  183.                         Local value:Object = LoadObject(s)
  184.                         m.Insert key, value
  185.                 Next
  186.                 Return m
  187.         End If
  188.        
  189.         'List?
  190.         If tid.Name()="TList"
  191.                 Local l:TList = New TList
  192.                
  193.                 'Get length
  194.                 Local length:Int = s.ReadInt()
  195.                
  196.                 'Load key-value pairs
  197.                 For Local i:Int = 0 To length - 1
  198.                         l.AddLast LoadObject(s)
  199.                 Next
  200.                 Return l
  201.         End If
  202.        
  203.         'Create the object
  204.         o = tid.NewObject()
  205.        
  206.         'Load fields
  207.         For Local f:TField = EachIn tid.EnumFields()
  208.                 If f.MetaData("no_save") Then Continue
  209.                 Local obj:Object = LoadObject(s)
  210.                 If obj Then f.Set o, obj
  211.         Next
  212.        
  213.         Return o
  214.        
  215. End Function


Comments :


Macguffin(Posted 1+ years ago)

 Hey Otus,Thanks for the code!  It works great for me, with one exception - multidimensional arrays.I'm currently bombing out on both custom type 2d arrays, and regular 2d int arrays... not sure what the deal is yet, but wanted to post in case you want to look at it.


Otus(Posted 1+ years ago)

 I don't think reflection supports multidimensional arrays. Could be wrong though. Have you tried arrays of arrays? That's usually the answer to multidim. array problems.


Macguffin(Posted 1+ years ago)

 Good call - it doesn't.  Looks like reflection doesn't return a multidimensional array as a TField.In my case, I wrapped my calls for saving out the 2d arrays with a function that took the 2d array, mapped it onto a 1d array, and then did the reverse when loading it.  My arrays are all equal in their x and y dimensions, so that went pretty easily.  I imagine that if people need this for non-square arrays, you could imbed data into the stream with that info.


markcw(Posted 1+ years ago)

 When trying to compile the module version of this I get an error.Building test_moduleCompiling:objload.bmxCompile Error: Function can not return a valueWhich is in this line but I don't understand what is going on, help?
Code: [Select]
'Compares two fields
Function CompareFieldNames(o1:Object, o2:Object)
Return TField(o1).Name().Compare(TField(o2).Name() ) ' ?
End Function



Otus(Posted 1+ years ago)

 markcw: Sorry, it should be
Code: [Select]
Function CompareFieldNames:Int(o1:Object, o2:Object)No idea how that slipped through... :p


markcw(Posted 1+ years ago)

 Oh great, thanks Otus. I was able to get it compiling by rem'ing them out and replacing the lines where they are called with the code here. Also, the update for TMap and TList is not in the module code. Must have slipped through eh? :) [/i]

 

SimplePortal 2.3.6 © 2008-2014, SimplePortal