torstai 16. huhtikuuta 2009

C on olio-ohjelmointi kieli! (osa 2)

*********** EDIT ************
Kirjoituksen lopussa olevat koodit on nyt lisätty zippiin, jonka voit ladata sivun oikeasta sarakkeesta. (Koodi ei todellakaan ole luettavaa blogisivulla...)
*****************************

Jos joku teistä on lukenut Englanninkielistä blogiani, niin tämä juttu ei anna juuri lisää tietoa teille. Oikeastaan olen lisännyt vain lotto koodin joka löytyy aivan tekstin lopusta.

Vuosien saatossa minua on SUUNNATTOMASTI alkanut ärsyttää C++ ja JAVA koodareiden olio-ohjelmointi hypetys. Keskustelupalstat ovat täynnä teinipoikia, jotka kertovat siitä, miten C++ (tai JAVA) hakkaa C:n mennen tullen. Ja perusteluksi heille tuntuu riittävän se, että C++ ja JAVA ovat olio-ohjelmointikieliä.

Siis kertauksena: C++ koodarit tuppaavat uskomaan (ja tuputtamaan uskomustaan pahaa aavistamattomille aloittelijoille), että C++ on loistava kieli olio-ominaisuuksiensa takia, ja että C vastaavasti on huono kieli, koska se ei ole olio-ohjelmointiin soveltuva.

No nyt on korkea aika kertoa teille kaikille oliohypettäjille, että myös C tukee olio-ohjelmointia. Repikää siitä.

Lisäksi C on kaukana huonosta kielestä. Maailman ehkä parhaat ohjelmat kun on kirjoitettu C-kielellä, ja lisää C koodia tehdään edelleen päivittäin. Tiesitkö, että esim. Linux on kirjoitettu C:llä? Lisäksi suurin osa sulautettujen järjestelmien softista on tehty C:llä. Tiesitkö myös että nykyaikaisessa autossa lieenee saman verran koodirivejä kuin windows XP:ssä? Pelottava ajatus...

Joo, Vesa, koetahan ottaa happea, muutut pikkuhiljaa punaiseksi...

Te jotka juuri ja juuri putositte penkiltä järkytyksestä, ja olette jo lähdössä hakemaan erinäisiä teräaseita opettaaksenne minulle tapoja/oikaistaksenne harhaoppejani, miettikää ensin hetki, mitkä piirteet saavat ohjelmointikielestä oliokielen. Sillävälin minä haen kahvia...

Itse asiassa, C++:ssa on vain muutamia asioita, joita ei C:llä voi tehdä (syntaksi on tietysti erilainen. mutta samat ideat voidaan toteuttaa). Tärkeimpinä rajoituksina tulee mieleen operaattoreiden ylikuormaus, templatet ja destructorit. Näitä kun ei paljaalla C:llä oikein voi tehdä.

Mitä se Vesa huutelee? Ai raivoat jotain luokista, perinnästä ja virtuaalisuudesta? Otahan taas happea, kerron kohta.

Löysittekö muuten vastaukset kysymykseeni, että mikä tekee kielestä oliokielen? Tärkeimpinä piirteinä sanotaan olevan datan ja toiminnallisuuden paketointi samaan pakettiin, sekä instanssien luonti. (Ts. luokka joka kokoaa muuttujat ja funktiot, ja josta voidaan luoda mielin määrin instansseja.)

Englanninkielisestä blogistani kopioidut esimerkit menevät näin:

C++:


const int AGE_STOMACH_RATIO 5
class dog
{
private:
EColour furColour;
int age;
unsigned int stomach_state;
bool check_condition();
int calculateStomachCapacity();
void die();
public:
dog(EColour colour);
void feed(int food_amount);
};

dog::dog(EColour colour)
{
furColour=colour;
}

bool dog::check_condition()
{
if(calculateStomachCapacity() <
.
.
.


Ja vastaava C:llä:



struct Sdog;
int dog_check_condition(Sdog *_this);

typedef int (*check_conditionF)(Sdog *_this);
typedef int (*calculateStomachCapacityF)(Sdog *_this);
typedef void (*dieF)(void);
typedef void (*dogF)(Sdog *_this, EColour colour);
typedef void (*feedF)(Sdog *_this);
typedef struct Sdog
{
EColour furColour;
int age;
unsigned int stomach_state;
check_conditionF check_condition;
calculateStomachCapacityF calculateStomachCapacity;
dieF die;
dogF dog;
feedF feed;
}Sdog;

int dog_check_condition(Sdog *_this)
{
//starvation or overeating
if(_this-&ft;stomach_state == 0 ||
_this->calculateStomachCapacity(_this)
<stomach_state
)
return 0;
return 1;
}
int dog_
void initDog(Sdog *_this, EColour colour)
{
_this->furColour=colour;
_this->age=1;
_this->stomach_state=3;
_this->check_condition=&dog_check_condition;
.
.
.
and so on..
}




Toivottavasti näette mitä ajan takaa :)

Mitenkäs sitten se seuraava, olio-ohjelmoinnin hiiiiieno paradigma? (Olen aina halunnut päästä käyttämään sanaa paradigma :) )

