Difference between revisions of "Best coding practices"

From AutoIt Wiki
Jump to navigation Jump to search
m
m (Merged Magic Numbers into this article.)
Line 358: Line 358:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 +
== Magic Numbers ==
 +
 +
Magic numbers are arbitrary numbers interspersed throughout a program's source code which do not have an associated identifier.  The downside to this is not being able to derive a meaning from the number.
 +
 +
For example:
 +
 +
<syntaxhighlight lang="autoit">
 +
MsgBox(262144, "Magic Numbers", "It's Adventure Time!")
 +
</syntaxhighlight>
 +
 +
In this example, the magic number is 262144 with the identifier being $MB_TOPMOST according to the helpfile.
  
 
More coming soon :)
 
More coming soon :)

Revision as of 14:25, 19 August 2013

Outlined in this section is a detailed explanation of what is to be considered the best coding practices within AutoIt.

Names of Variables

The Hungarian notation is adopted however it's simplified and regroup all the types of numbers in one type.

prefix covering type initialization
i Numbers (any type) $i = 0
a Arrays $a = 0 or $a[0]
s Strings (chars included) $s = ""
f Booleans $f = False or $f = True
b Binaries $b = ""
h Handles (and GUI handles) $h = 0
k (not decided) Keywords Null (not decided)
fu (not decided) Functions Null (not decided)
p Pointers $p = 0
tag Structures definition $tag = "" (should directly be filled)
t Structures $t = 0
o Objects $o = 0
v Various $v = 0 or $v = "" (or $v = Null)


The variables are named following this schema:

type (lower case) [optional] subtype (lower case) var name (first letter in upper case)
i f MyFlag

Example:

; Assign a Local variable the number 5.
Local $iSomeVar = 5

; Assign a Local variable the number 1.
Local $ifMyFlag = 1

; Assign a Local variable an array of numbers.
Local $aiArray = 0

; Re-declare the array to size and fill it.
Local $aiArray[3] = [0, 0.25, 3 / 4]


Always initialize a variable on it's declaration, and declare all the same type of variable on the same line.

Example:

;Bad
Local $iSomeVar1, $aiArray = 0
Local $iSomeVar2 = 5

;Good
Local $iSomeVar1 = 0, $iSomeVar2 = 5
Local $aiArray = 0

You can still categorize your variables and declare for example the $iSomeVar2 on another line.

Example2:

#include <Constants.au3>

Local $vValue ; Bad, this is initially blank.
Local $vVal = 0 ; Good

MsgBox($MB_SYSTEMMODAL, '', '$vValue: ' & $vValue & @CRLF & '$vVal: ' & $vVal)

; In C++ $vValue would display the previous value that was residing in the memory address $vValue points to. AutoIt
; is a little less forgiving in that it initialises the variable as being blank and doesn't throw any error, unlike some moder C++ compilers.


Scopes of Variables

To make the transition, the variables are also named according to their scope.

Global UDF variable Global variable Local variable
$__iSomeVar or $g__iSomeVar $_iSomeVar or $g_iSomeVar $iSomeVar

With this method, you will avoid non wanted re-assignments.


Example:

#include <MessageBoxConstants.au3>

; Assign a Global variable the number 0.
Global $iSomeVar1 = 0
; Assign a Global variable the number 5.
Global $_iSomeVar2 = 5

_SomeFunc()

Func _SomeFunc()
	; Assign Local variables respectively the numbers 3 and 4.
	Local $iSomeVar1 = 3, $iSomeVar2 = 4

	; Note: The user inadvertently re-assigned the global variable $iSomeVar1, because this one is not named as "global".

	; Display the value of $iSomeVar1.
	MsgBox($MB_SYSTEMMODAL, "", "Value of $iSomeVar1: " & $iSomeVar1)

	; Display the value of $iSomeVar2.
	MsgBox($MB_SYSTEMMODAL, "", "Value of $iSomeVar2: " & $iSomeVar2)

	; Display the value of $_iSomeVar2.
	MsgBox($MB_SYSTEMMODAL, "", "Value of $_iSomeVar2: " & $_iSomeVar2)
EndFunc


  • A variable declared globally (with the Global keyword) is visible anywhere in the script.

