Puzzling about Import

BlitzMax Forums/BlitzMax Programming/Puzzling about Import

Adam Novagen(Posted 2015) [#1]
So I was recently reading some threads on Import vs Include; having learned most of my habits from Blitz3D and classic Blitz BASIC (and still in the process of unlearning said habits, I might add), I've still been using Include in my project headers to pull all my other code files in.

Learning the concepts of object-oriented programming has done a great deal of good for me; where once my code would've been an inter-dependent mess, nowadays my list of Includes is simply arranged in alphabetical order, because I no longer initialize anything in mainspace save for a few "essential" Globals which are all in their own file of shame. I thought I was doing pretty well with this, and it's certainly a big step forward from my old practices, but experimenting with Import recently has brought to my attention that I'm still a long way from skilled.

Here's my current ponder: let's say we create a custom sprite type in its own import file, like:

---file sprites.bmx---

Type TSprite
	Field image:TImage
	
	Method draw(x:Int,y:Int,frame:Int)
		DrawImage image,x,y,frame
	End Method
	'... Other properties and stuff, methods, etc
End Type


Now we have a type that holds sprites as images with their own properties, methods for animating and drawing, etc. But now we need a way to use those sprites, i.e. we need some "actors" on the screen, which will call the sprites to be drawn at various locations, frames of animation, etc.

---file actors.bmx---

Type TActor
	Field x:Int,y:Int
	Field frame:Int
	
	Field sprite:TSprite ' The handle of the actual sprite to draw
	
	Method draw()
		sprite.draw(x,y,frame)
	End Method
	' ... Other code and such
End Type


This is where I fall flat with OO and Import. Because Import compiles everything separately before building (correct term I think?), the TSprite type doesn't exist as far as actors.bmx is concerned. Now, if actors.bmx is the only file that uses TSprite, then you could just move all the TSprite code into that file and call it a day. But what if you also want to use TSprite for other things, like map tiles, title logo, HUD elements, and so on?

Obviously you wouldn't want to copy the code into multiple files, so I figure one of two things is happening here: either I'm not thinking the sprite-to-game structure through enough, or there are OO-related ways to access types from imported files that I just don't know about.

This entire post is probably a mess of bad assumptions and miseducated ramblings, anyone care to lend some perspective?


Yasha(Posted 2015) [#2]
You're only missing one minor thing, which is that since Import does something slightly different from Include, you also use it in a slightly different way.

Include is a blind copy and paste, as you already know. Among other things this means that you never normally want to use it twice on the same file (I think if you do use it twice Blitz actually just helpfully ignores the second call, although this limits your options for C-style silliness), since you don't want two copies of the same class definitions pasted into your program's toplevel.

Import, however, doesn't paste anything and doesn't work like a copy-and-paste either. All it does is make the names declared at the toplevel visible in another translation unit (translation unit = "thing being compiled right now", result of pasted-together files or w/e), but the code of that TU stays exactly as it looks in the file as-written. The compiler is simply aware that a given set of names is available, and can be compiled against and referenced from this TU, but it doesn't need to copy the code or connect to it right now; the code behind those names has been previously built in a different TU just needs to be enqueued for use in the main program as well. Import provides information, to the tune of "yes this exists", but has no mechanical effect on the code being generated (beyond enabling it to be generated).

This means: Import can be used to refer to the same file multiple times in the same program, if it's done from separate translation units. This doesn't create multiple copies, but just indicates that multiple units depend on the same piece of code. So Actor can import Sprite, and Program can import Actor and Sprite, and this won't cause a duplicated definition of Sprite in Program, because neither Import statement actually caused any code to be generated: there's only one definition of Sprite off in its own TU, referenced in two other TUs. It will be linked together with the rest of them when the time comes to build a complete program object, now that the compiler knows there are other units that require the named things within it.


Adam Novagen(Posted 2015) [#3]
OH. That makes much more sense; so if I understand this correctly, every file used in a project can (and should) import any files it uses as dependencies, and then the header - rather than being a unified list of every file used in the project - can simply import the top-level files.

So like, for my example, actors.bmx would import sprites.bmx, and map tiles.bmx would also import sprites.bmx. header.bmx would then import both actors.bmx and map tiles.bmx, and finally main.bmx would import (include?) header.bmx. Am I getting this correctly?