Instanssin luonti:

Nyt siis pitäisi voida tuosta dog "luokasta" kaulita haukkuja vaikka talon täydeltä...



int main()
{
Sdog *lassie;

lassie=malloc(sizeof(Sdog));
lassie-&gtMdog=&initDog;
*(lassie->dog)(lassie,EColor_brown);
*(lassie->feed)(lasie,2);
.
.
// or as a local variable:
/*
Sdog lassie;
*lassie.dog=&initDog;
*lassie.dog(&lassie,EColor_brown);
*lassie.feed(&lassie,2);
.
.
*/



Hei Vesa, miksi noin hiljaa? Veikö koira kielen? ;)
Seuraavaksi sitten ehkä heikoimmin C:ssä toimiva OOP vaatimus,

tiedon "kapselointi":

Tässä on kyllä hieman huijauksen makua, mutta tarkoitushan olikin vain näyttää, että C:llä PYSTYY kirjoittamaan olioajatukset täyttävää koodia. Jaamme siis Sdog struktin kahteen osaan, julkiseen ja yksityiseen struktiin.



typedef struct Sdog
{
dogF dog;
feedF feed;
char internalData[1];
}Sdog;

typedef struct S_dog
{
//As abowe.
}S_dog;


Nyt sijoitamme julkisen struktin esittelyn
(structin joka siis sisältää funktiot ja datan, jota oliomme
käyttäjän on päästävä käsittelemään), headeriin, joka tarjotaan
oliomme käyttäjälle. Privaatti structin taas esittelemme
headerissa, jota ei tarjota asiakkaallemme. .c tiedostossa
jossa otustamme sitten käsittelemme, inkuloimme molemmat
headerit. (Tai jos emme jaa oliomme toteutusta useampaan
tiedostoon, voimme jopa esitellä privaatti osat c-tiedostossamme,
ja tehdä funktioista sekä mahdollisista globaaleistamme staticeja
- silloin kapselointi toimii erinomaisesti).

Teemme siis dog_init() funktion, joka varaa tilan koiruudelle, ja
palauttaa osoittimen luotuun olioon käyttäjälle. Instanssia
tehtäessa siis allokoimme muistiköntin, johon sopii sekä public,
että privaatti struktit, ja sijoitamme ne peräkäin, siten että
julkinen osa tulee köntin alkuun. Käyttäjälle palautamme osoittimen
julkisen struktin alkuun. Tai voimme myös käyttää realloccia, ja
kasvattaa käyttäjän varaamaa oliota, mikäli emme halua lähteä
puhtaalle osoitinlinjalle.

placeholder[1] joka esimerkissä näkyy, on tehty helpoittamaan
struktin käyttöä. castaamalla osoittimen placeholderiin privaatti
struktiksi, pääsemme käsittelemään oikaa kohtaa ilman
laskutoimituksia.

Ai keksitkö Vesa jotain uutta, vai mikä noin hymyilyttää? Että
siis mitenkä C:llä hoidetaan...

Periytys:

Helppoa kuin heinän teko. Edellistä esimerkkiä jatkaakseni,
määrittelemme nyt structin Sdoberman, jonne sijoitamme
dobermannille ominaisen tiedon ja toiminnallisuuden. Yleiset
koiramatskut pidämme Sdogissa. Sitten edellisen esimerkin tapaan,
sijoitamme Sdoberman structin Sdog structin perään, ja lähestymme
asiaa osoittimin. Tahdottaessa käyttää geneerisiä koiramaisuuksia,
käytämme Sdog struktia, ja dobermannille ominaisissa asioissa
asioimme Sdobermannin kanssa. Eli osoitteiden laskentaa ja casteja.
Kyllä se siitä lähtee.

Ja vielä kuuluu Vesasta pihinää... Mitä...? Ok.

Virtuaali luokat:

Pysytään edelleen koiramaisissa tunnelmissa. Eli pidämme geneerisen
Sdog structimme, ja teemme lisäksi Sdoberman, Schihuahua ja
Ssekarotuinen struktit. Lisäämme geneeriseen struktiimme kentän,
joka kertoo minkätyyppinen koira on kyseessä (esim. enumin). Sitten
chihuahuaa, dobermannia tai sekarotuista luotaessa, täytämme
tyyppitiedon oikein. Kaikki kutsut tehdään jälleen Sdog struktia
käyttäen, mutta virtuaalitoiminnot suoritetaan Sdoberman, Schihuahua
ja Ssekarotuinen struktista, tyypin tarkistuksen ja oikean
castauksen jälkeen.

Vielä pari sanaa Destructoreista ja C:stä.

