[bmx] Program settings with reflection by JoshK [ 1+ years ago ]

Started by BlitzBot, June 29, 2017, 00:28:43

Previous topic - Next topic

BlitzBot

Title : Program settings with reflection
Author : JoshK
Posted : 1+ years ago

Description : This code will handle settings loading and saving automatically, with a nice interface for accessing the values in your program.

You can add fields to the TSettings type, or create an extended type with your fields.

The following types are supported:
float
double
int
string
int array
float array
double array

I didn't bother with string arrays, but it will probably work.

Here is what the saved settings file looks like:
Code: BASIC
greeting="Hello"
color=255,128,64


Code :
Code: blitzmax
SuperStrict

Import brl.reflection
Import brl.stream

Type TSettings
	
	Field greeting:String="Hello"
	Field color:Int[]=[255,128,64]
	
	Method Save:Int(url:Object)
		Local typeid:TTypeId
		Local f:TField
		Local stream:TStream
		Local o:Object
		Local otypeid:TTypeId
		Local etypeid:TTypeId
		Local n:Int
		Local element:Object
		Local value:String
		
		stream=WriteStream(url)
		If Not stream Return False
		
		typeid=TTypeId.ForObject(Self)
		
		For f=EachIn typeid.enumfields()
			
			Select f._typeid
			
			Case IntTypeId
				stream.WriteLine f.name()+"="+f.GetInt(Self)
			
			Case FloatTypeId,DoubleTypeId
				stream.WriteLine f.name()+"="+f.GetFloat(Self)
			
			Case StringTypeId
				stream.WriteLine f.name()+"=~q"+f.getstring(Self)+"~q"
			
			Default
				If f._typeid.Extendstype(ArrayTypeId)
					o=f.Get(Self)
					otypeid=TTypeId.ForObject(o)
					value=""
					For n=0 To otypeid.ArrayLength(o)-1
						element=otypeid.GetArrayElement(o,n)
						If n>0 value:+","
						value:+String(element)	
					Next
					stream.WriteLine f.name()+"="+value
				EndIf
			EndSelect
			
		Next
		stream.close()
		
		Return True
	EndMethod
	
	Method Load:Int(url:Object)
		Local map:TMap=New TMap
		Local p:Int
		Local key:String
		Local value:String
		Local typeid:TTypeId
		Local stream:TStream
		Local s:String
		Local f:TField
		Local fieldid:TTypeId
		Local o:Object
		Local otypeid:TTypeId
		Local sarr:String[]
		Local n:Int
		
		stream=ReadStream(url)
		If Not stream Return False
		
		'Load pairs into map
		While Not stream.Eof()
			s=stream.ReadLine()
			p=s.Find("=")
			If p>-1
				key=s[..p]
				value=s[p+1..]
				map.insert key,value
			EndIf
		Wend
		stream.close()
		
		typeid=TTypeId.ForObject(Self)
		For f=EachIn typeid.enumfields()
			key=f.name()
			If map.contains(key)
				value=String(map.valueforkey(key))
				
				Select f._typeId
				
				Case StringTypeId
					value=value[1..]
					value=value[..value.length-1]
					f.setstring(Self,value)
				
				Case FloatTypeId
					f.setstring(Self,Float(value))
				
				Case DoubleTypeId
					f.setstring(Self,Double(value))
				
				Case IntTypeId
					f.setstring(Self,Int(value))
				
				Default
					If f._typeid.Extendstype(ArrayTypeId)
						o=f.Get(Self)
						otypeid=TTypeId.ForObject(o)
						sarr=value.split(",")
						sarr=sarr[..otypeid.ArrayLength(o)]
						For n=0 To otypeid.ArrayLength(o)-1
							otypeid.SetArrayElement(o,n,sarr[n])
						Next
					EndIf
				
				EndSelect
			EndIf
		Next
		
		Return True
	EndMethod
	
EndType

