Identation (and several other) guildines

BlitzMax Forums/BlitzMax Tutorials/Identation (and several other) guildines

Updated 7/9/2008 4:48 PM (EST)

This is by no means a beginners tutorial, you should have a basic understanding of the language before embarking on this! (Intermediate perhaps?)

One thing I was very slow to catch onto in the early blitz days was indentation. If you want your code to be easily readable by a wide range of people (and to have a 'pro' look), indentation is the way.

This tutorial will also cover SuperStrict'ing and commenting.
NOTE: This tutorial will have code to explain things, but not necessarily run/compile. After all, this is a guidelines tutorial not an asteroids project (no bashing to anyone out there intended!).

SuperStrict: SuperStrict is basically like military training, you best get things right or your going to have many problems in the future (sorry if I offended anyone using that terminology - it seemed to fit). SuperStricting is very good for debugging as well, as you should have less to sift through whilst solving problems.
It requires you to give every variable a type (that may have not made much sense.. Local MyVar:TType it requires the part after ':', if someone would be so kind please post a correct way of describing that), and to define that variable as local or global - defining variables in local/global space are the basis for good memory management.

Commenting: I don't normally use commenting in personal projects (because I don't need it, others with a busy lifestyle might find it better to comment on personal projects), but it is very useful in open source code - it will definitely help answer some of the questions people might ask about your code. Comments before types/functions/methods are all good especially before questionable function calls - to explain why you might of done something the way you did.

If your writing a module be sure to make use of bbdoc (example nabbed from brl.audio, if you need a more in-depth example just look at any of the other modules in your blitzmax\mod\ folder):
Rem
bbdoc: Audio sound type
End Rem
Type TSound
	Rem 
	bbdoc: Play the sound
	returns: An audio channel object
	about:
	Starts a sound playing through an audio channel.
	If no channel is specified, #Play automatically allocates a channel for you.
	End Rem
	Method Play:TChannel( alloced_channel:TChannel=Null )


Indentation.
I use a somewhat personalized form of indentation, you will probably gain one over time, one that suits you best.

First thing to remember: first parts of code are never indented - things like moduleinfo, setup comments, license details, globals/locals imports/framework etc. (a common practice in maxgui apps is to initiate gadgets as global variables before the mainloop).