Oletko ikinä kuullut sanaa "memory pool"? Hyyyvin usein varsinkin
linux maailmassa, joudutaan köntti muistia allokoimaan johonkin
tarkoitukseen, ja jakamaan siitä palasia aina pyydettäessä.
Esimerkkinä olkoon vaikka jaettu muisti prosessien välillä, johon
siis päästään kirjoittamaan useammasta kuin yhdestä prosessista
yhtä aikaa. Tällöin on tarpeen pitää kirjaa siitä, mitkä palikat
muistista on milloinkin käytössä, ettei useampi prosessi kirjoittele
samaan kohtaan omia juttujaan.

Ehkä tyypillisin tapa tehdä tämä on:
1. Allokoida köntti.
2. jakaa se pienempiin palasiin, ja tehdä kirjanpito siitä, mitkä
palat ovat käytössä.

Käyttäjän pyytäessä muistia, lasketaan montako palasta käyttäjän
tarvitsema tila vie, etsitään vapaat palat ja merkitään ne
varatuiksi, kirjoitetaan ensimmäisen palan alkuun määrä jonka
käyttäjä varasi ja palautetaan käyttäjälle osoitin tämän "headerin"
(kirjanpidon) jälkeiseen muistiosoitteeseen. Käyttäjä sitten käyttää
muistia miten tahtoo, ja lopulta pyytää vapauttamaan muistin.
Vapautettaessa sitten tutkitaan käyttäjän antamaa "vapautettavaa
osoitetta" edeltävä paikka, johon kirjoitimme varatun määrän, ja
merkitsemme vastaavat palaset jälleen vapaiksi.

Nyt tähän sisäiseen kirjanpitoon voidaan lisätä tila
funktio-osoittimelle, ja antaa palan varanneen käyttäjän rekisteröidä
callback funktio, jonka osoite siis tallennetaan tähän headeriin.
Käyttäjän nyt pyytäessä muistipaikan vapautusta, suoritetaan samalla
Em. callback, ja voila. Meillä on destructoin tapainen ominaisuus
C:ssä. Tämä kuitenkin eroaa destructorista siinä, että mikäli
käyttäjä instantioi otuksensa stackista, eikä näinollen joudu sitä
erikseen vapauttamaan, me emme voi tehdä destructoria
(ainakaan helposti).

LottoEsimerkki:

Ja loppuun vielä esimerkki jonka tekaisin äkkipäätä väitteitäni
tukemaan. Hirveän kauhean inhottavan näköinen lottokötöstys-C-olio,
joka siis arpoo lottonumeroita. Ikävä kyllä, minulla ei ole mitään
muistikuvaa siitä, montako numeroa Viking lotossa oikeasti on, eikä
myöskään jokereiden määrästä, joten lotto-otukseni ei liene ihan
vastaa todellisuutta. (tosin defineitä muuttamalla siitä pitäisi
tulla toimiva ;) ) Ainoa julkinen (käyttäjälle tarkoitettu) headeri
on nyt lotto.h

Huom! Kapselointi paranisi mikäli peruslotto.c ja vikinglotto.c
olisi sisällytetty lotto.c tiedostoon, ja static avainsanaa olisi
käytetty näiden kanssa. Selkeyden vuoksi laitoin ne kuitenkin omiin
tiedostoihinsa, ja tein niistä siis globaaleja funktioita. Lisäksi
tilanne paranisi mikäli muista tiedostoista paitsi lottoTesti.c:stä
käännettäisiin kirjasto, ja käyttäjälle annettaisiin vain kirjasto
ja lotto.h headeri käyttöön.






/* ******************************************************** */
/* *
* Implementation of a lotto engine written in C *
* Main purpose is to demonstrate that C can be used as *
* object oriented programming language. *
* *
* Written by Maz (2009) *
* http://maz-programmersdiary.blogspot.com/ *
* http://c-ohjelmoijanajatuksia.blogspot.com/ *
* *
* You're free to use this piece of code. *
* You can also modify it freely, but if you *
* improve this, you must write the improved code *
* in comments at: *
* http://maz-programmersdiary.blogspot.com/ *
* or at: *
* http://c-ohjelmoijanajatuksia.blogspot.com/ *
* or mail the corrected version to me at *
* Mazziesaccount@gmail.com *
* *
* I do not want to enforce any copyright for using *
* this piece unmodified, but I would be glad if I *
* heard of you - in case this was informative/usefull *
* to you. *
* *
* Revision History: *
* *
* -v0.0.1 16.09.2008/Maz *
* */
/* ******************************************************** */


lotto.h:

#include <stdio.h>

#ifndef LOTTOKONE_H
#define LOTTOKONE_H

struct lottokone;

typedef struct lottotulos
{
int lottoNroMaara;
int jokeriNroMaara;
int *lottonumerot;
int *jokeri;
}lottotulos;

typedef enum ElottoType
{
ElottoType_Perus = 0,
ElottoType_Viking,
ElottoType_PerusJaJokeri,
ElottoType_VikingJaJokeri,
ElottoType_Max
}ElottoType;

struct lottokone *lotto_luo( ElottoType lotonTyyli);
typedef void (*tuhoF)(struct lottokone **lotto);

