Problems with types and imports

BlitzMax Forums/BlitzMax Programming/Problems with types and imports

Hezkore(Posted 2016) [#1]
I'm trying to reach a type from another type which are all in different bmx files.
I'm only using "import" so the types can't really see each other.

Here's an example.

Main.bmx
SuperStrict
Import "TGame.bmx"
Global MyGame:TGame = New TGame


TGame.bmx
SuperStrict
Import "TGameActions.bmx"

Type TGame
	Global GameActions:TGameActions = New TGameActions
	
	Method Update()
		GameActions.Action(Self)
	EndMethod
EndType


And here comes the tricky part!
Let's say that TGameActions will alter the game, that means TGameActions has to somehow reach TGame.

TGameActions.bmx
SuperStrict

Type TGameActions
	Method Action(game:TGame)
	EndMethod
EndType


Now...
This code won't work, because TGameActions doesn't know what TGame is.
And I can't just throw "Import TGame.bmx" into TGameActions.bmx cause that won't compile.
Importing both TGame and TGameActions from Main.bmx won't work either.

So how will TGameActions alter/reach TGame?


therevills(Posted 2016) [#2]
What about using "Include" instead on "Import", with Include you just add all the files you want to the main file and you dont have do it for the rest of the files.


Hezkore(Posted 2016) [#3]
I'm working on a pretty big game, include is significantly slower. :/


markcw(Posted 2016) [#4]
How to you mean slow? Slow to compile? Why does that matter?

Anyway, there really is no other way to do this in Bmx.


Brucey(Posted 2016) [#5]
What's a TGame? Why does TGameAction need to know about it?

Why not create an abstract class/type with a defined set of methods (or even better an Interface if you are using BlitzMax NG)

You can then put this abstract type in a common place, for everywhere to see, and then create a "concrete"/implementation of the actual TGame elsewhere.
Type TGame
  Method doSomething() abstract
End Type

Type TGameImpl Extends TGame
  Method doSomething()
   ...
  End Method
End Type

Type TGameActions
	Method Action(game:TGame)
           game.doSomething()
	EndMethod
EndType


Just a thought..


Derron(Posted 2016) [#6]
To extend Brucey's hint:

I use this approach:

game.base.bmx:
Type TGameBase
  Field propertyWithoutExternalConnection:int = 1
  Field name:string
  Field ...

  Method GetName:string()
    return name
  End Method

  Method DoSomething()
    print "am not sure what to do"
  End Method
End Type


game.bmx:
Import "game.base.bmx"
Import "gameactions.bmx"

Type TGame extends TGameBase
  Field gameActions:TGameActions[0]

  'override to do something more
  Method GetName:string()
    return usingExternal.name + " " + name
  End Method

  Method DoSomething()
    print "now I might know what do"
  End Method
End Type



gameactions.bmx
Import "game.base.bmx"

Type TGameActions
  Method Action(gameBase:TGameBase)
    'if the given "gameBase" is of type "TGame"
    'it will output "now I might know what to do"
    gameBase.DoSomething()
  End Method
End Type



Brucey's suggestion is like giving a "definition" of the type somewhere (think this is pretty similar to ".h" files), while mine is saying: give the base type already all information which it can hold without referencing other things. It is then no longer a real "base" type but a "basic" implementation already.


So what was I to add to the hint?
You are able to _override_ the returned type of a method/function.
Base: bla:TMyBaseObject()
Implementation: bla:TBaseObject()

This eases the process when just accessing the base properties (it will still return "TBaseObject" if the object is of that type).

Within my code I then try to use the "lowest possible implementation" to lower the dependencies.


Using Brucey's/mine approach you will see a bit more complicated setup (more classes) but compilation times (when doing quick compile) are faster as it is able to reuse already compiled code.


BTW: the problem you are facing is called "circular dependencies" and it is up to you to solve them:
- via base/implementation
- via interfaces (more versatile, like a "programming interface description")
- via "include" (as everything is "visible" during the same time)


If you want to see more examples:
https://github.com/GWRon/TVTower/blob/master/source/game.player.base.bmx
https://github.com/GWRon/TVTower/blob/master/source/game.player.bmx
...
The interesting part there is, that I use some kind of "singleton" for collections of the objects.

Type TPlayerBaseCollection
Type TPlayerCollection extends TPlayerBaseCollection

And then I have two convenience accessors:
function GetPlayerBaseCollection()
function GetPlayerCollection()


Instead of now defining "global playerCollection:TPlayerCollection" I use the "GetInstance:XXX()" function in the types. Within Base, it returns the base variant - and within the normal implementation it returns the normal variant. The important trick is here: I convert the base instance to a normal instance if it isn't done yet.

Of course this trick just is useable if you do not have "base + impVariantA + impVariantB ...".

What benefit do I have of this approach?
Your code does not have two variables containing potentially 2 different instances of a single type.
So this approach makes sure, that as soon as you access "GetPlayerCollection()" all further accesses to "GetPlayerBaseCollection()" are done to a "TPlayerCollection" instance instead of "TPlayerBaseCollection". If you somehow miss to "take over" information (by manually assigning "global playerCollection:TPlayerCollection = new TPlayerCollection") you else would be able to run into trouble.
Using that approach in "Method New()" is not possible without trouble, as you then cannot create new instances for temporal storage. So when using Bruceys' TPersistence.mod with such singletons, you _might_ have problems then.
This is why I do conversion from "Base" to "implementation" during _request_ not during _initialization_.



bye
Ron


Hezkore(Posted 2016) [#7]
Thank you all!

@Brucey
I've tried BMX-NG multiple times, but compile times are even worse with it.
It takes about 15 seconds to compile Digesteroids for Windows x64 in release mode.
While with normal BMax it takes about 3 seconds.
And since compile time is exactly what I'm trying to reduce, BMX-NG sadly isn't a solution here.
I also had those nasty slowdowns with string splitting some time ago. (which I reported)
Plus, I'm a bit of a sucker for Blide heh. :)
So I guess I'm just waiting for it to mature a bit.

The abstract "base" type method is what I'm doing, but it's always felt like a cheat or hack.
But if that's the only way to do it, I guess I'll continue doing it.


dw817(Posted 2016) [#8]
Hezkore, as you should know, you can speed up compilation times by making use of FRAMEWORK and IMPORT.

Also from the main menu, click PROGRAM, BUILD OPTIONS, and make sure that QUICK BUILD is checkmarked.


Hezkore(Posted 2016) [#9]
@dw817
Yeah that's what I'm doing.
But quick build only works with Import (not include) so that's exactly why I made this post. :)


Derron(Posted 2016) [#10]
Use the most current BCC / BMK and it should really improve compilation times.


bye
Ron


markcw(Posted 2016) [#11]
There is one other way to speed up compile times, split your code into modules.


Derron(Posted 2016) [#12]
Modules or imports: it is the same, "imports" are more portable than modules (unzip and compile - vs - unzip, copy to mods, compile).


bye
Ron


Cocopino(Posted 2016) [#13]
@Hezkore

If compile times are getting too high, you might want to set up a more "unit testable" approach, where a unit is a small chunk of your program that can be tested.
Do you really need to compile the entire code + imported modules every time you want to test something new?