Always declare your global variables in the global scope, not in the functions. It will prevent another function to use it before its declaration and the declaration is implicit (see examples).

  • A variable declared locally (with the Local keyword), has a visibility which depends of the scope where it's declared.
Declaration in the global scope: the variable is nonetheless visible everywhere; declare it as Local if this one is only used in the same scope.
Declaration in a function: the variable is visible by the function itself and nowhere else.


Structure of a code scope:

; Global scope.

; Include the Constants file, it contains various constants; it's needed here for the $MB_SYSTEMMODAL flag of the MsgBox function).
#include <MessageBoxConstants.au3>

; This scope is either Global or Local, depending on where do you use the variables.

; Assign a Global variable the number 0 (which corresponds to an initialization of a variable number), its scope is Global because it's used at least in one function.
Global $_iVar1 = 0

; Assign a Local variable the string "foo", its scope is Local because it's use is restricted to this scope.
Local $_sVar2 = "foo"

; Display the content of $_sVar2
MsgBox($MB_SYSTEMMODAL, "", "Value of $sVar2: " & $_sVar2)

; Re-assign a Local variable the string returned by the function _MyFunc1.
$_sVar2 = _MyFunc1()

; Re-Display the content of $_sVar2
MsgBox($MB_SYSTEMMODAL, "", "Value of $sVar2: " & $_sVar2)

; Declare a function (its main utility is described later in Functions, we can see one here which is to create a Local scope).
Func _MyFunc1()
	; Local scope.

	; Display the content of $_iVar1.
	MsgBox($MB_SYSTEMMODAL, "", "Value of $_iVar1: " & $_iVar1)

	; Assign a Local variable the string "bar", its scope is Local because it's use is restricted to the function's scope.
	Local $sVar3 = "bar"

	; Display the content of $sVar3.
	MsgBox($MB_SYSTEMMODAL, "", "Value of $sVar3: " & $sVar3)

	; Return the $sVar3 content, it will be visible (if used) to the scope where the function is called.
	Return $sVar3
EndFunc


Concerning the Dim keyword, its recommended usage is limited to empty an existing array:

; Include the Array UDF, it's needed here for the _ArrayDisplay function.
#include <Array.au3>
 
; Assign a Local variable an array containing numbers with a size of 5.
; Note than an array is based 0 index, to access the first element the code is: $aiArray[0].
Local $aArray[5] = [1, 2, 3, 4, 5]
 
; Display the contents.
_ArrayDisplay($aArray)
 
; Empty the array (and keep its size).
Dim $aArray[5]
 
; Display the contents.
_ArrayDisplay($aArray)

Remark: The variable type of the emptied array is a string, every variable non initialized is a string.


And for the ReDim keyword, limit its use to resize an array you want to keep its content:

; Include the Array UDF, it's needed here for the _ArrayDisplay function.
#include <Array.au3>
 
; Assign a Local variable an array containing numbers with a size of 5.
; Note than an array is based 0 index, to access the first element the code is: $aiArray[0].
Local $aArray[5] = [1, 2, 3, 4, 5]
 
; Display the contents.
_ArrayDisplay($aArray)
 
; Resize the array (and keep its content).
Dim $aArray[3]
 
; Display the contents.
_ArrayDisplay($aArray)


Why using Dim over Local/Global is not always a good option:

#include <MessageBoxConstants.au3>
 
Dim $vVariableThatIsGlobal = "This is a variable that has ""Program Scope"" aka Global."
 
MsgBox($MB_SYSTEMMODAL, "", "An example of why Dim can cause more problems than solve them.")
 
Example()
 
Func Example()
	MsgBox($MB_SYSTEMMODAL, "", $vVariableThatIsGlobal) ; That looks alright to me as it displays the following text: This is a variable that has "Program Scope" aka Global.
 
	Local $vReturn = SomeFunc() ; Call some random function.
 
	MsgBox($MB_SYSTEMMODAL, $vReturn, $vVariableThatIsGlobal) ; The Global variable ($vVariableThatIsGlobal) changed because I totally forgot I had a duplicate variable name in "SomeFunc".
EndFunc   ;==>Example
 