typedef void (*arvontaF)(struct lottokone *lotto);
typedef lottotulos (*tulosF)(struct lottokone *lotto);

typedef struct lottokone
{
ElottoType ltype;
arvontaF arvo;
tulosF haeArvonnanTulos;
tuhoF lotto_tuhoa;
}lottokone;

#endif


lottoTesti.c:


/* ******************************************************** */
/* *
* Implementation of a lotto engine written in C *
* Main purpose is to demonstrate that C can be used as *
* object oriented programming language. *
* *
* Written by Maz (2009) *
* http://maz-programmersdiary.blogspot.com/ *
* http://c-ohjelmoijanajatuksia.blogspot.com/ *
* *
* You're free to use this piece of code. *
* You can also modify it freely, but if you *
* improve this, you must write the improved code *
* in comments at: *
* http://maz-programmersdiary.blogspot.com/ *
* or at: *
* http://c-ohjelmoijanajatuksia.blogspot.com/ *
* or mail the corrected version to me at *
* Mazziesaccount@gmail.com *
* *
* I do not want to enforce any copyright for using *
* this piece unmodified, but I would be glad if I *
* heard of you - in case this was informative/usefull *
* to you. *
* *
* Revision History: *
* *
* -v0.0.1 16.09.2008/Maz *
* */
/* ******************************************************** */


#include "lotto.h"
#include <stdio.h>
int main(void)
{
int i;
lottokone *lotto;
lottotulos tulos;
lotto=lotto_luo(ElottoType_Perus);
lotto->arvo(lotto);
tulos=lotto->haeArvonnanTulos(lotto);
/*
Ollaksemme Wielä Enemmän OlioOtuksia, voisimme
tunkea tulos structiin sekalaisen joukon funktioita,
joilla arvotut numerot voisi esim. tulostaa
*/
for(i=0;i<tulos.lottoNroMaara;i++)
{
printf("nro %d = %u\n",i+1,tulos.lottonumerot[i]);
}
return 0;
}




lotto_sisainen.h


/* ******************************************************** */
/* *
* Implementation of a lotto engine written in C *
* Main purpose is to demonstrate that C can be used as *
* object oriented programming language. *
* *
* Written by Maz (2009) *
* http://maz-programmersdiary.blogspot.com/ *
* http://c-ohjelmoijanajatuksia.blogspot.com/ *
* *
* You're free to use this piece of code. *
* You can also modify it freely, but if you *
* improve this, you must write the improved code *
* in comments at: *
* http://maz-programmersdiary.blogspot.com/ *
* or at: *
* http://c-ohjelmoijanajatuksia.blogspot.com/ *
* or mail the corrected version to me at *
* Mazziesaccount@gmail.com *
* *
* I do not want to enforce any copyright for using *
* this piece unmodified, but I would be glad if I *
* heard of you - in case this was informative/usefull *
* to you. *
* *
* Revision History: *
* *
* -v0.0.1 16.09.2008/Maz *
* */
/* ******************************************************** */


#ifndef SISAINEN_LOTTO_H
#define SISAINEN_LOTTO_H

#include <stdio.h>
#include "lotto.h"
#include <stdlib.h>

#define LOTTO_NRO_MAARA 10
#define VIKINGLOTTO_NRO_MAARA 6
#define JOKERI_NRO_MAARA 7
#define VIKINGJOKERI_NRO_MAARA 7
#define LOTTO_MAX 45
#define VIKINGLOTTO_MAX 45

typedef struct SlottoPerus
{
lottokone virtlotto;
int jokerimukana;
/* itse asiassa kaikille lottotyypeille
voitaisiin käyttää samaa typedef structia, tai C++
tyyliin, esim jokerimukana muuttuja voitaisiin siirtää
"base" luokkaan (eli lottokone structiin, tai muuhun
yhteiseen (sisäiseen) structiin)
*/
arvontaF arvo;
tulosF haeArvonnanTulos;
char tulosplaceholder[1];
}SlottoPerus;

typedef struct SlottoViking
{
lottokone virtlotto;
int jokerimukana;
arvontaF arvo;
tulosF haeArvonnanTulos;
char tulosplaceholder[1];

}SlottoViking;

typedef struct SperusNumerot
{
int lottoNumerot[LOTTO_NRO_MAARA];
}SperusNumerot;

typedef struct SvikingNumerot
{
int vikingNumerot[VIKINGLOTTO_NRO_MAARA];
}SvikingNumerot;

typedef struct SperusJokeriNumerot
{
int jokeriNumerot[JOKERI_NRO_MAARA];
}SperusJokeriNumerot;

typedef struct SvikingJokeriNumerot
{
int vikinJokeriNumerot[VIKINGJOKERI_NRO_MAARA];
}SvikingJokeriNumerot;


lottotulos vikingtulos(lottokone *siht_);
void vikingarvonta(lottokone *siht_);
void perusarvonta(lottokone *siht_);
lottotulos perustulos(lottokone *siht_);


#endif



lotto.c


