Import interdependent types help!

BlitzMax Forums/BlitzMax Beginners Area/Import interdependent types help!

tobykurk(Posted 2015) [#1]
Newbie question,

Trying to organise OO code as suggested, by putting each type in a separate file, and then import them all into main program.

But what about interdependent types that need to include each other?

E.g. I have a TWindow and a TWindowItem. The TWindow needs to import TWindowItem, and also TWindowItem needs to import TWindow.

Compile throws "Duplicate Identifier TWindow"

What to do?


Derron(Posted 2015) [#2]
You will have to create an helper - or "base type" - or you use the wrong approach.

First ask yourself: why does "TWindowItem" need to know about "TWindow" ?


Ok, imagine we have two classes "TPlayer" and "TPlayerSound". TPlayerSound needs to know "TPlayer" because it needs to know the position of the player for correct 3D/positional sound. And "TPlayer" needs to know "TPlayerSound" to be able to have 'sound.Play("footsteps")' or so.

BUT ... you have multiple ways of decoupling things:

a) the base type

file "gameentity.bmx" (does neither need TPlayer nor TPlayerSound)
TGameEntity
  field x:int
  field y:int
  ...
End Type



file "player.bmx"
import "gameentity.bmx"

Type TPlayer Extends TGameEntity
  Field sound:TPlayerSound
  ...
End Type


and file "playersound.bmx"
import "gameentity.bmx"

Type TPlayerSound
  field entity:TGameEntity

  Method Play(key:string)
    local sound:TSoundObject = GetSpecialSound(key)
    sound.playMy3DSound(entity.x, entity.y,...)
  End Method
End Type



b) avoid dependencies at all and create a "Play(key, x, y)" function, using this approach means, that "TPlayerSound" does not need to know about entities/players at all.


c) use "loose coupling" by using "GUID"/"ID"s ... instead of storing "entity:TGameEntity" you store "entityGUID:string". Now of course your "TPlayerSound" does not have direct access to the entity ... but you could have "TPlayerSoundBase" (storing the guid) and "TPlayerSound extends TPlayerSoundBase" which then imports "TPlayer" (and a potential collection/list) which the specific "TPlayer"-class then is able to use.

This approach is needed, if you have a real tight coupling of both types (cross linking).


Why is it bad to store "entity" in TPlayerSound ?
You run into trouble with the GC. Imagine you have
player.sound = playerSound
and
playerSound.entity = player

If you now somehow remove the player (deleting, nulling ...) the reference of this player is still stored in "player.sound". The Garbage Collector now is not able to remove the now-unused PlayerSound, as it is still used by something, which is referenced by "player" (which is marked for deletion - if nobody references it) - and then "playerSound.entity".

At the end the objects wont get deleted as both keep the refcount too high.

You are able to avoid this by calling _manually_ custom Remove()-functions which itself clear all references then (call Remove() of their child properties).


bye
Ron


tobykurk(Posted 2015) [#3]
Thanks Ron,

I guess my question is really implying;

Why to put the types in separate files, when simply keeping them together in one file avoids all the ensuing problems that you describe workaround solutions for? Merely for the sake of tidiness?


H&K(Posted 2015) [#4]
Because untidiness WILL kill your program


Derron(Posted 2015) [#5]
It also allows "reusablity" (your code could be easily used in another programme ... as the dependences are easily satisfied, and you do not need to "add the whole programme").

Smaller apps do not need to have this and could be stored in one big source file - but the GC-problem keeps swinging its hammer-of-mem-leak :-)


bye
Ron