Func SomeFunc()
	; This should create a variable in Local scope if the variable name doesn"t already exist.
	; For argument sake I totally forgot that I declared a variable already with the same name.
	; Well I only want this to be changed in the function and not the variable at the top of the script.
	; Should be OK right? Think again.
	Dim $vVariableThatIsGlobal = ""
 
	For $i = 1 To 10
		$vVariableThatIsGlobal &= $i ; This will return 12345678910 totally wiping the previous contents of $vVariableThatIsGlobal.
	Next
	Return $vVariableThatIsGlobal
EndFunc   ;==>SomeFunc


Declaring Global variables in a Function is never a good idea:

#include <MessageBoxConstants.au3>

; Calling Example() first will initialise the Global variable $vVariableThatIsGlobal and therefore calling SomeFunc() won't return an error.
; Now look at Example 2.
Example()
 
Func Example()
	; Declaring a variable in a function can cause serious problems, hence why all Global variables should be declared at the top of a script.
	Global $vVariableThatIsGlobal = 'This is a variable that has ''File Scope'' aka Global.'
	SomeFunc()
EndFunc   ;==>Example
 
Func SomeFunc()
	MsgBox($MB_SYSTEMMODAL, '', $vVariableThatIsGlobal) ; As the variable was initialised this will not return an error.
EndFunc   ;==>SomeFunc

Example 2:

#include <MessageBoxConstants.au3>

; Calling SomeFunc() first will bypass the Global variable $vVariableThatIsGlobal being initialised and therefore AutoIt has no idea of what data the variable
; $vVariableThatIsGlobal contains.
SomeFunc()
 
Func Example()
	; Declaring a variable in a function can cause serious problems, hence why all Global variables should be declared at the top of a script.
	Global $vVariableThatIsGlobal = 'This is a variable that has ''File Scope'' aka Global.'
	SomeFunc()
EndFunc   ;==>Example
 
Func SomeFunc()
	MsgBox($MB_SYSTEMMODAL, '', $vVariableThatIsGlobal) ; As the variable wasn't initialised this will return an error of "variable used without being declared."
EndFunc   ;==>SomeFunc


Declaring variables in loops (For, While, Do etc..) can have an affect on efficiency:

#include <MessageBoxConstants.au3>

; Declaring variables inside loops should be avoided as the variable is re-declared on each repetition.
For $i = 1 To 10 ; $i is in 'loop scope.'
	Local $iInt = $i
Next
MsgBox($MB_SYSTEMMODAL, '', $iInt) ; This will display 10.
#include <MessageBoxConstants.au3>

; Declaring variables outside of loops is more efficent in the long run.
Local $iInt = 0
For $i = 1 To 10 ; $i is in 'loop scope.'
	$iInt = $i
Next
MsgBox($MB_SYSTEMMODAL, '', $iInt) ; This will display 10.


There is no requirement to declare the iteration count variable in a loop:

; Correct
Local Const $iCount = 99
Local $aArray[$iCount]
For $i = 0 To UBound($iCount) - 1 ; $i is only used in the loop, so there is no requirement to declare it.
	$aArray[$i] = $i
Next

; Incorrect
Local Const $iCount = 99
Local $aArray[$iCount]
Local $i ; This is only used to store the iteration count value in the loop and therefore doesn't need to be declared. This is known as loop scope.
For $i = 0 To UBound($iCount) - 1
	$aArray[$i] = $i
Next

As you can see, there is the Const keyword in the example, we are going to talk about it.


Const, Static, Enum

Const

We won't talk about the advantages of a constant variable, they are neglibible (for your information, an autoit constant variable is marked as read-only and remains a variable as read-only when compiled).

The Const keyword may be used in a first by some of you to avoid re-assignments. The best way to use them is not this last case, the constants should be used for real static variables, meaning that their value won't change regardless to the instance of the program.


Example:

;Bad
Local Const $hGUI = GUICreate("MyGUI")
; The handle of the window is unique, it's generated by Windows and changes.

;Good
Local Const $iMyAge = 19

Magic Numbers

Magic numbers are arbitrary numbers interspersed throughout a program's source code which do not have an associated identifier. The downside to this is not being able to derive a meaning from the number.

For example:

MsgBox(262144, "Magic Numbers", "It's Adventure Time!")

In this example, the magic number is 262144 with the identifier being $MB_TOPMOST according to the helpfile.

More coming soon :)