/* ******************************************************** */
/* *
* Implementation of a lotto engine written in C *
* Main purpose is to demonstrate that C can be used as *
* object oriented programming language. *
* *
* Written by Maz (2009) *
* http://maz-programmersdiary.blogspot.com/ *
* http://c-ohjelmoijanajatuksia.blogspot.com/ *
* *
* You're free to use this piece of code. *
* You can also modify it freely, but if you *
* improve this, you must write the improved code *
* in comments at: *
* http://maz-programmersdiary.blogspot.com/ *
* or at: *
* http://c-ohjelmoijanajatuksia.blogspot.com/ *
* or mail the corrected version to me at *
* Mazziesaccount@gmail.com *
* *
* I do not want to enforce any copyright for using *
* this piece unmodified, but I would be glad if I *
* heard of you - in case this was informative/usefull *
* to you. *
* *
* Revision History: *
* *
* -v0.0.1 16.09.2008/Maz *
* */
/* ******************************************************** */


//#include "lotto.h"
#include "lotto_sisainen.h"

static lottotulos virtuaalitulos(lottokone *lotto);
static void virtuaaliarvonta(lottokone *_this);

static void lotto_tuhoa(lottokone **lotto)
{
free(*lotto);
lotto=NULL;
}
lottokone *lotto_luo(ElottoType lotonTyyli)
{
void *paluuarvo;
switch(lotonTyyli)
{
case ElottoType_Perus:
{
SlottoPerus *peruslotto;
peruslotto=malloc
(
sizeof(SlottoPerus)+sizeof(SperusNumerot)-1
);
paluuarvo=(void *)peruslotto; if(peruslotto==NULL)
{
printf("lotto_alusta() : pannahinen, muisti loppu?");
break;
}
peruslotto->virtlotto.ltype=lotonTyyli;
peruslotto->virtlotto.lotto_tuhoa=&lotto_tuhoa;
peruslotto->virtlotto.arvo=&virtuaaliarvonta;
peruslotto->virtlotto.haeArvonnanTulos=&virtuaalitulos;
peruslotto->jokerimukana=0; //tämä on kyllä huijausta, en jakasa leikkiä olioihmistä ja tehdä jokeri oliota, tahi eri oliota lotolle ja lotolle jokerin kera...
peruslotto->arvo=&perusarvonta;
peruslotto->haeArvonnanTulos=&perustulos;
break;
}
case ElottoType_Viking:
{
SlottoViking *vikinglotto;
vikinglotto=malloc
(
sizeof(SlottoViking)+
sizeof(SvikingNumerot)-1
);
paluuarvo=(void *)vikinglotto;
if(vikinglotto==NULL)
{
printf("lotto_alusta() : pannahinen, muisti loppu?");
break;
}
vikinglotto->virtlotto.ltype=lotonTyyli;
vikinglotto->virtlotto.lotto_tuhoa=&lotto_tuhoa;
vikinglotto->virtlotto.arvo=&virtuaaliarvonta;
vikinglotto->virtlotto.haeArvonnanTulos=&virtuaalitulos;
vikinglotto->jokerimukana=0;
/*
tämä on kyllä huijausta, en jakasa leikkiä olioihmistä ja
tehdä jokeri oliota,
tahi eri oliota lotolle ja lotolle jokerin kera...
*/
vikinglotto->arvo=&vikingarvonta;
vikinglotto->haeArvonnanTulos=&vikingtulos;
break;
}

break;
case ElottoType_PerusJaJokeri:
{
SlottoPerus *peruslotto;
peruslotto=malloc
(
sizeof(SlottoPerus)+
sizeof(SperusNumerot)+
sizeof(SperusJokeriNumerot)-1
);
paluuarvo=(void *)peruslotto;
if(peruslotto==NULL)
{
printf("lotto_alusta() : pannahinen, muisti loppu?");
break;
}
peruslotto->virtlotto.ltype=lotonTyyli;
peruslotto->virtlotto.arvo=&virtuaaliarvonta;
peruslotto->virtlotto.lotto_tuhoa=&lotto_tuhoa;
peruslotto->virtlotto.haeArvonnanTulos=&virtuaalitulos;
peruslotto->jokerimukana=1;
//tämä oli myös huijausta
peruslotto->arvo=&perusarvonta;
peruslotto->haeArvonnanTulos=&perustulos;
break;
}

break;
case ElottoType_VikingJaJokeri:
{
SlottoViking *vikinglotto;
vikinglotto=malloc
(
sizeof(SlottoViking)+
sizeof(SvikingNumerot)+
sizeof(SvikingJokeriNumerot)-1
);
paluuarvo=(void *)vikinglotto;
if(vikinglotto==NULL)
{
printf("lotto_alusta() : pannahinen, muisti loppu?");
break;
}
vikinglotto->virtlotto.ltype=lotonTyyli;
vikinglotto->virtlotto.lotto_tuhoa=&lotto_tuhoa;
vikinglotto->virtlotto.arvo=&virtuaaliarvonta;
vikinglotto->virtlotto.haeArvonnanTulos=&virtuaalitulos;
vikinglotto->jokerimukana=1;
vikinglotto->arvo=&vikingarvonta;
vikinglotto->haeArvonnanTulos=&vikingtulos;
break;
}
break;
default:
printf
(
"lotto_alusta() : Pannahinen, annoit olemattoman lottotyypin!\n"
);
paluuarvo=NULL;
break;
}
return (lottokone *)paluuarvo;

}

