"Luokat" CoolBasiciin
Posted: Fri Apr 01, 2011 4:22 am
Kävipä mielessä muistipalakikkailuja tehdessä, että joskus olisi kiva kun ei tarvitsisi huolehtia indekseistä ja voisi vain koodailla. Tälläinen puolivillainen luokkatoteutus siitä sitten tuli.
Ideana siis se että voi luoda omia muistipaloja yksinkertaisella konstruktorilla, jolle voi antaa alkeistyyppisiä kenttiä (Int, Float, String). Tämän jälkeen tietoja pääsee muokkaamaan kentän nimellä vähän kuin tyyppikokoelmien tapauksessa, mutta koska toteutus on tehty muistipaloja käyttäen voi näitä myös panna toistensa sisään, välittää funktiolle ja laittaa taulukkoihin yms. Luokalle yksityisistä funktioista voi vain haaveilla, mutta olion/olioiden luokan tarkistaminen/vertailu onnistuu.
Mitä tehokkuuteen sitten tulee, niin merkkijonojen vertailu on aika raskasta, vaan CoolBasicin sisäinen CRC32 funktio avitti kenttien vertailussa kummasti. Tehokkaampaa koodia saa kun luo koodinsa muistipaloilla alusta saakka, vaan monesti niissä tuntuu olevan ongelmia niin ajattelin josko näistä joku hyötyisi/oppisi.
Parannelkaa ja heitelkää ehdotuksia jos tulee. (:
Ideana siis se että voi luoda omia muistipaloja yksinkertaisella konstruktorilla, jolle voi antaa alkeistyyppisiä kenttiä (Int, Float, String). Tämän jälkeen tietoja pääsee muokkaamaan kentän nimellä vähän kuin tyyppikokoelmien tapauksessa, mutta koska toteutus on tehty muistipaloja käyttäen voi näitä myös panna toistensa sisään, välittää funktiolle ja laittaa taulukkoihin yms. Luokalle yksityisistä funktioista voi vain haaveilla, mutta olion/olioiden luokan tarkistaminen/vertailu onnistuu.
Mitä tehokkuuteen sitten tulee, niin merkkijonojen vertailu on aika raskasta, vaan CoolBasicin sisäinen CRC32 funktio avitti kenttien vertailussa kummasti. Tehokkaampaa koodia saa kun luo koodinsa muistipaloilla alusta saakka, vaan monesti niissä tuntuu olevan ongelmia niin ajattelin josko näistä joku hyötyisi/oppisi.
Parannelkaa ja heitelkää ehdotuksia jos tulee. (:
Code: Select all
SCREEN 800,600
//Luodaan luokka nimeltään Henkilö
//kenttinä nimi, paikka syntymäpäivä, puoliso ja lemmikki
//kentät erotellaan pilkulla
//kenttien nimet toimivat kuten normaalissa Cb:ssä merkeillä $#% voi merkata tyyppiä
//vaihtoehtoisesti voi kertoa kentän tyypin seuraavasti:
//Class("Testiluokka","kenttä:Integer, pieniluku: Byte, kokonainen:i")
//Kaikki mahdolliset lyhennelmät löytyvät Class funktiosta
Person = Class("Henkilö","nimi$, palkka, syntymäpäivä$, vaimo, lemmikki")
//Tässä luodaan olio luokalle Henkilö, käytetään luotua luokkaa parametrina
matti = Object(Person)
//nyt oliolle voidaan antaa arvoja, Huomaa että coolBasicin rajoituksista johtuen
//on kokonaisluvuille (Int, Short, Byte) liukuluvuille (Float) ja merkkijonoille String
//omat funktionsa
SetString(matti,"nimi","Matti Meikäläinen")
SetInt(matti,"palkka",Rand(1000,5000))
//tässä luodaan toinen olio luokasta Henkilö
maija = Object(Person)
SetString(maija,"nimi","Maija Meikäläinen")
SetInt(maija,"palkka",Rand(1000,5000))
//olioita voi laittaa myös toistensa kentiksi
//tällöin tulee käyttää SetInt funktiota sillä muistipalat
//näkyvät coolbasicissa kokonaislukuina (muistiosoitteina)
SetInt(Matti,"vaimo",maija)
//Vihdoin päästään käyttämään annettuja tietoja, samalla get funktiolla saadaan
//kaikki tiedot hankittua, huomaa kuitenkin että kenttien nimissä kiRJaiNkoOlla on väliä
Print Get(Matti,"nimi")+" saa palkkaa "+Get(Matti,"palkka")+" euroa kuukaudessa."
//koska oliot käyttäytyvät myös kuin kokonaisluvut voidaan niitä käsitellä mukavasti
matinpuoliso = Get(Matti,"vaimo")
//tässä tarkistetaan että matinpuoliso muuttujassa oleva olio on oikeasti
//Henkilö luokan olio, tällä voidaan välttää virheitä.
If isObjectOf(matinpuoliso,"Henkilö")
//lisää hakuja, ja hyvinhän nuo toimii vaikka maija vaihtui matinpuolisoon
Print "Hänen vaimonsa "+get(matinpuoliso,"nimi")+" saa palkkaa "+get(matinpuoliso,"palkka")+" euroa kuukaudessa."
Print
//täällä tulee esiin olioiden dynaamisuus, koska olio on vain osoite käytettyyn muistipalaan
//voidaan sitä vaihtaa kuin sukkia tai useamminkin! :o
If Get(Matti,"palkka")>Get(matinpuoliso,"palkka") Then pienempi= matinpuoliso: Suurempi=Matti Else pienempi = Matti: suurempi = matinpuoliso
//täällä taasen huomataan että get todellakin palauttaa luokan kentissä ilmoitettuja arvoja muutehan palkkoja ei voisi vähentää toisistaan
Print Get(pienempi,"nimi")+" saa "+Int(Get(suurempi,"palkka")-Get(pienempi,"palkka"))+" euroa vähemmän palkkaa kuin "+Get(suurempi,"nimi")+"."
EndIf
//tehdään vielä yksi Henkilö
liisa = Object(Person)
SetString(liisa,"nimi","Liisa Lillukka")
SetInt(liisa,"palkka",Rand(1000,5000))
//ja luodaan uusi luokka, jolla kaksi kenttää
lemmikki = Class("Lemmikki","nimi$, söpöys$")
//luodaan lemmikki luokalle puudeli olio
puudeli = Object(lemmikki)
//asetetaan puudelille vähän tietoja
SetString(puudeli,"nimi","Struudeli puudeli")
SetString(puudeli,"söpöys","awww")
//ja alistetaan puudeli liisan lemmikiksi
//huomataan taas että lemmikki on kokonaislukukenttä jotta
//puudeli voidaan siihen tallentaa
SetInt(liisa,"lemmikki",puudeli)
//taas kikkaillaan vähän osoituksella
//tätä kannattaa suosia sillä jatkuva nimellä hakeminen on varsin tehotonta
liisanlemmikki = get(liisa,"lemmikki")
//Tässä kokeillaan ominaisuutta jossa voidaan olion luokan nimi kertoa merkkijonona
//jos vertailua aikoo tehdä niin on kuitenkin nopeampaa käyttää kahden olion väliseen vertailuun
//isSameClass funktiota tai jos haluaa tarkistaa onko haluttu olio tietyn luokan jäsen
//isObjectOf funktiota, tällöin vältytään muuntamasta muistissa tavuina tallennettua
//merkkijonoa merkki kerrallaan takaisin merkkijonoksi.
Print get(liisa,"nimi")+" on "+getClassName(liisa)+"."
//tässä varmistetaan että liisanlemmikki olio on todella Lemmikki luokan olio
If isObjectOf(liisanlemmikki,"Lemmikki") Then
//ja vielä tulostellaan vähän tietoja
Print "Ja hänellä on "+get(liisanlemmikki,"nimi")+" lemmikkinään."
Print get(liisanlemmikki,"nimi")+ " ON söpöydeltään aivan "+get(liisanlemmikki,"söpöys")+"."
EndIf
//lopuksi poistetaan, vaikka se ei taitaisi olla aivan välttämätöntä
Remove(liisa)
Remove(Matti)
Remove(Maija)
//Kannattaa huomioida että luokka tulee poistaa vasta sen olioiden jälkeen
//erityisesti siksi ettei luokan olioita tule vahingossa käytettyä
//luokan poistamisen jälkeen. Se aiheuttaa virhetilanteita
//tietysti tämäkin olisi kierrettävissä ylimääräisillä tarkastuksilla
//mutta kaikki varmistelutarkistelut maksavat suoritusaikaa
//ja silloin emme saa tehtyä niin hienoja pelejä. Vaikea valinta, eikös vain :P
DeleteClass(Personator)
//sama toimenpide myös Lemmikki luokalle.
REmove(puudeli)
DeleteClass(lemmikki)
WaitKey
//tästä sitten alkaa itse koodi
//aluksi on hieman indeksejä joita käytetään
//muistipaloissa offsetteinä, eli ne kertovat
//kuinka paljon muistipalan alusta kyseiseen kohtaan on.
Const pObjectDataAmount = 0
Const pFieldAmount = 4
Const pFieldNames = 8
Const pFieldTypes = 12
Const pFieldPositions = 16
Const pClassName = 20
Const pClassMemBlockSize = 24
Const pObjectData = 0
Const pObjectMemBlockSize = 24
//Luo uuden Luokan
//ottaa parametreiksi luokan nimen
//sekä luokan kenttien nimet ja tietotyypit
//huomaa ettei a As Integer merkitää tueta (vielä)
//Turvallisinta on käyttää a$ a# tai a merkkijonoille
//liukuluvuille ja kokonaisluvuille.
Function Class(ClassName$,parameters$)
size = 0
//alustetaan hieman luokan muistipaloja
//lasketaan siis kenttien määrä ensin
fields = CountWords(parameters$,",")
fieldnamemem = MakeMEMBlock(4*fields)
fieldtypemem = MakeMEMBlock(fields)
fieldpositionmem = MakeMEMBlock(4*fields)
//tässä parsitaan komento ja tietotyyppi toisistaan
//koska eri tietotyypit vievät eri verran tilaa
//pitää myös se ottaa huomioon
//ja koska tietotyyppejä luetaan erilaisilla
//komennoilla, myös niiden järjestyksellä on väliä
For i=1 To fields
oldsize = size
word$ = Trim(GetWord(parameters,i,","))
If InStr(word,":") Then
name$ = Trim(GetWord(word,1,":"))
mtype$ = Trim(GetWord(word,2,":"))
Select mtype$
Case "Int", "Integer", "I", "i", "%"
size = size + 4
memtype = 1
Case "Float", "f", "F", "#"
size = size + 4
memtype = 2
Case "String", "s", "S", "$"
size = size + 4
memtype = 3
Case "MemString", "ms", "MS", "$$"
size = size + 4
memtype = 4
Case "Short", "sh", "SH"
size = size + 2
memtype = 5
Case "Byte", "b", "B"
size = size + 1
memtype = 6
End Select
Else
If Right(word,1)="#" Then
size = size + 4
memtype = 2
name$ = Left(word,Len(word)-1)
ElseIf Right(word,1)="$" Then
size = size + 4
memtype = 3
name$ = Left(word,Len(word)-1)
ElseIf Right(word,1)="%" Then
size = size + 4
memtype = 1
name$ = Left(word,Len(word)-1)
Else
size = size + 4
memtype = 1
name$ = word
EndIf
EndIf
//ja sitten ne tallennetaan luokan muistipalaan
//kenttien nimistä otetaan CRC jotta vertailu
//olisi nopeaa
mem = TextToMem(name$)
PokeInt fieldnamemem,(i-1)*4,Crc32(mem)
DeleteMEMBlock mem
PokeInt fieldtypemem,(i-1),memtype
PokeInt fieldpositionmem,(i-1)*4,oldsize
Next i
//lopuksi kaikki muistipalat kääritään nätisti
//yhden muistipalan sisään, joka palautetaan
//koodaajalle
C = MakeMEMBlock(pClassMemBlockSize)
PokeInt C, pClassName,TextToMem(ClassName$)
PokeInt C,pDataSize,size
PokeInt C,pFieldAmount,fields
PokeInt C,pFieldNames,fieldnamemem
PokeInt C,pFieldTypes,fieldtypemem
PokeInt C,pFieldPositions,fieldpositionmem
Return C
End Function
//Varoitus, luokan poistaminen ennen sen olioita
//tekee olioista epävakaita ts. kone kaatuu todennäköisesti MAViin. Siis
//poista ensin oliot ja sitten vasta luokka
Function DeleteClass(C)
If C Then
If PeekInt(C,pFieldNames) Then DeleteMEMBlock PeekInt(C,pFieldNames)
if PeekInt(C,pFieldTypes) Then DeleteMEMBlock PeekInt(C,pFieldTypes)
if PeekInt(C,pFieldPositions) Then DeleteMEMBlock PeekInt(C,pFieldPositions)
DeleteMEMBlock C
C=0
EndIf
End Function
//tämä antaa olion luokan nimen merkkijonona, ei kannata
//käyttää jos ei aivan pakko
Function getClassName(obj)
If obj Then Return MemToText(PeekInt(obj,pClassName)) Else Return ""
End Function
//vertailee kahta oliota keskenään palauttaa true jos ovat saman luokan olioita
Function isSameClass(obj,obj2)
If obj And obj2 Then
objectClassName = PeekInt(obj,pClassName)
object2ClassName = PeekInt(obj2,pClassName)
If objectClassName = object2ClassName Then Return True
If Crc32(object2ClassName) = Crc32(objectClassName) Then Return True
EndIf
Return False
End Function
//Kertoo kuuluuko olio tämännimiseen luokkaan. Suhteellisen hidas.
Function isObjectOf(obj,ClassName$)
If obj Then
classNameMem = TextToMem(ClassName$)
objectClassName = PeekInt(obj,pClassName)
If Crc32(classNameMem) = Crc32(objectClassName) Then Return True
EndIf
Return False
End Function
//Tämä luo uusia olioita ottaa luokan parametrikseen
Function Object(C)
//tässä luodaan olion datalle muistipaljoja ja
//määritetään niiden kokoa
obj = MakeMEMBlock(pObjectMemBlockSize)
objectDataAmount = PeekInt(C,pObjectDataAmount)
objectData = MakeMEMBlock(objectDataAmount)
For i=0 To MEMBlockSize(objectData)-1
PokeByte objectData,i,0
Next i
PokeInt obj,pObjectData, objectData
PokeInt obj,pClassName,PeekInt(C,pClassName)
//lopuksi taas kääritään kaikki tieto yhteen muistipalaan
//johon kopioidaan luokan tiedoista paikalliset kopiot
PokeInt obj,pFieldAmount,PeekInt(C,pFieldAmount)
PokeInt obj,pFieldNames,PeekInt(C,pFieldNames)
PokeInt obj,pFieldTypes,PeekInt(C,pFieldTypes)
PokeInt obj,pFieldPositions,PeekInt(C,pFieldPositions)
Return obj
End Function
//Tällä poistetaan olioita
Function Remove(obj)
If obj Then
//koska oliolla voi olla muistipaloja kentissään
//pitää ne poistaa ennen olion poistamista
//tässä tarkistetaan merkkijonokenttien poistamista
types = PeekInt(obj,pFieldTypes)
If types Then
For i=0 To MEMBlockSize(types)-1
If PeekByte(types,i) = 3 Then
s=PeekInt(PeekInt(obj,pObjectData),i*4)
If s Then DeleteMEMBlock s
EndIf
Next i
EndIf
//lopuksi koko olio poistetaan ja nollataan osoitin
If PeekInt(obj,pObjectData) Then DeleteMEMBlock PeekInt(obj,pObjectData)
DeleteMEMBlock obj
obj=0
EndIf
End Function
//tällä asetetaan oliolle kokonaislukukenttään arvo
Function SetInt(obj,name$,value)
fieldname = TextToMem(name$)
fieldnames = PeekInt(obj,pFieldNames)
//ensin kenttä tulee kuitenkin etsiä
For i=0 To MEMBlockSize(fieldnames)-1 Step 4
//nimen perusteella etsitään
//voisi myös olla tehokkaampi
//hakupuu tai hajautustaulu
//muutamilla kentillä ei kuitenkaan merkittävää
//eroa.
If Crc32(fieldname) = PeekInt(fieldnames,i) Then
DeleteMEMBlock fieldname
objectData = PeekInt(obj,pObjectData)
fieldPositions = PeekInt(obj,pFieldPositions)
position = PeekInt(fieldPositions,i)
fieldTypes = PeekInt(obj,pFieldTypes)
fieldtype = PeekByte(fieldTypes,RoundDown(i/4))
//kirjoitetaan vain jos kenttä todella on haluttua
//tietotyyppiä Myös tavut ja shortit on tuettuna
Select fieldtype
Case 1
PokeInt objectData,position,value
Case 4
PokeInt objectData,position,value
Case 5
PokeShort objectData,position,value
Case 6
PokeByte objectData,position,value
Default
MakeError "Tried t"+"o set "+name$+" with wrong t"+"ype!"
End Select
PokeInt obj,pObjectData,objectData
Return obj
EndIf
Next i
//täällä tulee virhettä jos etsittyä kenttää ei löydykään
MakeError "Tried t"+"o set value "+value+" to Int field of Object "+name$+" and it isn't defined!"
End Function
//tämä muuten samanlainen mutta value on merkkijonona ja ainoastaan merkkijonot sallittuja
Function SetString(obj,name$,value$)
fieldname = TextToMem(name$)
fieldnames = PeekInt(obj,pFieldNames)
For i=0 To MEMBlockSize(fieldnames)-1 Step 4
If Crc32(fieldname) = PeekInt(fieldnames,i) Then
DeleteMEMBlock fieldname
objectData = PeekInt(obj,pObjectData)
fieldPositions = PeekInt(obj,pFieldPositions)
position = PeekInt(fieldPositions,i)
fieldTypes = PeekInt(obj,pFieldTypes)
fieldtype = PeekByte(fieldTypes,RoundDown(i/4))
Select fieldtype
Case 3
PokeInt objectData,position,TextToMem(value$)
Default
MakeError "Tried t"+"o set "+name$+" with wrong t"+"ype!"
End Select
PokeInt obj,pObjectData,objectData
Return obj
EndIf
Next i
MakeError "Tried t"+"o set value "+value$+" to Int field of Object "+name$+" and it isn't defined!"
End Function
//sama juttu täällä kts. SetInt jos ongelmia
Function SetFloat(obj,name$,value#)
fieldname = TextToMem(name$)
fieldnames = PeekInt(obj,pFieldNames)
For i=0 To MEMBlockSize(fieldnames)-1 Step 4
If Crc32(fieldname) = PeekInt(fieldnames,i) Then
DeleteMEMBlock fieldname
objectData = PeekInt(obj,pObjectData)
fieldPositions = PeekInt(obj,pFieldPositions)
position = PeekInt(fieldPositions,i)
fieldTypes = PeekInt(obj,pFieldTypes)
fieldtype = PeekByte(fieldTypes,RoundDown(i/4))
Select fieldtype
Case 2
PokeFloat objectData,position,value#
Default
MakeError "Tried t"+"o set "+name$+" with wrong t"+"ype!"
End Select
PokeInt obj,pObjectData,objectData
Return obj
EndIf
Next i
MakeError "Tried t"+"o set value "+value#+" to Int field of Object "+name$+" and it isn't defined!"
End Function
//tämä periaatteessa hieman nopeampi tapa käsitellä kenttiä, mutta nimien sijasta
//pääsee vaain tiettyyn kenttään numerolla käsiksi
// jos kentät on määritelty a, b, c niin kenttä c olisi numero 2 eli numerot alkaa
//nollasta.
Function GetN(obj,pos)
fields = PeekInt(obj,pFields)
If fields>=pos Then
objectData = PeekInt(obj,pObjectData)
fieldPositions = PeekInt(obj,pFieldPositions)
position = PeekInt(fieldPositions,pos*4)
fieldTypes = PeekInt(obj,pFieldTypes)
fieldtype = PeekByte(fieldTypes,RoundDown(i/4))
Select fieldtype
Case 1
Return PeekInt(objectData,position)
Case 2
Return PeekFloat(objectData,position)
Case 3
Return MemToText(PeekInt(objectData,position))
Case 4
Return PeekInt(objectData,position)
Case 5
Return PeekShort(objectData,position)
Case 6
Return PeekByte(objectData,position)
End Select
Return 0
Else
MakeError "Tried t"+"o get position "+pos+" and there is only "+fields+"!"
EndIf
End Function
//tässä tämä yleiskäyttöinen hakufunktio
//palauttaa löytämänsä kentän juuri oikeassa muodossa.
Function Get(obj,name$)
fieldname = TextToMem(name$)
fieldnames = PeekInt(obj,pFieldNames)
For i=0 To MEMBlockSize(fieldnames)-1 Step 4
If Crc32(fieldname) = PeekInt(fieldnames,i) Then
DeleteMEMBlock fieldname
objectData = PeekInt(obj,pObjectData)
fieldPositions = PeekInt(obj,pFieldPositions)
position = PeekInt(fieldPositions,i)
fieldTypes = PeekInt(obj,pFieldTypes)
fieldtype = PeekByte(fieldTypes,RoundDown(i/4))
Select fieldtype
Case 1
Return PeekInt(objectData,position)
Case 2
Return PeekFloat(objectData,position)
Case 3
Return MemToText(PeekInt(objectData,position))
Case 4
Return PeekInt(objectData,position)
Case 5
Return PeekShort(objectData,position)
Case 6
Return PeekByte(objectData,position)
End Select
Return 0
EndIf
Next i
MakeError "Tried t"+"o get "+name$+" and it isn't defined!"
End Function
//tämä tallentaa tekstin muistipalaan ja palauttaa sen
Function TextToMem(Txt$)
l = Len(txt$)
mem = MakeMEMBlock(l)
If l>0
For i=1 To l
PokeByte mem,i-1,Asc(Mid(txt$,i,1))
Next i
EndIf
Return mem
End Function
//tämä muuntaa muistipalassa olevan tekstin takaisin merkkijonoksi
Function MemToText(mem)
txt$ = ""
l = MEMBlockSize(mem)
If l>0
For i=0 To l-1
txt$=txt$+Chr(PeekByte(mem,i))
Next i
EndIf
Return txt$
End Function