Kirjastot¶
Kirjastona ajatellaan mitä tahansa koodimodulin ulkopuolista C-koodia, jota haluamme uudelleenkäyttää. Tyypillisesti kirjastot sisältävät monikäyttöisiä funktioita, esim. matemaattisia funktioita, joille on useita eri käyttötapauksia Varsinainen kirjaston koodi voi olla C-kielinen ohjelma, ts. lähdekooditiedosto
pasta.c
, tai se voi olla valmiiksi käännettyä koodia (ts. binääri pasta.o
), joka linkitetään ohjelmaamme käännösprosessin aikana. Kirjaston esittely¶
Kirjastolla on aina oma otsikkotiedosto. Otsikkotiedostossa esitellään kirjaston ne funktiot, globaalit muuttujat ja vakiot (ja makrot), joita kirjastoa käyttävät ohjelmat saavat käyttöönsä. Otsikkotiedoston lisääminen kääntäjän näkökulmasta on sama kuin että otsikkotiedoston koodit kopioisi suoraan #include -käskyn tilalle kooditiedostoon.
Otsikkotiedosto
pasta.h
voisi olla esimerkiksi seuraava:#ifndef __PASTA_H_ // Hox! Vakiolla estetään uudelleenkäännös
#define __PASTA_H_
#include <stdio.h>
// Funktioiden prototyypit
uint8_t lisaa_vesi(void);
uint8_t lisaa_suola(void);
uint8_t kiehuuko_vesi(void);
void hammenna_kattilaa(void);
void aseta_keittoaika(void);
uint8_t onko_valmista(void);
// Uusi muuttujatyyppi vakioille, jännää!!
enum pasta_types { SPAGETTI=1, MAKARONI, FUSILLI, PENNE_RIGATE };
// Globaalit muuttujat
uint8_t _annoskoko; // Tämä muuttuja alustetaan pääohjelmassa
uint8_t _valittu_pasta = SPAGETTI;
#endif
Muuttujat otsikkotiedostossa¶
Muuttujien esittely ja/tai globaalien muuttujien käyttäminen suoraan otsikkotiedostojen kautta on hieman kyseenalaista, parempaa koodia olisi tehdä erilliset asetus- ja lukufunktiot tällaisille muuttujille. Näin muuttuja itse olisi kapseloituna kirjastomoduulin sisäiseksi muuttujaksi, jolloin muuttujan käyttö muissa moduleissa pysyy paremmin hanskassa.
void set_annoskoko(uint8_t annoksia) {
_annoskoko = annoksia;
}
uint8_t get_annoskoko(void) {
return _annoskoko;
}
Jos vakio on esitelty otsikkotiedostossa, se voidaan ottaa vapaasti käyttöön useissa eri koodimoduleissa. Esimerkiksi,
math.h
-otsikkotiedostossa on piin arvolle määritelty vakio, jonka pitäisi tietysti olla koko ohjelmassa.// math.h:n sisällä..
#define M_PI 3.14159265358979323846
Tämän vakion käyttö omassa ohjelmassa ei tosin toimi, ellemme ole ensin määritelleet vakiota
#define _USE_MATH_DEFINES
.Kirjaston toteutus¶
Kirjaston lähdekoodi tehdään omaan koodimoduliinsa, ts. tiedostoon (tässä
pasta.c
), joka se sisältää sitten kirjastojen jaettujen ja sisäisten funktioiden toteutukset. #include "pasta.h" // Pakottaa kirjaston funktiot noudattamaan prototyyppejä
// Kirjaston sisäiset muuttujat
uint8_t _vesimaara = 0;
uint8_t _keittoaika = 0;
uint8_t _annoskoko = 0; // Siirsimme muuttujan tänne otsikkotiedostosta!!
// Funktiot
void set_annoskoko(uint8_t annoksia) {
_annoskoko = annoksia;
}
uint8_t get_annoskoko(void) {
return _annoskoko;
}
uint8_t lisaa_vesi(void) {
_vesimaara = _annoskoko * 0.2;
}
void aseta_keittoaika(void) {
switch(_valittu_pasta) {
case SPAGETTI:
_keittoaika = 10;
break;
case MAKARONI:
_keittoaika = 8;
break;
...
}
}
C-kielessä ei kirjastofunktiota käytettäessä tarvitse määritellä, minkä kirjaston funktio on kyseessä. Tietenkään eri kirjastoissa ei saisi olla samannimisiä funktioita, jotta kääntäjä ei mene sekaisin että mitäs funktiota tässä ollaankaan kutsumassa..
C:n stardardikirjasto¶
C:n standardikirjasto sisältää ensinnäkin iso joukon määrittelyjä, jotka tukevat (ANSI-)standardin mukaisen C-kielen toteutusta eri laitealustoilla ja käyttöjärjestelmissä.
Standardikirjaston osien toteutus tulee (varsinkin kaupallisten) kääntäjäympäristöjen mukana valmiiksi binääriksi käännettynä ja tällöin toteutus on usein valmistajan optimoima. Näin ollen, valmiita funktioita kannattaa siis käyttää aina kuin mahdollista! Pyörän uudelleenkeksimisestä, varsinkaan siellä työelämässä sitten, ei saa pomolta bonuspisteitä.
C-kielen standardikirjastoista löytyy yleiskuvaus mm. Wikipediasta C:n Standardikirjasto tai oppikirjoista.
Muutamia hyödyllisiä kirjastoja:
- inttypes.h: Hyödyllisiä johdettuja muuttujatyyppejä
- stdio.h: Luku- ja kirjoitusfunktioita, tiedostonkäsittely
- stdlib.h: Tyyppimuunnokset, muistinkäsittely, järjestelmäkomentoja
- string.h: Merkkijonojen käsittely
- ctype.h: Yksittäisen merkin käsittely
- time.h: Kello ja kalenteri (yleensä ei toteutusta sulautetuissa järjestelmissä)
- math.h: Matemaattisia funktioita
Sulautetuissa järjestelmissä standardikirjastosta joudutaan laitteen suorituskyky- ja muistirajoitusten vuoksi jättämään joskus paljonkin osia pois. Tyypillisesti esimerkiksi
stdio.h
-kirjastoa on optimoitu siten, että rajoitetaan liukulukujen tarkkuutta ja printf
-funktiossa ole toteutettu liukulujen tulostamista.. Yleistä kirjastoista¶
Ohjelmakokonaisuuden kasvaessa funktiot kannattaa jaotella kirjastoiksi, joka selkeyttää pääohjelmaa huomattavasti, tekee siitä helpommin hallittavan ja ylläpidettävän.
Kirjastoihin jako myös helpottaa testausta ja virheenmetsästystä (ts. debuggausta) laajemmissa ohjelmissa. Jokainen kirjasto voidaan moduulina testata erikseen ennen sen käyttöönottoa ohjelmassa. Ajonaikaisia virheitä metsästäessä kirjastoja voidaan debuggerissa tarkastella erillisenä kokonaisuutena.
Ohjelmia voi jakaa kirjastoihin usealla eri tapaa, ja se paras tapa riippuu paljolti siitä keneltä asiasta kysyy. Tässä on esitelty yksi näkemys:
- Alustakohtaiset funktiot ja määrittelyt: sulautettujen ohjelmoinnissa laitealustalle täytyy yleensä kirjoittaa hyvinkin spesifejä funktioita, joille ei - ainakaan ilman suurempaa muokkausta - ole muille laitealustoille ohjelmoitaessa mitään hyötyä. Tällaiset laitekohtaiset funktiot ja määrittelyt kannattaa sijoittaa omaan kirjastoonsa, jolloin samaa ohjelmaa toiselle alustalle kääntäessä lähdekoodissa ei ole ns. ylimääräistä tavaraa.
- Sovelluskohtaiset funktiot ja määrittelyt: Ohjelmoidessa on aina hyvä pyrkiä modulaarisuuteen, mutta kaikista funktioista on hyvin vaikeaa tehdä mahdollisimman yleiskäyttöisiä eikä niiden sellaiseksi pakottaminenkaan ole järkevää. Tällaiset sovelluskohtaiset funktiot ja määrittelyt on hyvä sijoittaa omaan kirjastoonsa.
- Yleiskäyttöiset funktiot ja määrittelyt: Funktiot ja määrittelyt joita uskot voivasi käyttää jossakin toisessa ohjelmistoprojektissa jollekin toiselle laitteelle kannattaa sijoittaa omaan kirjastoonsa.
Lopuksi¶
Kurssilla tulemme (paljonkin) käyttämään standardkikirjastoa sekä CCS:n ja SensorTag:n reaaliaikakäyttöjärjestelmän RTOS:n tarjoamia valmiita kirjastoja laitteen ohjelmoinnissa. SensorTagin oheislaitteiden ohjaamiseen pitää yleensä käyttää erilaisia konfiguraatioparametrejä, eikä niiden uudelleenmäärittely- ja kirjoittaminen ole mielekästä.
Myös harrastelijat toteuttavat kirjastoja eri toiminnalisuuksille ja sulautettujen oheiskomponenteille, joten Google-haku on tässä kaveri. Tosin, usein on syytä pieneen varovaisuuteen, kun ei tiedä mitä oletuksia tai muita ehtoja kirjaston koodiin on asetettu. Eli ulkopuolinen koodi on hyvä käydä läpi ensin itse, ennekuin sen ottaa käyttöön omassa ohjelmassa. Lisäksi, koodin kommenteissa voi esimerkiksi olla arvokasta tietoa kirjaston käytöstä.
Anna palautetta
Kommentteja materiaalista?