static void virtuaaliarvonta(lottokone *_this)
{
if(*(unsigned int*)_this<ElottoType_Max)
{
/*
Jos tekisin tätä helpolla ja yksinkertaisella C -
tyylillä, tässä olisi hyppytaulu eri
lottoarvontafunktioihin, indexoituna
ElottoType:llä

koska tämän on kuitenkin tarkoitus matkia C++:n
virtuaaliluokkaa, kutsutaan täältä "aliluokan
sisällä olevaa" funktiota. Nyt siis tämä if-else
runko olisi voitu korvata seuraavalla switch
rakenteella, mutta if-else rakenne on täällä,
jotta hyppytaulun käyttöidea näkyisi. Ts. tässä
voisi olla:
arvontaF arvontafunktiot[ElottoType_Max];
arvontafunktiot[ElottoType_Perus]=
&perusarvontafunktio;
arvontafunktiot[ElottoType_Viking]=
&viikinkiarvontafunktio;
arvontafunktiot[ElottoType_PerusJaJokeri]=
&perusjokeriarvontafunktio;
arvontafunktiot[ElottoType_VikingJaJokeri]=
&viikinkijokeriarvontafunktio;
*arvontafunktiot[*(unsigned int*)_this](_this);
*/
switch(*(unsigned int*)_this<ElottoType_Max)
{
case ElottoType_Perus:
return ((SlottoPerus*)_this)->arvo(_this);
break;
case ElottoType_Viking:
return ((SlottoViking*)_this)->arvo(_this);
break;
case ElottoType_PerusJaJokeri:
return ((SlottoPerus*)_this)->arvo(_this);
break;
case ElottoType_VikingJaJokeri:
return ((SlottoViking*)_this)->arvo(_this);
break;
default:
printf("Mahdoton Tapahtui!!! %s:%d\n",__FILE__,__LINE__);
fflush(stdout);
exit(-1);
break;
}
}
else
{
printf
(
"lottoarvonta : pannahinen, joku meni poskelleen, lotto olio viallinen!"
);
fflush(stdout);
exit(-1);
}
}
static lottotulos virtuaalitulos(lottokone *_this)
{
switch(*(unsigned int*)_this<ElottoType_Max)
{
case ElottoType_Perus:
return ((SlottoPerus*)_this)->haeArvonnanTulos(_this);
break;
case ElottoType_Viking:
return ((SlottoViking*)_this)->haeArvonnanTulos(_this);
break;
case ElottoType_PerusJaJokeri:
return ((SlottoPerus*)_this)->haeArvonnanTulos(_this);
break;
case ElottoType_VikingJaJokeri:
return ((SlottoViking*)_this)->haeArvonnanTulos(_this);
break;
default:
printf
(
"lottoHaeTulos : pannahinen, joku meni poskelleen, lotto olio viallinen!"
);
fflush(stdout);
exit(-1);
break;
}
}



peruslotto.c



/* ******************************************************** */
/* *
* Implementation of a lotto engine written in C *
* Main purpose is to demonstrate that C can be used as *
* object oriented programming language. *
* *
* Written by Maz (2009) *
* http://maz-programmersdiary.blogspot.com/ *
* http://c-ohjelmoijanajatuksia.blogspot.com/ *
* *
* You're free to use this piece of code. *
* You can also modify it freely, but if you *
* improve this, you must write the improved code *
* in comments at: *
* http://maz-programmersdiary.blogspot.com/ *
* or at: *
* http://c-ohjelmoijanajatuksia.blogspot.com/ *
* or mail the corrected version to me at *
* Mazziesaccount@gmail.com *
* *
* I do not want to enforce any copyright for using *
* this piece unmodified, but I would be glad if I *
* heard of you - in case this was informative/usefull *
* to you. *
* *
* Revision History: *
* *
* -v0.0.1 16.09.2008/Maz *
* */
/* ******************************************************** */


#include <stdlib.h>
#include <time.h>
#include <string.h>
#include "lotto_sisainen.h"