Local settings:TSettings=New TSettings
settings.Save("settings.cfg")
settings.color=[0,0,0]
settings.Load("settings.cfg")
Print settings.greeting
Print settings.color[0]+", "+settings.color[1]+", "+settings.color[2]
End


Comments :


JoshK(Posted 1+ years ago)

 And this version will handle string arrays with quotes:
Code: BASIC
SuperStrict

Import brl.reflection
Import brl.stream

Type TSettings
	
	Method Save:Int(url:Object)
		Local typeid:TTypeId
		Local f:TField
		Local stream:TStream
		Local o:Object
		Local otypeid:TTypeId
		Local etypeid:TTypeId
		Local n:Int
		Local element:Object
		Local value:String
		
		stream=WriteStream(url)
		If Not stream Return False
		
		typeid=TTypeId.ForObject(Self)
		
		For f=EachIn typeid.enumfields()
			
			Select f._typeid
			
			Case IntTypeId
				stream.WriteLine f.name()+"="+f.GetInt(Self)
			
			Case FloatTypeId,DoubleTypeId
				stream.WriteLine f.name()+"="+f.GetFloat(Self)
			
			Case StringTypeId
				stream.WriteLine f.name()+"=~q"+f.getstring(Self)+"~q"
			
			Default
				If f._typeid.Extendstype(ArrayTypeId)
					o=f.Get(Self)
					otypeid=TTypeId.ForObject(o)
					value=""
					For n=0 To otypeid.ArrayLength(o)-1
						element=otypeid.GetArrayElement(o,n)
						etypeid=otypeid.ElementType()
						If n>0 value:+","
						Select etypeid
						Case StringTypeId
							value:+"~q"+String(element)+"~q"
						Default
							value:+String(element)
						EndSelect
					Next
					stream.WriteLine f.name()+"="+value
				EndIf
			EndSelect
			
		Next
		stream.close()
		
		Return True
	EndMethod
	
	Method Load:Int(url:Object)
		Local map:TMap=New TMap
		Local p:Int
		Local key:String
		Local value:String
		Local typeid:TTypeId
		Local stream:TStream
		Local s:String
		Local f:TField
		Local fieldid:TTypeId
		Local o:Object
		Local otypeid:TTypeId
		Local etypeid:TTypeId
		Local sarr:String[]
		Local n:Int
		
		stream=ReadStream(url)
		If Not stream Return False
		
		'Load pairs into map
		While Not stream.Eof()
			s=stream.ReadLine()
			p=s.Find("=")
			If p>-1
				key=s[..p]
				value=s[p+1..]
				map.insert key,value
			EndIf
		Wend
		stream.close()
		
		typeid=TTypeId.ForObject(Self)
		For f=EachIn typeid.enumfields()
			key=f.name()
			If map.contains(key)
				value=String(map.valueforkey(key))
				
				Select f._typeId
				
				Case StringTypeId
					value=value[1..]
					value=value[..value.length-1]
					f.setstring(Self,value)
				
				Case FloatTypeId
					f.setstring(Self,Float(value))
				
				Case DoubleTypeId
					f.setstring(Self,Double(value))
				
				Case IntTypeId
					f.setstring(Self,Int(value))
				
				Default
					If f._typeid.Extendstype(ArrayTypeId)
						o=f.Get(Self)
						otypeid=TTypeId.ForObject(o)
						sarr=value.split(",")
						sarr=sarr[..otypeid.ArrayLength(o)]
						etypeid=otypeid.ElementType()
						For n=0 To otypeid.ArrayLength(o)-1
							If etypeid=StringTypeId
								sarr[n]=sarr[n][1..]
								sarr[n]=sarr[n][..sarr[n].length-1]
							EndIf
							otypeid.SetArrayElement(o,n,sarr[n])
						Next
					EndIf
				
				EndSelect
			EndIf
		Next
		
		Return True
	EndMethod
	
EndType



JoshK(Posted 1+ years ago)

 This version has a Copy() method that will work on extended types, without adding a new method:
Code: BASIC
SuperStrict

Import brl.reflection
Import brl.stream

Type TSettings
	
	Method Save:Int(url:Object)
		Local typeid:TTypeId
		Local f:TField
		Local stream:TStream
		Local o:Object
		Local otypeid:TTypeId
		Local etypeid:TTypeId
		Local n:Int
		Local element:Object
		Local value:String
		
		stream=WriteStream(url)
		If Not stream Return False
		
		typeid=TTypeId.ForObject(Self)
		
		For f=EachIn typeid.enumfields()
			
			Select f._typeid
			
			Case IntTypeId
				stream.WriteLine f.name()+"="+f.GetInt(Self)
			
			Case FloatTypeId,DoubleTypeId
				stream.WriteLine f.name()+"="+f.GetFloat(Self)
			
			Case StringTypeId
				stream.WriteLine f.name()+"=~q"+f.getstring(Self)+"~q"
			
			Default
				If f._typeid.Extendstype(ArrayTypeId)
					o=f.Get(Self)
					otypeid=TTypeId.ForObject(o)
					value=""
					For n=0 To otypeid.ArrayLength(o)-1
						element=otypeid.GetArrayElement(o,n)
						etypeid=otypeid.ElementType()
						If n>0 value:+","
						Select etypeid
						Case StringTypeId
							value:+"~q"+String(element)+"~q"
						Default
							value:+String(element)
						EndSelect
					Next
					stream.WriteLine f.name()+"="+value
				EndIf
			EndSelect
			
		Next
		stream.close()
		
		Return True
	EndMethod
	
	Method Copy:TSettings()
		Local settings:TSettings
		Local bank:TBank=CreateBank()
		Local stream:TStream=CreateBankStream(bank)
		Local typeid:TTypeId=TTypeId.ForObject(Self)
		
		Save(stream)
		stream=CreateBankStream(bank)
		
		settings=TSettings(typeid.NewObject())
		settings.Load(stream)
		
		Return settings		
	EndMethod
	
	Method Load:Int(url:Object)
		Local map:TMap=New TMap
		Local p:Int
		Local key:String
		Local value:String
		Local typeid:TTypeId
		Local stream:TStream
		Local s:String
		Local f:TField
		Local fieldid:TTypeId
		Local o:Object
		Local otypeid:TTypeId
		Local etypeid:TTypeId
		Local sarr:String[]
		Local n:Int
		
		stream=ReadStream(url)
		If Not stream Return False
		
		'Load pairs into map
		While Not stream.Eof()
			s=stream.ReadLine()
			p=s.Find("=")
			If p>-1
				key=s[..p]
				value=s[p+1..]
				map.insert key,value
			EndIf
		Wend
		stream.close()
		
		typeid=TTypeId.ForObject(Self)
		For f=EachIn typeid.enumfields()
			key=f.name()
			If map.contains(key)
				value=String(map.valueforkey(key))
				
				Select f._typeId
				
				Case StringTypeId
					value=value[1..]
					value=value[..value.length-1]
					f.setstring(Self,value)
				
				Case FloatTypeId
					f.setstring(Self,Float(value))
				
				Case DoubleTypeId
					f.setstring(Self,Double(value))
				
				Case IntTypeId
					f.setstring(Self,Int(value))
				
				Default
					If f._typeid.Extendstype(ArrayTypeId)
						o=f.Get(Self)
						otypeid=TTypeId.ForObject(o)
						sarr=value.split(",")
						sarr=sarr[..otypeid.ArrayLength(o)]
						etypeid=otypeid.ElementType()
						For n=0 To otypeid.ArrayLength(o)-1
							If etypeid=StringTypeId
								sarr[n]=sarr[n][1..]
								sarr[n]=sarr[n][..sarr[n].length-1]
							EndIf
							otypeid.SetArrayElement(o,n,sarr[n])
						Next
					EndIf
				
				EndSelect
			EndIf
		Next
		
		Return True
	EndMethod
	
EndType