If you are working on a program with a group of people, or you think the program is going to be much longer than 100 lines (this is not a hard and fast rule - you can determine when a program is too long), you should consider breaking your program into modules.
In this post I will show you how I write large programs into manageable modules. The program I will work with is not particularly exciting but the process of writing is important. We'll write a program that gives the user the option to add two number, give a random number, or print a greeting.
I've created a new code file called LargeProgram.bas in its own directory.
Let's begin with a simple menu:
/'
======================
Large Program Example in FreeBasic
Franktic
3 June 2017
======================
'/
Dim iOption As integer
iOption = 0
Do
Print "What would you like to do: "
Print" 1. Print a Greeting"
Print" 2. Give a random number"
Print" 3. Add two numbers"
Print" 99. Quit"
Input iOption, "Please enter a menu item number: "
Loop Until iOption = 99
All of this should be pretty obvious, but if not then look at some of the earlier posts. The code presents a simple menu and the user is asked to enter an option number.
Let's add code to handle the chosen option. Please note I have shown the additional code in blue to see where it fits into the main program:
Input iOption, "Please enter a menu item number: "
Select Case As Const iOption
Case 1
Print "selected option 1"
Case 2
Print "selected option 2"
Case 3
Print "selected option 3"
Case 99
Exit Select
Case Else
Print "Not an option"
End Select
Loop Until iOption = 99
I have decided to use the Select Case statement to process the option ( http://www.freebasic.net/wiki/wikka.php?wakka=KeyPgSelectcase ) . Notice that the program is still extremely simple but we give get feedback while testing the program that the option chosen is actually being processed. The line with Case 99 exits the Select statement, prints two blank lines, then returns control to the Loop Until iOption = 99 line which effectively ends the program.
Another point to note is the Case Else statement. This catches all invalid inputs and gives an error message.
Let's begin with the simplest of the options, print a greeting. In your editor begin a new code file called greeting.bas and save it into the same location as your main file above. Add the following code:
/'
======================
Greeting in FreeBasic
Franktic
3 June 2017
======================
'/
Declare Sub pGreeting
Sub pGreeting
Print "Hello"
End sub
In the Fundamental posts we had a look at functions, blocks of code that return a value. In this case we don't need a value returned, we want the block of code to simply print Hello then pass control back. Functions that do not return values are known as Procedures. In FreeBASIC these are called Subs (? short for subroutine) http://www.freebasic.net/wiki/wikka.php?wakka=KeyPgSub. Subs need to be declared as shown above. This is followed by the code for the sub, in this case it simply prints Hello. I have prefixed the name with a p for procedure to avoid clashes with existing FreeBASIC words.
To use the new procedure we need to tell our main program to include the code, then we can simply call it by using its name. To include the code we add the following line:
#Include once "Greeting.bas"
I typically place all the #Include lines together at after the title comments. Once I open a program file I can see what other files are needed. The 'once' qualifier tells FreeBASIC to include the code only once even if other code modules call Greeting.bas.
Now to call it we change the code in the case statement to say the following, the new line is in blue:
Case 1
pGreeting
Case 2
If we now compile and run the program, pressing option 1 gives us a Hello.
Let's now tackle option 2, give a random number. We saw the code for getting a random number between 1 and 100 in the post about random phrases. We'll use the same idea here. Because we expect a result back we will not use a procedure; we'll use a function instead. To add even more functionality to the code we will allow the user to add the highest random number.
As we did earlier, create a new code file in the same directory as our other two files and call it random.bas. Cut and paste the following code:
/'
======================
Random in FreeBasic
Franktic
3 June 2017
Usage: fRandom(limit)
Returns: random integer between 1 and limit
======================
'/
Declare Function fRandom(limit As Integer) As Integer
Function fRandom(limit As Integer) As Integer
Randomize
Return Int(Rnd * limit) + 1
End Function
The fRandom function is similar to the pGreeting procedure. Where they differ is our function accepts an integer parameter (procedures can accept parameters if required) , the limit integer, and returns an integer value when it is called. Compile the file to check for errors.
In the main program you will need to include the file and allocate a new variable to hold the upper limit for our random number. The blue lines is the new code:
#Include once "Greeting.bas"
#Include once "Random.bas"
Dim iOption As Integer
Dim iTopNumber As Integer
To call the function we add the code in blue:
Case 2
Input "What is the highest random number that can be presented? ", iTopNumber
Print fRandom(iTopNumber)
Case 3
The final option adds two numbers. Again start a new code file in the same directory and call it AddTwo. Cut and paste the following code:
/'
======================
AddTwo in FreeBasic
Franktic
3 June 2017
Usage: fAddTwo(a, b)
Returns: a+b integer
======================
'/
Declare Function fAddTwo(a As Integer, b As Integer) As Integer
Function fAddTwo(a As Integer, b As Integer) As Integer
Return a + b
End Function
This should be making sense now. We declare the function and then we write the function code. In this function we are accepting two parameters and returning the integer sum.
In the main program we add the #include and the two new variables (in blue):
#Include once "Greeting.bas"
#Include once "Random.bas"
#Include Once "AddTwo.bas"
Dim iOption As Integer
Dim iTopNumber As Integer
Dim iFirst As Integer
Dim iSecond As Integer
We also call the code in the Case statement:
Case 3
Input "What is the first number? ", iFirst
Input "What is the second number? ", iSecond
Print "Sum is ";
Print fAddTwo(iFirst, iSecond)
Case 99
That's it for the coding. Getting back to what I mentioned at the start of this post, you can see that our main program is now compact and quite clear. The program was built up with simple statements and, as each module was written it was added to the main program. This allows us to test our program as it is being built. It is easier to work on small sections, without the distraction of hundreds of lines of interdependent code.
If I was collaborating with others on this program, I could have asked a colleague to write a random number generator that took a limit and returned a random number up to that limit. I could keep writing the the main program, placing a 'stub' where the function belonged, then include their code once it was written. I could even write a dummy function that simply printed a "hello from dummy function xyz" in preparation. Large programs are usually written by teams of programmers, each working on specific functions. Given clear input parameters and equally clear returns, along with good documentation, large programs become can be put together rapidly. Blocks of completed and tested code references replace stubs to become part of a larger program.
A further advantage of this modular style of programming is that the functions and procedures that sit in separate files can be used in any number of programs we write. You simply need to include them and call them. This highlights the importance of good documentation in the header of the modules so it is clear what parameters they require and what is returned (in the case of functions). For instance, any program I write going forward that needs a random number can reference the block we saw above.
If clarity, collaboration, and reusability of code were not enough, breaking code into separate files allows for easier programming. Each code file can be thoroughly tested to ensure it is bug free and working correctly. It can then be put aside and called as needed, knowing that only in exceptional circumstances do you need to revisit the code.
Although the include files above have the usual .bas extension, they could have had a .bi extension instead indicating that they are basic include files and not normal code files. Follow the link to the #include section of the wiki for more information: http://www.freebasic.net/wiki/wikka.php?wakka=KeyPgInclude .
One final point to note: The example include files we used, each perform one simple action. Include files can include collections related functions and procedures that can extend the functionality of the FreeBASIC language. Libraries of include files exist that add complex capabilities, such as performing graphics or database work, not present in the base language. You can even include other programming languages, such as Lua, into FreeBASIC code! Here is a list of external libraries to explore http://www.freebasic.net/wiki/wikka.php?wakka=ExtLibTOC .
As always, please feel free to leave a comment and see you in the next post.
The line with Case 99 exits the Select statement, prints two blank lines, then returns control to the Loop Until iOption = 99 line which effectively ends the program.
ReplyDeleteYou put Print Print outside the select case 99. should be inside case 99
Thanks Owen, entering 99 runs the Exit Select command that jumps out of Select Case ... End Select block. It then prints two blank lines and runs the line: Loop Until iOption = 99 . This ends the program. The two blank lines are used to give a two lines of space so you can see the result easier. If the two blank lines were printed within the select case 99 then you would never see the effect because the program would end and the console would shut.
ReplyDeleteoops sorry i think i looked at it wrong. maybe i didn't see the input statement or something... any how about the exit select. don't your mean exit do?
ReplyDeletetry this code to see how the program flow works.
Dim As String k
Do
k=InKey
Select Case k
Case "a"
Print "A"
Case "b"
Print "B
Case "c"
Exit Select
Case Chr(27)
Exit Do
End Select
Print "press the esc key to exit"
Sleep 1000
Loop
End
Interesting bit of code. This keeps looping and asking you to press the esc key to exit. Pressing "c" exits select but because you are in a loop it simply starts the process again. This section of code would not work for the purposes of my program. Pressing Esc - ie chr(27) - then exits the Do loop. This would work for me and would mean that I would not need the Until option of the do..loop. However jumping out of one loop at a time might be easier to understand. You don't want newbies jumping through too many hoops (or was that loops?).
DeleteIt's just that I have never seen "Exit Select" used. Maybe I should ask the freebasic community about Exit Select.
DeleteThat's what I love about programming. I learn something new all the time.
Oh wao you are right
ReplyDeleteHi I went thru your tutorial all the way up to the Largeprogram. Everything worked fine. When I ran LargeProgram the program crashed. windows gave the following notice
ReplyDelete[Window Title]
LargeProgram.exe
[Main Instruction]
LargeProgram.exe has stopped working
[Content]
A problem caused the program to stop working correctly. Windows will close the program and notify you if a solution is available.
[Close program]
I loaded the 64 bit FB version on a windows 8.1 computer and a win 7 laptop. It appears that the program will not access the subroutines. Do you have any suggestions. Jan V
I followed your setup all the way thru to blog 10 LargeProgram. I down loaded FB to a win8.1 computer and a win7 laptop. Both were 64 bit. LargeProgram.exe was unable to call a subroutine on either computor. I recieved the following window notice on both computers.
ReplyDelete[Window Title]
LargeProgram.exe
[Main Instruction]
LargeProgram.exe has stopped working
[Content]
A problem caused the program to stop working correctly. Windows will close the program and notify you if a solution is available.
[Close program]
Do you have any suggestions on what the problem is? Thanks for your help. Your tutorial blog is very helpful. Jan V
I ran the program tonight using a 32 bit version of FB, using FbEdit, and it worked fine. There is no unusual code in this example that would not work with the 64bit version so that should work fine as well. Try running the main program without the calls to the small helper programs, up to the section where I introduce Greeting.bas. If previous examples from this blog have worked then this main program should work as well.
ReplyDeleteIntroduce one help program at a time and test often. Please let me know how you go.
Thanks for your help. I had to change the Input line to Input "Please enter a menu item number: "; iOption to get the program to work. With this change everything runs fine.
ReplyDeleteAlsp using the 64 bit Win 1p Pro:
ReplyDeleteI had the same problem and changed the input line to "Input "Please enter a menu item number: "; iOption"
Worked.