'good' or 'really really bad' idea?

BlitzMax Forums/BlitzMax Programming/'good' or 'really really bad' idea?

Nate the Great(Posted 2009) [#1]
well I had an idea, here it is, I basicly thought, what if my physics engine was very dynamic, flexible, easy to make addons for, and more oo... so heres how it works, you add whatever vars you need to be 'global' in each instance of a physics world to the fields, then you use the addproperty with pointers to the functions to initalize and update it

Rem
This is the physics world type
EndRem


Rem
	Instructions for adding/removing custom properties to the physicsworld type:
	1. add a field with all variables and lists your type will need.
	2. After you create a physicsworld (assuming you call your world myworld:physicsworld) call the myworld.addperoperty( function pointer )
		with a function pointer pointing to the update function of your new "property"  This function must have a single parameter as follows
		myfunction(p:physicsworld)
		The physics world type will call the function passing itself as a parameter giving that function access to the lists and variables of that world
	3. Now all you have to do is call myworld.update() and it will call all of your update functions.  Call myworld.init and it will call all of your
		initiation functions you set using the myworld.addpropertyinit( function pointer )  This function must also have a single parameter of p:physicsworld
EndRem


Private

Global fplist:TList = New TList
Function getfp:functionptr(func(p:physicsworld))
	For Local p:functionptr = EachIn fplist
		If p.func = func Then
			Return P:functionptr
		EndIf
	Next
End Function


Public

Type PhysicsWorld
	Global PWorldList:TList = New TList	'list of all physics worlds
	
	Field Width#,Height#				'width and height of the physics world.
	Field VerletList:TList				'verlets in the physics world
	Field ConstraintList:TList			'constraints in the physics world
	Field ForceList:TList				'forces in the physics world
	Field PhysicsBodies:TList			'physics bodies in the physics world
	Field gravityx:Float				'gravity x velocity
	Field gravityy:Float				'and gravity y velocity
	
	Field PropertiesInit:TList		'list of functions that initiate the properties
	Field Properties:TList			'list of functions called every physics update.
	
	Function Create:PhysicsWorld(Width#,Height#,Property_Flag:Int = 1)
		
		Local p:physicsworld = New physicsworld
		
		p.width = width
		p.height = height
		
		p.propertiesInit:TList = New TList
		p.properties:TList = New TList
		
		pworldlist.addlast(p:physicsworld)
		
		Return p:physicsworld
	End Function
	
	Method AddProperty(prop(p:physicsworld))
		Local f:functionptr = New functionptr
		f.func = prop
		fplist.addlast(f:functionptr)
		properties.addlast(f:functionptr)
	End Method
	Method RemoveProperty(prop(p:physicsworld))
		
		properties.remove(getfp:functionptr(prop))
		
	End Method
	Method AddPropertyInit(prop(p:physicsworld))
		Local f:functionptr = New functionptr
		f.func = prop
		fplist.addlast(f:functionptr)
		propertiesinit.addlast(f:functionptr)
	End Method
	Method RemovePropertyInit(prop(p:physicsworld))
		
		propertiesinit.remove(getfp:functionptr(prop))
		
	End Method
	
	Method Update()
		For Local p:functionptr = EachIn properties
			p.func(Self)
		Next
	End Method
		
	Method Init()
		For Local p:functionptr = EachIn propertiesinit
			p.func(Self)
		Next
	End Method
End Type


Type FunctionPtr
	Field func(p:physicsworld)
End Type



idk if this is the best idea ive had or a bad idea doomed to failure. anyone tried this approach before?


Evil Roy Ferguson(Posted 2009) [#2]
You're looking at a variation of the entirely legitimate Strategy pattern. It's not a stupid idea, and in fact is very probably a good one for what you're doing -- although I think that "Property" is a pretty vague name for it.

I would recommend the use of functors / function objects over function pointers here -- something like:

Type IPhysicsFunctor
    Method Apply(physics:PhysicsWorld) Abstract
End Type

Type FunctionPtr Extends IPhysicsFunctor
    Field func(p:PhysicsWorld)

    Method Apply(physics:PhysicsWorld)
        func(physics)
    End Method
End Type


and then using p.Apply(Self) instead of p.func(Self). The reason for this is that then you can do something like this:

Type MoveAllBy Extends IPhysicsFunctor    
    Field xSpeed:Float, ySpeed:Float

    Method Apply(physics:PhysicsWorld)
        ' move everything by xSpeed and ySpeed
    End Method
End Type


That is to say, your functors can encapsulate additional information (here, x/y speed) that a simple function pointer cannot (without extra work).

You may want to consider allowing individual objects to have update strategies, as well.


Nate the Great(Posted 2009) [#3]
hmm I dont exactly get what you are saying but it sounds like a better idea than mine... what exactly is a functor? lol


Evil Roy Ferguson(Posted 2009) [#4]
It's basically just a type that you use for one of its methods, in lieu of a function pointer.

Here we have a functor type that takes a string and returns a string:

' Basic functor
Type IFunctorWithString
    Method Apply:String(s:String) Abstract
End Type


And here are some concrete implementations:
' Upper functor
Type UpperFunctor Extends IFunctorWithString
    Method Apply:String(s:String)
        Return Upper(s)
    End Method
End Type

' Lower functor
Type LowerFunctor Extends IFunctorWithString
    Method Apply:String(s:String)
        Return Lower(s)
    End Method
End Type

' This function tests a functor
Function TestFunctor(func:IFunctorWithString)
    Local s:String = "Hello world!"
    Print func.Apply(s)
End Function

TestFunctor(New UpperFunctor)
TestFunctor(New LowerFunctor)


The advantage over just a function pointer is that it can encapsulate additional information:

' This functor appends the value of 'toAppend' to the given string.
Type AppendFunctor Extends IFunctorWithString
    ' We couldn't include this as easily with a function pointer.
    Field toAppend:String

    Method Apply:String(s:String)
        Return s + toAppend
    End Method
End Type

Local appendCapybara:AppendFunctor = New AppendFunctor
appendCapybara.toAppend = "Capybara"

TestFunctor(appendCapybara)




Nate the Great(Posted 2009) [#5]
thanks but if I want my users to be able to 'set and forget' so to speak how would this functor structure allow them to do that?

Is it possible to look through all extended types?

I think I might go with what I originally planned because I dont quite get functors lol


slenkar(Posted 2009) [#6]
its basically an abstract method


Evil Roy Ferguson(Posted 2009) [#7]
There is an abstract method but that's definitely not the main point to pull out of that. The functor structure would allow set and forget -- something like passing in a New AppendFunctor.WithString("bob") instead of a pointer to AppendStringBob.

That having been said, the function pointer route is still perfectly fine and a good idea if you don't want to overcomplicate things at the moment. :)


N(Posted 2009) [#8]
What I'd give for closures...