lottotulos perustulos(lottokone *siht_)
{
SlottoPerus *_this = (SlottoPerus *)siht_;
lottotulos tulos;
memset(&tulos,0,sizeof(lottotulos));
if
(
*(unsigned int*)_this!=ElottoType_Perus &&
*(unsigned int*)_this!=ElottoType_PerusJaJokeri
)
{
printf("perustuloshae : lottokone rikki, soittakaa Wirallisille Walvojille!!");
fflush(stdout);
exit(-1);
}
if(_this->jokerimukana)
{
tulos.jokeriNroMaara=JOKERI_NRO_MAARA;
tulos.jokeri=
&(((int *)_this->tulosplaceholder)[LOTTO_NRO_MAARA]);
}
tulos.lottoNroMaara=LOTTO_NRO_MAARA;
tulos.lottonumerot=(int *)(_this->tulosplaceholder);
return tulos;
}
void perusarvonta(lottokone *siht_)
{
SlottoPerus *_this = (SlottoPerus *)siht_;

int i, ok;

srand(time(NULL));
if
(
*(unsigned int*)_this!=
ElottoType_Perus &&
*(unsigned int*)_this!=
ElottoType_PerusJaJokeri
)
{
printf
(
"perusarvonta : lottokone rikki, soittakaa Wirallisille Walvojille!!"
);
fflush(stdout);
exit(-1);
}
for(i=0;i<LOTTO_NRO_MAARA;i++)
{
ok=0;
while(!ok)
{
int j;
ok=1;
((int *)_this->tulosplaceholder)[i]=
(rand()%LOTTO_MAX+1);
//varmista että arvottu numero on uniikki
for(j=0;j<i;j++)
{
if
(
((int *)_this->tulosplaceholder)[i] ==
((int *)_this->tulosplaceholder)[j]
)
{
//sama numero arvottiin kahteen kertaan => hylkää!
ok=0;
//koska tupla-arvo löytyi jo, ei kannata jatkaa.
break;
}
}
}
}
if(_this->jokerimukana)
{
for(i=0;i<JOKERI_NRO_MAARA;i++)
{
ok=0;
while(!ok)
{
int j;
ok=1;
((int *)_this->tulosplaceholder)[LOTTO_NRO_MAARA+i]=
(rand()%LOTTO_MAX+1);
//varmista että arvottu numero on uniikki
for(j=0;j<i;j++)
{
if(
((int *)_this->tulosplaceholder)[LOTTO_NRO_MAARA+i]==
((int *)_this->tulosplaceholder)[LOTTO_NRO_MAARA+j]
)
{
//sama numero arvottiin kahteen kertaan => hylkää!
ok=0;
//koska tupla-arvo löytyi jo, ei kannata jatkaa.
break;
}
}
}
}
}
}





vikinglotto.c



/* ******************************************************** */
/* *
* Implementation of a lotto engine written in C *
* Main purpose is to demonstrate that C can be used as *
* object oriented programming language. *
* *
* Written by Maz (2009) *
* http://maz-programmersdiary.blogspot.com/ *
* http://c-ohjelmoijanajatuksia.blogspot.com/ *
* *
* You're free to use this piece of code. *
* You can also modify it freely, but if you *
* improve this, you must write the improved code *
* in comments at: *
* http://maz-programmersdiary.blogspot.com/ *
* or at: *
* http://c-ohjelmoijanajatuksia.blogspot.com/ *
* or mail the corrected version to me at *
* Mazziesaccount@gmail.com *
* *
* I do not want to enforce any copyright for using *
* this piece unmodified, but I would be glad if I *
* heard of you - in case this was informative/usefull *
* to you. *
* *
* Revision History: *
* *
* -v0.0.1 16.09.2008/Maz *
* */
/* ******************************************************** */


#include <stdlib.h>
#include <time.h>
#include <string.h>

#include "lotto_sisainen.h"

lottotulos vikingtulos(lottokone *siht_)
{
SlottoViking *_this = (SlottoViking *)siht_;
lottotulos tulos;
memset(&tulos,0,sizeof(lottotulos));
if
(
*(unsigned int*)_this!=
ElottoType_Perus &&*(unsigned int*)_this!=
ElottoType_PerusJaJokeri
)
{
printf
(
"perustuloshae : lottokone rikki, soittakaa Wirallisille Walvojille!!"
);
fflush(stdout);
exit(-1);
}
if(_this->jokerimukana)
{
tulos.jokeriNroMaara=JOKERI_NRO_MAARA;
tulos.jokeri=
&(((int *)_this->tulosplaceholder)[VIKINGLOTTO_NRO_MAARA]);
}
tulos.lottoNroMaara=VIKINGLOTTO_NRO_MAARA;
tulos.lottonumerot=(int *)(_this->tulosplaceholder);
return tulos;
}
void vikingarvonta(lottokone *siht_)
{
SlottoViking *_this = (SlottoViking *)siht_;

int i, ok;

srand(time(NULL));
if
(
*(unsigned int*)_this!=
ElottoType_Viking &&*(unsigned int*)_this!=
ElottoType_VikingJaJokeri
)
{
printf
(
"perusarvonta : lottokone rikki, soittakaa Wirallisille Walvojille!!"
);
fflush(stdout);
exit(-1);
}
for(i=0;i<VIKINGLOTTO_NRO_MAARA;i++)
{
ok=0;
while(!ok)
{
int j;
ok=1;
((int *)_this->tulosplaceholder)[i]=
(rand()%LOTTO_MAX+1);
//varmista että arvottu numero on uniikki
for(j=0;j<i;j++)
{
if
(
((int *)_this->tulosplaceholder)[i]==
((int *)_this->tulosplaceholder)[j]
)
{
//sama numero arvottiin kahteen kertaan => hylkää!
ok=0;
//koska tupla-arvo löytyi jo, ei kannata jatkaa.
break;
}
}
}
}
if(_this->jokerimukana)
{
for(i=0;i<VIKINGJOKERI_NRO_MAARA;i++)
{
ok=0;
while(!ok)
{
int j;
ok=1;
((int *)_this->tulosplaceholder)[VIKINGLOTTO_NRO_MAARA+i]=
(rand()%LOTTO_MAX+1);
//varmista että arvottu numero on uniikki
for(j=0;j<i;j++)
{
if
(
((int *)_this->tulosplaceholder)[VIKINGLOTTO_NRO_MAARA+i]==
((int *)_this->tulosplaceholder)[VIKINGLOTTO_NRO_MAARA+j]
)
{
//sama numero arvottiin kahteen kertaan => hylkää!
ok=0;
//koska tupla-arvo löytyi jo, ei kannata jatkaa.
break;
}
}
}
}
}
}