If using SuperStrict (which you should always be doing), it should be the first line of your program (sometimes license details are put before Strict/SuperStrict - though rarely seen, a good example are some of Brucey's Modules), followed by a module framework and module imports.
Example of the beginning of a typical program (the modules imported and frameworked are just placeholders):
SuperStrict

Framework brl.blitz
Import brl.basic
Import brl.retro


The Framework command is used to specify that you want to only have certain modules included with your program, not all the standard modules (which can bloat your compiled executable size). It is essentially the import command, but does what is described above. Framework should be just after SuperStrict and just before module imports.

The Import command specifies what modules your program will be using (if you fail to import the correct modules your program will crash, don't be too pessimistic, there is an easy way to figure out what your program needs to import: Framework Assistant).

Some programs are built different then others, if small, everything is generally put in one source file, if the project is large your probably going to find it hard to manage under one file. Using multiple files for different types; and for groups of functions is essential for large projects.
To import single source files you can use the Import command just like you would with modules, only give it a path (ie. 'Import "\includes\types\gentypes.bmx"').

The next part of your code is global/local variables and one time initiations, things like graphics width/height, initiating graphics and/or initiating gadgets (pertaining to maxgui).
An example for a graphical program (working example!):
SuperStrict

Framework brl.max2d
Import brl.keycodes
Import brl.glmax2d

'We may need these variables later on, and
'executing GraphicsWidth() And GraphicsHeight()
'everytime you need to know, is a bad habit.
'It is also common for the Graphics command
'to use these values on initiation.
Global GD_Width:Int = 1024, GD_Height:Int = 768 'We can't use GraphicsWidth/Height as our variables because those are function identifiers!

'Set the driver we want to use
SetGraphicsDriver GLMax2DDriver()

'Initiate Graphics!
Graphics GD_Width, GD_Height

'Our main loop
While Not KeyHit(Key_Escape)
   Cls
	
	DrawOval 45, 45, 400, 400
	
   Flip
 Delay 1
Wend
End


The basics of indentation throughout your code.
NOTE: Some parts of this section are very personalized, like using 1-3 spaces for beginning and ending parts of a function/method/type/loop.

Types: Types should start out with no tabulation, globals/consts (ie. a global list for keeping track of all created instances of the type) should be two spaces from the beginning of the type.
Fields should be one tab from the beginning of the type.
Methods and Functions (within a type): Constructor/destructor methods should be two tabs from the beginning of the type
(followed by any other methods) and should come right after the fields. All functions should start with two tabs from the beginning of the type and should be after any methods, with a two line separation from the last method.

Functions (not within a type): Functions should start out with no tabulation (like a type).

While/Repeat/For loops: Code within a loop should not touch the walls of the loop - unless that line is a beginning or ending section of a loop, more on this later.
For Local i:Int = 0 To 100
 If i = 50 Then Print "The variable 'i' has reached 50!"

	callfunction(Var,var2,var3)
	dosomethingelse()
	
Next


Locals (within a function/method): Locals should be two spaces from local tabulation (ie. if your putting locals in a method, you extend from where the method starts on the line).

Concept of beginning and ending sections of a method/function/loop: 'IF' statements at the beginning of a section should be one space from the beginning of the section. Calls like Cls() and Flip() are generally considered beginning and ending functions. Beginning function calls should be three or one space(s) from the beginning of the section, and ending should be three or one as well.

Calling Functions/Methods (from an instance of an a type/an object): When calling functions (from a type) or methods (of an object) you should always use '()' parenthesis at the end (and remember to include variables to pass to the function/method!) of the call, as is custom with OO standards (even if the method/function does not return a value).

Calling Functions: When calling Functions (not from a type, global functions) I generally don't use parenthesis at the end of my calls, but either way is fine.
NOTE:If your function/method returns a value then parenthesis ARE necessary.
NOTE:Writing functions in SuperStrict is similar to declaring variables, however less strict, If your function will not be returning a value you don't need to assign it a type, however if it does you must remember to do that.

Wrap-Up, a full blown example using all the technique's above.
'License: You, and anyone obtaining 
'a copy of this file, are free to 
'distribute, and use this file for 
'any purpose.

SuperStrict

'NOTE: Framework can only be used ONCE!
Framework BRL.Basic
Import BRL.GLMax2d
Import BRL.Win32MaxGUI 'This may need to be maxgui.maxgui, depending on what you have installed.
Import BRL.EventQueue
Import BRL.Timer

'I didn't include this, but you can find it in the original post.
'Import "Types.bmx"


'Set our Graphics Driver.
SetGraphicsDriver GLMax2DDriver()

'Create our Graphics resolution variables - these will be used for creating our canvas later.
Global GD_Width:Int = 1024, GD_Height:Int = 768

'Create our main window.
Global Wnd_Main:TGadget = CreateWindow("Test Window!", 15, 15, GD_Width, GD_Height, Null, WINDOW_TITLEBAR)

'Create our graphics canvas.
Global Cnv_Drawing:TGadget = CreateCanvas(0, 0, GD_Width, GD_Height, Wnd_Main)

'Create our redraw timer, this is local because it will not need to be accessed by functions.
Local Tmr_ReDraw:TTimer = CreateTimer(60) '60 hertz
Repeat
	
	'Wait for an event and parse it
	Select WaitEvent()
		Case EVENT_TIMERTICK
			Select EventSource()
				Case Tmr_ReDraw
					RedrawGadget Cnv_Drawing
					
			End Select
			
		Case EVENT_GADGETPAINT
			Select EventSource()
				Case Cnv_Drawing
					'Keep our loop small (code wise) for read-ability, call other functions instead of clustering!
					RedrawCanvas() 'Again, parenthesis are unecessary in this functions (because it returns nothing), but it doesn't hurt either!
			
			End Select
			
		Case EVENT_WINDOWCLOSE
			Select EventSource()
				Case Wnd_Main
					End
					
			End Select
			
	End Select
	
Forever
End

'Redraw our canvas!
Function RedrawCanvas()
 SetGraphics CanvasGraphics(Cnv_Drawing)
   Cls
	
	DrawOval 10, 10, 150, 200
	
   Flip
End Function

'Depicts local variable tabulation and end/begin sections
Function AddAndMultiply:Int(Var1:Int, Var2:Int)
 If Var1 > Var2 Then Print "Var1 is larger then Var2"

  Local Result:Int
	
	Result = Var1 - Var2 * 2
	
 Return Result
	
End Function


Types.bmx
'Types.bmx

SuperStrict

'Create a dummy type!
Type TCar
  'Create a global list to keep track of instances.
  Global _list:TList = New TList
  
  'Dummy value.
  Const value:Float = 0.7
  
	'What the Car will be named
	Field name:String
	
	'The year the Car was made.
	Field year:Int
	
	'The color of the car, the default value for this field will be 'Red'.
	Field color:String = "Red"
		
		'Override new method.
		Method New()
			'Add the newly created TCar to the list.
			_list.AddLast(Self)
		End Method
		
		'Remove our object instance from the list and nullify it.
		Method Remove()
			'Nullify.
			name = Null ; year = Null ; color = Null
			
		   'Remove from list
		   _list.Remove(Self)
		End Method
		
		'Prints the details the instance contains.
		Method PrintCar()
			Print "Car name: " + name
			Print "Year: " + String(year)
			Print "Color: " + color
		End Method
		
		
		'Function Constructor. Will use defualt color if none is specified.
		Function Create:TCar(name:String, year:Int, color:String = Null)
		  'Create a new object instance.
		  Local tc:TCar = New TCar
			
			'Set name and year.
			tc.name = name
			tc.year = year
			
			 'Set color only if you specified one.
			 If color <> Null Then tc.color = color
			
		   Return tc
			
		End Function
		
End Type

'Wrapper for TCar.Create()
Function CreateCar:TCar(name:String, year:Int, color:String = Null)
	
   'Call internal TCar function.
   Return TCar.Create(name, year, color)
	
End Function


NOTE: Sorry for the pushy-ness of the whole article, I really hate seeing un-indented code on the forums. If everyone would take some time to learn these techniques everything would be a lot easier :)