Object Oriented Programming in Bascom: Unterschied zwischen den Versionen
Mat (Diskussion | Beiträge) K (→Inheritance and Polymorphism) |
Mat (Diskussion | Beiträge) K (→Index) |
||
| (3 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) | |||
| Zeile 5: | Zeile 5: | ||
* [[Bascom Precompiler|''[OOP 3/3] Bascom-Precompiler'']] | * [[Bascom Precompiler|''[OOP 3/3] Bascom-Precompiler'']] | ||
| + | |||
| + | * [https://www.mcselec.com/index2.php?option=com_forum&Itemid=59&page=viewtopic&p=83584#83584 Forum thread] | ||
== Object as collection of data == | == Object as collection of data == | ||
| Zeile 189: | Zeile 191: | ||
== Examples == | == Examples == | ||
| + | === TLSF Memory Allocator === | ||
| + | Blocks of memory are organized in double linked lists, the corresponding data structure is located in the block header. | ||
| + | |||
| + | |||
=== Single linked list === | === Single linked list === | ||
In this example, the user enters an arbitrary amount of strings into the console which are stored in a linked list. When completed, every second entry is deleted and the list is printed out again. | In this example, the user enters an arbitrary amount of strings into the console which are stored in a linked list. When completed, every second entry is deleted and the list is printed out again. | ||
| Zeile 272: | Zeile 278: | ||
| − | The same application implemented in Bascom: | + | The same application implemented in Bascom (runs in the simulator): |
<pre>$regfile = "m32def.dat" | <pre>$regfile = "m32def.dat" | ||
$crystal = 16000000 | $crystal = 16000000 | ||
| Zeile 513: | Zeile 519: | ||
Print "----------------------------------" | Print "----------------------------------" | ||
Loop</pre> | Loop</pre> | ||
| − | |||
=== Real world example: Semaphore implementation from Chronos === | === Real world example: Semaphore implementation from Chronos === | ||
| Zeile 724: | Zeile 729: | ||
== Download == | == Download == | ||
| − | [ | + | * [http://www.braunecker.at/downloads/memory-allocators/memory-allocators-1-0.zip Memory Allocators & Precompiler Package 1.0] |
Aktuelle Version vom 5. November 2023, 16:51 Uhr
Inhaltsverzeichnis
Index
- [OOP 1a/3] Dynamic Memory Allocation using TLSF
- [OOP 1b/3] Static Memory Allocation
- [OOP 2/3] Object Oriented Programming in Bascom
- [OOP 3/3] Bascom-Precompiler
Object as collection of data
Defining an Object
Each object consists of its own member variables (and functions, see below), which are organized in memory as a unit. For example, here is a simple object (in pseudocode, intended string size in parenthesis), storing some personal information:
Object Person Name As String(30) Birth_year As Word Height_cm As Byte End Object
The member fields therefore have a fixed memory layout, the member's positions could be defined like this:
Const Person_name = 0 ' Name string is starting at position 0 Const Person_birth_year = 31 ' year of birth is stored at position 31 (30 bytes string data + 0 terminator Const Person_height_cm = 33 ' adding 2 bytes of previous field (word) Const Person_object_size = 34 ' needing a total of 34 bytes
To create an instance of an object, a block of memory has to be reserved for the object's data (using either static or dynamic memory allocation).
Further references to the object are done with the starting address of the memory block, the offsets obviously remain the same for each object of the same kind.
The memory map defined above describes the address offsets of the member fields.
Access and manipulation of the field data could then be done by adding the object's memory address and the member field's offset (pseudocode):
Object.Field = Data ' store data at address + offset Data = Object.Field ' read data from address + offset
Working with Objects
Since Bascom does not support this kind of data access, own routines have to be created (read/write data for each variable type, for example the byte type):
Sub Setbyte(object As Word, Byval Offset As Word, Byval Value As Byte) ' calculate address + offset ' copy contents of Value to memory address End Sub Function Getbyte(object As Word, Byval Offset As Word) As Byte ' calculate address + offset ' copy contents from memory address to return value End Function
"Interfacing" to the Bascom world needs the help of normal variables:
Dim Object As Word ' reference to object instance; memory address Dim Name As String * 30 ' needs to be big enough to store the string member data Dim Birth_year As Word Dim Height_cm As Byte ' Reserve memory Object = Malloc(Person_object_size) ' Fill the object with some data Name = "John Doe" Birth_year = 2000 Height_cm = 175 Setstring Object, Person_name, Name Setword Object, Person_birth_year, Birth_year Setbyte Object, Person_height_cm, Height_cm ' do some other things ' ... ' Get back the object's data Name = Getstring(Object, Person_name) Birth_year = Getword(Object, Person_birth_year) Height_cm = Getbyte(Object, Person_height_cm) Print Name; ": born in "; Birth_year; ", is measuring "; Height_cm; "cm."
Implementations of these functions for every data type can be found in the file os_memory.inc.
Member Functions
Lets expand the object definition by a function that prints the informations stored in the object (pseudocode):
Object Person
Name As String(30)
Birth_year As Word
Height_cm As Byte
Sub Print_info()
Print Me.Name; ", "; Me.Birth_year; ", "; Me.Height_cm
End Sub
End Object
Translated to Bascom, the sub needs to know which object it manipulates, by passing it the object reference:
Sub Print_info(byref Object As Word) Local Name As String * 30 Local Birth_year As Word Local Height_cm As Byte Name = Getstring(Object, Person_name) Birth_year = Getword(Object, Person_birth_year) Height_cm = Getbyte(Object, Person_height_cm) Print Name; ": born in "; Birth_year; ", is measuring "; Height_cm; "cm." End Sub
Inheritance and Polymorphism
The pseudocode-application is enhanced to manage two kinds of personal data connected to a person: one type consisting of address fields (street, city, state, ...), the other of email addresses. Since both of them still should have the same data as the person object from before, the field definitions could be copied to the second type. Instead, the person object could be extended by either the additional street address data or email address data (the abstracted objects inherit the base object):
Object Person
Name As String(30)
Birth_year As Word
Height_cm As Byte
Sub Print_info()
Print Me.Name; ", "; Me.Birth_year; ", "; Me.Height_cm
End Sub
End Object
Object Street_address
Extends Person
Street As String(50)
Street_nr As String(5)
City As String(20)
State As String(30)
End Object
Object Email_address
Extends Person
Email As String(50)
End Object
The abstracted object definitions Street_address and Email_address each contain the fields of the base object Person (Name, Birth_year and Height_cm) as well as their own specialized fields.
In other words, the abstracted object definitions contain an instance of the base object as additional member field.
Accordingly, the definitions in Bascom are extended to:
Const Person_name = 0 ' Name string is starting at position 0 Const Person_birth_year = 31 ' year of birth is stored at position 31 (30 bytes string data + 0 terminator Const Person_height_cm = 33 ' adding 2 bytes of previous field (word) Const Person_object_size = 34 ' needing a total of 34 bytes Const Street_address_person = 0 ' the first field consists of the inherited object Const Street_address_street = Person_object_size ' next field offset is the size of the previous field, object "Person", Street is String * 50 Const Street_address_street_nr = Person_object_size + 51 Const Street_address_city = Person_object_size + 57 Const Street_address_state = Person_object_size + 78 Const Street_address_object_size = Person_object_size + 109 Const Email_address_person = 0 Const Email_address_email = Person_object_size Const Email_address_object_size = Person_object_size + 51
Note that the inherited object is always the first field of the abstracted object, which leads to the field offsets of the base object always beeing the same along the abstracted objects. The Name field always starts at position 0, Birth_year at position 31 and so on.
Because of that, the base member function Print_info() could as well be used with objects of the type Street_address or Email_address:
Dim Personobject As Word Dim Streetobject As Word Dim Emailobject As Word Dim Tempstring As String * 50 Dim Tempword As Word Personobject = Malloc(Person_object_size) Tempstring = "John Doe" Setstring Personobject, Person_name, Tempstring Tempword = 2000 Setword Personobject, Person_birth_year, Tempword Streetobject = Malloc(Street_address_object_size) Tempstring = "Joanne Doe" Setstring Streetobject, Person_name, Tempstring Tempword = 2001 Setword Streetobject, Person_birth_year, Tempword Tempstring = "Doetown" Setword Streetobject, Street_address_city, Tempstring Emailobject = Malloc(Email_address_object_size) Tempstring = "Joe Doe" Setstring Emailobject, Person_name, Tempstring Tempword = 1970 Setword Emailobject, Person_birth_year, Tempword Tempstring = "joe@thedoes.com" Setword Emailobject, Email_address_email, Tempstring ' ... Print_info Personobject Print_info Streetobject Print_info Emailobject
Examples
TLSF Memory Allocator
Blocks of memory are organized in double linked lists, the corresponding data structure is located in the block header.
Single linked list
In this example, the user enters an arbitrary amount of strings into the console which are stored in a linked list. When completed, every second entry is deleted and the list is printed out again. First, here is an implementation in an OOP language, VB.net:
Module Module1
' Object definition
Class ListEntry
Public NextPtr As ListEntry ' point to the next object in the list
Public Size As UInt16 ' store the size of associated data
Public Data As String ' store the data
' print out information of an object
Public Sub PrintInfo()
Console.Write("Size: " & Size)
Console.WriteLine(vbTab & "Text: " & Data)
End Sub
End Class
' reference to the first object in the list
Public ListHead As ListEntry = Nothing
' adds an object to the single linked list
Public Sub ListAdd(Data As String)
Dim Entry As New ListEntry() ' > Malloc
Entry.NextPtr = ListHead ' link to previous object in list
ListHead = Entry ' set new list head
Entry.Size = Data.Length ' store data size
Entry.Data = Data ' store data
End Sub
' iterates through the list and deletes every second object
Public Sub ListRemoveEverySecond()
Dim Entry As ListEntry
Dim NextEntry As ListEntry
Dim DeleteEntry As ListEntry
Entry = ListHead ' begin iterating
While Entry IsNot Nothing ' as long as there is an object available
DeleteEntry = Entry.NextPtr ' get next entry which is to be deleted
If DeleteEntry IsNot Nothing Then ' object exists
NextEntry = DeleteEntry.NextPtr ' get next object in list
Entry.NextPtr = NextEntry ' update link from previous object
' garbage collection is responsible for freeing the object if there are no references to it anymore
' > Free DeleteObject
Else
NextEntry = Nothing ' no more objects in list
End If
Entry = NextEntry ' continue iterating
End While
End Sub
' iterates through the list and prints out information about the stored objects
Public Sub ListPrint()
Dim Entry As ListEntry
Entry = ListHead ' begin iterating
While Entry IsNot Nothing ' as long as there is an object available
Entry.PrintInfo() ' do something with the object
Entry = Entry.NextPtr ' continue with next list entry
End While
End Sub
Sub Main()
Dim Text As String
Do
Do
Console.Write("Enter text (or ""exit""): ")
Text = Console.ReadLine()
If Text = "exit" Then Exit Do
ListAdd(Text)
Loop
Console.WriteLine("----------------------------------")
ListPrint()
Console.WriteLine("----------------------------------")
ListRemoveEverySecond()
ListPrint()
Console.WriteLine("----------------------------------")
Loop
End Sub
End Module
The same application implemented in Bascom (runs in the simulator):
$regfile = "m32def.dat"
$crystal = 16000000
$hwstack = 32
$swstack = 48
$framesize = 64
$baud = 9600
Config Submode = New
Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0
' uncomment to view detailed debug output
'Const Debug_level_tlsf = 3
' Set start address of the free memory pool manually (examine compile report, "show internal variables" - setting enabled)
'Const Os_mem_start_free = 250
' include needed libraries
$include "inc\os_malloc_tlsf.inc"
' Object definition, this represents the memory layout of the object
Const List_next_ptr = 0 ' word (ram address), point to the next object in the list
Const List_data_size_ptr = 2 ' byte, store the size of associated data
Const List_data_ptr = 3 ' string, store the data
Const List_header_size = 3 ' object header size (excl. data)
' print out information of an object
Sub Print_info(byref Object As Word)
Local Size As Byte
Size = Getbyte(object , List_data_size_ptr)
Print "Size: " ; Size;
Text = Getstring(object , List_data_ptr)
Print "{009}Text: " ; Text
End Sub
' adds an object to the single linked list
Sub List_add(byref Text As String)
Local Size As Byte
Local Object As Word
Size = Len(text)
Size = Size + 1 ' string trailing zero byte
Object = Size + List_header_size ' total memory size
Object = Malloc(object) ' try to allocate memory
If Object = 0 Then Exit Sub ' check if successful
Setword Object , List_next_ptr , List_head ' link to previous object in list
List_head = Object ' set new list head
Setbyte Object , List_data_size_ptr , Size ' store data size
Setstring Object , List_data_ptr , Text ' store data
End Sub
' iterates through the list and deletes every second object
Sub List_remove_every_second()
Local Object As Word
Local Next_object As Word
Local Delete_object As Word
Object = List_head ' begin iterating
While Object <> 0 ' as long as there is an object available
Delete_object = Getword(object , List_next_ptr) ' get next entry which is to be deleted
If Delete_object <> 0 Then ' object exists
Next_object = Getword(delete_object , List_next_ptr) ' get next object in list
Setword Object , List_next_ptr , Next_object ' update link from previous object
Free Delete_object ' delete actual object
Else
Next_object = 0 ' no more objects in list
End If
Object = Next_object ' continue iterating
Wend
End Sub
' iterates through the list and prints out information about the stored objects
Sub List_print()
Local Object As Word
Object = List_head ' begin iterating
While Object <> 0 ' as long as there is an object available
Print_info Object ' do something with the object
Object = Getword(object , List_next_ptr) ' continue with next list entry
Wend
End Sub
' reference to the first object in the list
Dim List_head As Word
Dim Text As String * 50
Do
' collect strings from the console and add to list
Do
Input "Enter text (or {034}exit{034}): " , Text
If Text = "exit" Then Exit Do
List_add Text
Loop
' print out list of strings
Print "----------------------------------"
List_print
' modify list and print it out again
Print "----------------------------------"
List_remove_every_second
List_print
Print "----------------------------------"
Loop
In Bascom using the precompiler:
$regfile = "m32def.dat"
$crystal = 16000000
$hwstack = 32
$swstack = 48
$framesize = 64
$baud = 9600
Config Submode = New
Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0
' uncomment to view detailed debug output
'Const Debug_level_tlsf = 3
' Set start address of the free memory pool manually (examine compile report, "show internal variables" - setting enabled)
'Const Os_mem_start_free = 250
' include needed libraries
$include "..\..\inc\os_common.inc" ' per default os_common.inc and os_memory.inc are included by the memory allocator and are expected
$include "..\..\inc\os_memory.inc" ' in the subdirectory "inc". To accomodate for the different directory structure of the samples,
' we need to override the default includes
$include "..\..\inc\os_malloc_tlsf.inc"
' Object definition
Typedef Stringlist_item
Next_item As Stringlist_item
Data_size As Byte
Stringdata As String * 1
End Typedef
' print out information of an object
Sub Print_info(byref Object As Stringlist_item)
Local Size As Byte
Size = Object.data_size
Print "Size: " ; Size;
Text = Object.stringdata
Print "{009}Text: " ; Text
End Sub
' adds an object to the single linked list
Sub List_add(byref Text As String)
Local Size As Byte
Local Objectsize As Word
Local Object As Stringlist_item
Size = Len(text)
Size = Size + 1 ' string trailing zero byte
Objectsize = Typdefsize(stringlist_item) + Size ' total memory size
Object = Malloc(objectsize) ' try to allocate memory
If Object = 0 Then Exit Sub ' check if successful
Object.next_item = List_head
List_head = Object ' set new list head
Object.data_size = Size
Object.stringdata = Text
End Sub
' iterates through the list and deletes every second object
Sub List_remove_every_second()
Local Object As Stringlist_item
Local Next_object As Stringlist_item
Local Delete_object As Stringlist_item
Object = List_head ' begin iterating
While Object <> 0 ' as long as there is an object available
Delete_object = Object.next_item ' get next entry which is to be deleted
If Delete_object <> 0 Then ' object exists
Next_object = Delete_object.next_item ' get next object in list
Object.next_item = Next_object ' update link from previous object
Free Delete_object ' delete actual object
Else
Next_object = 0 ' no more objects in list
End If
Object = Next_object ' continue iterating
Wend
End Sub
' iterates through the list and prints out information about the stored objects
Sub List_print()
Local Object As Stringlist_item
Object = List_head ' begin iterating
While Object <> 0 ' as long as there is an object available
Print_info Object ' do something with the object
Object = Object.next_item ' continue with next list entry
Wend
End Sub
' reference to the first object in the list
Dim List_head As Stringlist_item
Dim Text As String * 50
Do
' collect strings from the console and add to list
Do
Input "Enter text (or {034}exit{034}): " , Text
If Text = "exit" Then Exit Do
List_add Text
Loop
' print out list of strings
Print "----------------------------------"
List_print
' modify list and print it out again
Print "----------------------------------"
List_remove_every_second
List_print
Print "----------------------------------"
Loop
Real world example: Semaphore implementation from Chronos
'(*****h* /Semaphore ***********************************************************
* DESCRIPTION
* A Semaphore is an object dedicated to task communication.
* It has a defined count of tokens, a task can aquire one as long as there
* is one left. If there are no more tokens left, the queue mode action
* takes place. Every task can release a token.
* SEE ALSO
* /Messagequeue, /Mutex, /Pipe, /Signal, /Syncpipe
')
'******** **********************************************************************
$nocompile
'(*****O* Semaphore/Os_Semaphore_header ****************************************
* DESCRIPTION
* Header structure of the semaphore object
* DECLARATION
')
Const Os_semaphore_hdr_taskqueue = 0 ' task waiting queue
Const Os_semaphore_hdr_tokencount = Os_taskqueue_hdr_size + 0 ' available tokens
Const Os_semaphore_hdr_tokensize = Os_taskqueue_hdr_size + 1 ' configured token count
Const Os_semaphore_hdr_ownertask = Os_taskqueue_hdr_size + 2 ' task that aquired the last token owns the semaphore
Const Os_semaphore_hdr_size = Os_taskqueue_hdr_size + 4
'******** **********************************************************************
Sub Os_semaphore_create_at(memptr As Word , Byval Tokens As Byte) As Word
Os_mem_clear Memptr , Os_semaphore_hdr_size
Setbyte Memptr , Os_semaphore_hdr_tokensize , Tokens
Setbyte Memptr , Os_semaphore_hdr_tokencount , Tokens
End Sub
'(*****f* Semaphore/Os_semaphore_create ****************************************
* DESCRIPTION
* Creates a new semaphore object.
* SEE ALSO
* Semaphore/Os_semaphore_kill
* DECLARATION
')
Function Os_semaphore_create(byval Tokens As Byte) As Word
'(
* SOURCE
')
Local Semaphore As Word
Semaphore = Malloc(os_semaphore_hdr_size)
If Semaphore = 0 Then
Os_semaphore_create = 0
Exit Function
End If
Os_semaphore_create_at Semaphore , Tokens
Os_semaphore_create = Semaphore
End Function
'******** **********************************************************************
'(*****f* Semaphore/Os_semaphore_kill ******************************************
* DESCRIPTION
* Kills a semaphore object
* SEE ALSO
* Semaphore/Os_semaphore_create
* DECLARATION
')
Sub Os_semaphore_kill(byref Semaphore As Word)
'(
* SOURCE
')
If Semaphore <> 0 Then
Os_semaphore_flush Semaphore
Free Semaphore
End If
End Sub
'******** **********************************************************************
'(*****f* Semaphore/Os_semaphore_aquire ****************************************
* DESCRIPTION
* Tries to aquire a semaphore token. If there are no tokens left, the queue
* mode action takes place.
* SEE ALSO
* Semaphore/Os_semaphore_release, Semaphore/Os_semaphore_flush
* DECLARATION
')
Function Os_semaphore_aquire(byref Semaphore As Word , Byval Queuemode As Word) As Byte
'(
* SOURCE
')
Local Tokencount As Byte
'cli
Os_enter_critical
Tokencount = Getbyte(semaphore , Os_semaphore_hdr_tokencount)
If Tokencount = 0 Then
' no tokens left, put in waiting list and suspend
' Os_sched_priority_inheritance(os_task_active)
Select Case Queuemode
Case Os_queuemode_noblock:
' return error
Os_exit_critical
Os_semaphore_aquire = False
Exit Function
Case Os_queuemode_block:
' suspend and wait to send a message
Os_task_suspendmode Os_task_active , Os_task_suspend_nowakeup , 0
Case Else
' suspend and wait to send a message or timeout
Os_task_suspendmode Os_task_active , Os_task_suspend_timersingleshot , Queuemode
End Select
Os_sched_taskqueue_insert Semaphore , Os_task_active
'Os_exit_critical
Os_task_suspend Os_task_active
Os_enter_critical
Tokencount = Getbyte(semaphore , Os_semaphore_hdr_tokencount)
If Tokencount = 0 Then
Os_exit_critical
Os_semaphore_aquire = False
Exit Function
End If
End If
Decr Tokencount
Setbyte Semaphore , Os_semaphore_hdr_tokencount , Tokencount
Os_exit_critical
Os_semaphore_aquire = True
End Function
'******** **********************************************************************
'(*****f* Semaphore/Os_semaphore_release ***************************************
* DESCRIPTION
* Releases a semaphore token. The releasing task must not have aquired it
* before.
* SEE ALSO
* Semaphore/Os_semaphore_aquire, Semaphore/Os_semaphore_flush
* DECLARATION
')
Sub Os_semaphore_release(byref Semaphore As Word)
'(
* SOURCE
')
Local Tokencount As Byte
Local Tokensize As Byte
Local Task As Word
Os_enter_critical
Tokencount = Getbyte(semaphore , Os_semaphore_hdr_tokencount)
Tokensize = Getbyte(semaphore , Os_semaphore_hdr_tokensize)
If Tokencount < Tokensize Then
' release a token
Incr Tokencount
Setbyte Semaphore , Os_semaphore_hdr_tokencount , Tokencount
' let any waiting task aquire the released token
Task = Os_sched_taskqueue_remove(semaphore)
Os_exit_critical
If Task <> 0 Then
Os_task_event Task
End If
Else
Os_exit_critical
End If
End Sub
'******** **********************************************************************
'(*****f* Semaphore/Os_semaphore_flush *****************************************
* DESCRIPTION
* Releases all semaphore tokens. The releasing task must not have aquired it
* before. All tasks from the waiting list are put to ready state.
* SEE ALSO
* Semaphore/Os_semaphore_aquire, Semaphore/Os_semaphore_release
* DECLARATION
')
Sub Os_semaphore_flush(byref Semaphore As Word)
'(
* SOURCE
')
Local Tokensize As Byte
Local Task As Word
' reset tokens
Os_enter_critical
Tokensize = Getbyte(semaphore , Os_semaphore_hdr_tokensize)
Setbyte Semaphore , Os_semaphore_hdr_tokencount , Tokensize
Os_exit_critical
' resume all tasks waiting in the list
Do
Os_enter_critical
Task = Os_sched_taskqueue_remove(semaphore)
Os_exit_critical
If Task = 0 Then Exit Do
Os_task_event Task
Loop
End Sub
'******** **********************************************************************