Blitz:Procedures

From Amiga Coding
Jump to: navigation, search

Blitz Basic / AmiBlitz supports the concept of procedures, which are like separate "modules" in your code that can be treated like statements or functions. In effect, this allows you to create your very own commands for situations where suitable commands don't exist in the standard Blitz libraries. Using procedures and functions helps keep your code tidy and easy to read, which in turn helps to avoid introducing bugs into your code.


Using Procedures

Two types of procedures are available: Statements and Functions. Similarly to the built-in statements and functions, these are more or less the same except that statements don't return a value whereas functions do. They can be used anywhere in you code *after* their definition's position in the source, so it's generally a good idea to have them near the top. Using procedures is similar to using built-in commands, however there are a couple of differences:

  • Procedures always need curly brackets around their arguments, for example:
DrawMySprite{x,y}
  • Procedures must have the curly brackets after their name, even if there are no arguments. For example:
ClearBoard{}

Declaring Statements

To create a statement, the Statement...End Statement commands are used, with the statement code in between. Curly brackets are required after the Statement, even if arguments are not required. For example:

Statement ShowMyMessage{}
  NPrint "This is my first statement!"
End Statement

This statement will show the message every time it is called in your code. Up to 6 arguments can be added in the curly brackets, separated by commas:

Statement PrintWelcome{name$, messagecount}
  NPrint "Welcome ", name$
  NPrint "You have ", messagecount, " message(s) waiting"
End Statement
Note: It is important that the data types of the arguments passed match what the code in your statement expects. To enforce proper variable type matching, use
Syntax 2
near the top of your code.


Declaring Functions

Functions are declared similarly to statements, however they require an extra line of code in order to provide the return value. The command
Function Return <i>value</i>
is used to exit the function and return the value. Value can be any primitive type of information, and can be a constant or a variable. For example:
Function AddUp{a, b}
  result = a + b
  Function Return result
End Function

This function will return the sum of the two numbers provided as arguments when it is called.

Note: It is important to ensure that the data type that is returned by the function is what is expected by your code. Returning a string is going to cause problems if your main code tries to use the result in a maths operation! To enforce proper variable type matching, use
Syntax 2
near the top of your code.


Calling Procedures

Procedures are called similarly to the built-in commands. To call a procedure, just enter it as you would a built-in command, remembering to include the curly brackets after the procedure name. To call the example statement above:

Print Welcome{"Steve", 3}

This will produce the following output:

Welcome Steve
You have 3 message(s) waiting

Function procedures, like the built-in procedures, need something to do with their return value. To use the example above:

NPrint "The sum of 3 and 5 is ", AddUp{3, 5}

This will produce the following output:

The sum of 3 and 5 is 8

Local Variables

Procedures behave like separate individual little programs, which involves the concept of "local variables". Variables used within a procedure are used only in that procedure. This means that variables used inside a procedure can't be accessed by another procedure or the main body of the program, and vice versa. This is very useful in many situations, but can also lead to bugs if you're not careful. For example:

Statement SetValue{argument1}
  MyValue = argument1
  NPrint "The value is ", MyValue
End Statement

; Main program here
SetValue{25}
NPrint "The value is ", MyValue

This code will actually print:

The value is 25
The value is 0
and not both 25 as might be expected. This is because the variable MyValue inside the procedure is local to that procedure only, and in the main program it hasn't been initialized yet so it defaults to 0. As you can imagine, this can easily lead to bugs unless you're careful. One way to help is to enter the compiler directive
Syntax 2
near the start of your program. This will then give you errors when you try to use an uninitialized variable, and so will cause an error when you try to compile the example above.


Sharing a Variable

While having all variables kept local is ideal for most circumstances, sometimes it would be simpler to just use the same variable inside and outside a procedure. Two ways of doing this are possible, both using the SHARED statement.

Global Variables

The first method is to declare a variable as global in your program. This means it can be accessed by every procedure in your code, as well as the main program itself. To do this, use the SHARED statement outside any procedures, and before the variable is first used by your code. Note that this method is not supported by Blitz 2.1. For example:

SHARED MyStatus

Statement SetMyStatus{arg1}
  MyStatus = arg1
End Statement

SetMyStatus{10}
NPrint "Status code is ", MyStatus

This example will display

Status code is 10

since the MyStatus variable was declared as global before the procedure that uses it.

Locally Shared Variables

The second method for accessing variables from your main code in your procedures is to use the SHARED statement in the procedure itself. This will make the variable available to that procedure, but any other procedures without a similar SHARED statement won't be able to access it. For example:

MyStatus = 10

Statement ShowMyStatus1{}
  Nprint "The status code is ", MyStatus
End Statement

Statement ShowMyStatus2{}
  SHARED MyStatus
  Nprint "The status code is ", MyStatus
End Statement

ShowMyStatus1{}
ShowMyStatus2{}

Will display:

The status code is 0
The status code is 10

In this case, only the second procedure is able to access the variable MyStatus. The first procedure does not have access so creates a local variable MyStatus, which hasn't been initialized and so defaults to zero.

Procedures in AmiBlitz

AmiBlitz adds some extra capabilities to statements and functions over the existing Blitz features above. These are the ability to use a variable number of parameters, an increased limit on the number of parameters possible from 6 to 10, and the ability to declare special "fast" procedures.

Variable Parameter Count

Procedures under AmiBlitz can have optional parameters defined in their declaration. Optional parameters are identified by placing an @ character directly before the variable name. Once a parameter is made optional, all further parameters to the right must also be optional. For example:

Function CalculatePosition{x1.w, y1.w, @x2.w, @y2.w}

In this example, x1 and y1 are mandatory, whereas x2 and y2 are optional. Values must be given for all parameters up to the last optional parameter required - otherwise your procedure would not be able to tell which optional parameter should go in which variable. So in the above example, if you want to provide parameter y2, you must provide x2 as well, although you can provide x2 and leave out y2 if you like.

Any parameters that are left out when calling your function will be given the value -1, so:

valid = CalculatePosition{22, 45, 23}

Will set x1 to 22, y1 to 45, x2 to 23 and y2 to -1. Left out string parameters will be set to an empty string ("").

Extra Parameters

Under AmiBlitz, up to 10 parameters may be passed to procedures, instead of the older Blitz Basic limit of 6. No special steps need to be taken to do this, just add the parameters as required in the procedure definition.

The original limit of 6 parameters is due to there being 8 data registers available on the 68k CPU - two of these are reserved for other uses so 6 are available to pass parameters to the procedure. AmiBlitz places the extra 4 parameters on the stack instead and reads them in as necessary. This will incur a very slight speed penalty but is probably only noticeable if the procedure is called hundreds of times a second.

Fast Procedures

Procedures can be declared as a "fast" type by using the keyword FAST in their declaration:

 Function FAST Calculate{param1.l, param2.l}

This results in a procedure that has reduced initialization code and is therefore faster to execute, but has some limitations as a result:

  • Variables are not initialised when the function is called, so any local variables should be set to 0 if needed.
  • NewTypes and strings are not allowed inside the function, so only numerical primitive types can be used.

The difference in execution time is quite small, but may be useful for procedures called many times a second.