Kääntäminen gcc:llä onnistuu käskyllä:

gcc -Wall -o lottoEsimerkki lottoTesti.c lotto.c peruslotto.c vikinglotto.c


Ja vielä teille jotka kahlasitte tänne asti...

Eli, loppusanat:

C++ on hyvä kieli, ja olio-ohjelmointi on tuonut joitain hienoja piirteitä tullessaan. Mutta Olio-Ohjelmointi ei automaattisesti tarkoita hyvää asiaa. Monet olio-ohjelmoijat ovat uponneet liian syvälle oliokuoppaansa, eivätkä näe enää metsää puilta. Liian pitkälle menevä perintä lienee yksi kauheimmista asioista mitä tiedän. Olen joskus joutunut porautumaan oliopohjalta tehtyyn koodiin, ja tekemään sen päälle jotain.. Usein huomasin vasta päiviä puuhasteltuani, että sama asia jota tein kaikkien periytettyjen oliokerrosten päälle, olijo (<=kirjoitusvirhe tarkoituksellinen) toteutettu jossain alempien kerrosten syövereissä. Ja kaikenkaikkiaan, nykyään tuntuu, että Olio-ohjelmointi nähdään liian usein itse tarkoituksena, ei suinkaan työkaluna joksi se on tarkoitettu. On tilanteita joissa oliokielet ovat hyviä, mutta on myös tilanteita joissa proseduraalisella kielellä saavutetaan tavoite paljon pienemmällä koodimäärällä, yksinkertaisemmin, ylläpidettävämmin ja ymmärrettävämmin. Siis muistakaa tytöt ja pojat, (niin, sinäkin Vesa): Olio-ohjelmoijien ja ohjelmoinnin päätarkoitus ei oikeasti ole vain kikkailla ja keksiä entistä hienompia olio-ismejä.

Ja viimeinen kommentti. Koodiesimerkit tuppaavat menemään rikki tässä html wysiwyg maailmassa - olen pahoillani. Koetan saada lottokone-esimerkin pakattua zippiin, ja jonnekkin palvelimelle, mikäli joku haluaa oikeasti nähdä sen / koettaa sitä :)

6 kommenttia:

Maz kirjoitti...

Koska kävijöitä näyttää kiinnostaneen olio-ohjelmointi ja C kieli, sekä näiden yhdistelmä (ainakin hakusanojen perusteella), minua alkoi kiinnostaa, että ovatkohan kävijät löytäneet etsimänsä, ja mitä he ovat etsineet :) Siksipä lisäsin "copyright" huomautuksen koodeihini.

(Ja erityisen kiinnostunut olen siitä, rohkeniko joku kopioida koodini harjoitustyön ratkaisuksi johonkin oppilaitokseen. Sekä erityisesti sen, mitä opettajanne sanoi... )

Anonyymi kirjoitti...

hirveetä tuubaa :D Koodin luettavuus?

Anonyymi kirjoitti...

lupaavasti

Maz kirjoitti...

"hirveetä tuubaa :D Koodin luettavuus?" -
Totta. Olio-ohjelmointi on helposti hirveetä tuubaa. Mutta C on usein huomattavasti luettavampaa kuin C++ - varsinkin jos C++ koodin on kirjoittanut joku, joka kuvittelee olevansa cool käyttäessään kaikkia hienoja olioaatteita koodissaan - osaamatta kuitenkaan ajatella lopputuloksen ymmärrettävyyttä.

Mutta toisinaan olio-ajattelu helpottaa tekemistä ja ylläpitoa, myös silloin kun kysessä on C, ei C++.

Maz kirjoitti...

Joskaan ei tässä tapauksessa ;)

Anonyymi kirjoitti...

Äskettäin Suunto haki C-koodaria. Heidän ilmoituksessaan oli että he käyttävät C-koodia
"in object oriented manner."