ˇ ´ vysoke ´ uc ˇen´ı technicke ´ v Praze Cesk e Fakulta stavebn´ı
´r ˇska ´ pra ´ ce Bakala
Praha 2011
V´aclav Petr´aˇs
ˇ ´ vysoke ´ uc ˇen´ı technicke ´ v Praze Cesk e Fakulta stavebn´ı Obor geoinformatika
´r ˇska ´ pra ´ ce Bakala ´ ze SQLite Podpora databa pro program gama-local Support of SQLite database in program gama-local
ˇ Vedouc´ı pr´ace: prof. Ing. Aleˇs Cepek, CSc. Katedra mapov´an´ı a kartografie Praha 2011
V´aclav Petr´aˇs
ČESKÉ VYSOKÉ UČENÍ TECHNICKÉ V PRAZE Fakulta stavební Thákurova 7, 166 29 Praha 6
ZADÁNÍ
BAKALÁŘSKÉ
studijní program:
Geodézie a kartografie
studijní obor:
Geoinformatika
akademický rok:
2010/2011
PRÁCE
Jméno a příjmení studenta: Václav Petráš Zadávající katedra:
Katedra mapování a kartografie
Vedoucí bakalářské práce:
prof. Ing. Aleš Čepek, CSc.
Název bakalářské práce: Název bakalářské práce v anglickém jazyce
Podpora databáze SQLite pro program gama-local Support of SQLite database in program gama-local
Rámcový obsah bakalářské práce: Navrhněte a implementujte podporu databáze SQLite pro program gama-local pro vyrovnání geodetických sítí projektu GNU Gama. Pro komunikaci s databází použijte nativní C/C++ rozhraní databáze. Pro implementaci použijte techniku callback funkcí. Datum zadání bakalářské práce:
Termín odevzdání:
13. 5. 2011 (vyplňte poslední den výuky příslušného semestru)
Pokud student neodevzdal bakalářskou práci v určeném termínu, tuto skutečnost předem písemně zdůvodnil a omluva byla děkanem uznána, stanoví děkan studentovi náhradní termín odevzdání bakalářské práce. Pokud se však student řádně neomluvil nebo omluva nebyla děkanem uznána, může si student zapsat bakalářskou práci podruhé. Studentovi, který při opakovaném zápisu bakalářskou práci neodevzdal v určeném termínu a tuto skutečnost řádně neomluvil nebo omluva nebyla děkanem uznána, se ukončuje studium podle § 56 zákona o VŠ č. 111/1998. (SZŘ ČVUT čl. 21, odst. 4) Student bere na vědomí, že je povinen vypracovat bakalářskou práci samostatně, bez cizí pomoci, s výjimkou poskytnutých konzultací. Seznam použité literatury, jiných pramenů a jmen konzultantů je třeba uvést v bakalářské práci. ....................................................... vedoucí bakalářské práce
....................................................... vedoucí katedry
ˇ ORIGINALN ´ ´I ZADAN ´ ´I ZDE VLO ZIT Zadání bakalářské práce převzal dne: ....................................................... student
Formulář nutno vyhotovit ve 3 výtiscích – 1x katedra, 1x student, 1x studijní odd. (zašle katedra) Nejpozději do konce 2. týdne výuky v semestru odešle katedra 1 kopii zadání BP na studijní oddělení a provede zápis údajů týkajících se BP do databáze KOS. BP zadává katedra nejpozději 1. týden semestru, v němž má student BP zapsanou. (Směrnice děkana pro realizaci studijních programů a SZZ na FSv ČVUT čl. 5, odst. 7)
Abstrakt Program gama-local je souˇc´ast´ı projektu GNU Gama a umoˇzn ˇuje vyrovn´ an´ı lok´ aln´ıch geodetick´ ych s´ıt´ı. C´ılem t´eto bakal´aˇrsk´e pr´ace je n´avrh a implementace podpory souborov´e datab´aze SQLite v programu gama-local. V souˇcasn´e dobˇe program gama-local podporuje jedin´ y vstupn´ı form´at, a to XML. D´ıky t´eto pr´ aci z´ısk´a moˇznost ˇc´ıst vstupn´ı data i z datab´aze SQLite. Pr´ ace se zab´ yv´ a specifiky pouˇzit´ı callback funkc´ı v C++ pˇri uˇzit´ı nativn´ıho rozhran´ı datab´ aze SQLite, kter´e je oznaˇcov´ano jako SQLite C/C++ API. Souˇc´ ast´ı pr´ ace jsou i testy novˇe vytvoˇren´e verze programu gama-local. Kl´ıˇ cov´ a slova: GNU Gama, vyrovn´an´ı geodetick´ ych s´ıt´ı, programov´an´ı, C, C++, datab´ aze, SQLite
Abstract The program gama-local is a part of GNU Gama project and allows adjustment of local geodetic networks. The aim of this bachelor thesis is to design and implement support for the SQLite database in the program gama-local. Currently, the program gama-local supports only XML as an input. Thanks to this thesis program can read input data from the SQLite database. The thesis deals with the specifics of the use of callback functions in C++ using the native SQLite C/C++ Application Programming Interface. Tests of newly developed version of gama-local are also part of this thesis. Keywords: GNU Gama, adjustment of geodetic networks, programming, C, C++, databases, SQLite
Prohl´aˇsen´ı Prohlaˇsuji, ˇze bakal´aˇrskou pr´aci na t´ema Podpora datab´aze SQLite pro pro” gram gama-local“ jsem vypracoval samostatnˇe. Pouˇzitou literaturu a podkladov´e materi´aly uv´ad´ım v seznamu zdroj˚ u. V Praze dne .................
.................................. (podpis autora)
Podˇekov´an´ı Dˇekuji sv´e rodinˇe a bl´ızk´ ym, jenˇz mi jsou oporou po celou dobu studia. Na tomto m´ıstˇe vˇsak pˇredevˇs´ım dˇekuji vedouc´ımu m´e bakal´aˇrsk´e pr´ace prof. Ing. Aleˇsovi ˇ Cepkovi, CSc. za pomoc, inspiraci a hlavnˇe za spolupr´aci, bez n´ıˇz by nebylo moˇzn´e plnohodnotn´e dokonˇcen´ı m´e pr´ace.
Obsah ´ Uvod
9
1 Program gama-local
11
1.1
Podporovan´e form´aty . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.2
Pouˇz´ıv´an´ı . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.3
Rozˇs´ıˇren´ı o podporu SQLite datab´aze . . . . . . . . . . . . . . . . . . 13
1.4
Datab´azov´e sch´ema . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2 SQLite C/C++ API
14
2.1
Klasick´e rozhran´ı . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.2
Rozhran´ı s callback funkcemi
3 Propojen´ı C a C++
. . . . . . . . . . . . . . . . . . . . . . 17 20
3.1
Funkce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.2
Ukazatele na funkce . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.3
Viditelnost funkc´ı . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 3.3.1
Deklarace extern "C" a prostor jmen . . . . . . . . . . . . . . 24
3.3.2
Deklerace extern "C" a viditelnost . . . . . . . . . . . . . . . 25
3.3.3
Pouˇzit´ı pˇri implementaci tˇr´ıdy SqliteReader . . . . . . . . . 27
3.4
Pˇred´av´an´ı objekt˚ u . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.5
Zpracov´an´ı v´ yjimek . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.6
Komplexn´ı ˇreˇsen´ı pomoc´ı ˇsablon a maker . . . . . . . . . . . . . . . . 31
4 Soukrom´ a implementace
34
5 Polymorfn´ı pr´ ace s v´ yjimkami
36
5.1
Klonov´an´ı . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
5.2
Ukl´ad´an´ı a vyvol´av´an´ı v´ yjimek . . . . . . . . . . . . . . . . . . . . . 38
5.3
Implementace ve tˇr´ıdˇe SqliteReader . . . . . . . . . . . . . . . . . . 41
6 Tˇ r´ıda SqliteReader a jej´ı implementace
45
6.1
Rozhran´ı . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
6.2
Implementace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
6.3
Integrace do gama-local . . . . . . . . . . . . . . . . . . . . . . . . . . 48
7 Testov´ an´ı
49
7.1
Kontroly pomoc´ı kompil´atoru GCC . . . . . . . . . . . . . . . . . . . 50
7.2
Testov´an´ı pomoc´ı programu diff . . . . . . . . . . . . . . . . . . . . . 51
7.3
Testov´an´ı pomoc´ı programu gama-local-cmp . . . . . . . . . . . . . . 53
Z´ avˇ er
56
Pouˇ zit´ e zdroje
57
Seznam pouˇ zit´ ych zkratek
58
Seznam pˇ r´ıloh
59
A Datab´ azov´ e sch´ ema gama-local-schema.sql
60
B Dokumentace tˇ r´ıdy SqliteReader
63
C Pouˇ zit´ a nastaven´ı kompil´ atoru GCC
75
ˇ CVUT Praha
´ UVOD
´ Uvod Velk´a ˇc´ast geod´ezie se zab´ yv´a zpracov´an´ım namˇeˇren´ ych hodnot, jako jsou napˇr´ıklad d´elky a u ´hly. V´ ysledkem jsou vˇetˇsinou souˇradnice bod˚ u na povrchu Zemˇe. Zpracov´an´ı je tvoˇreno v´ ypoˇcty, kter´e dnes vˇsechny m˚ uˇzeme zahrnout pod pojem vyrovn´an´ı lok´aln´ı geodetick´e s´ıtˇe. Lok´aln´ı s´ıt´ı se m´ın´ı s´ıt’ vyrovn´avan´a v kart´ezsk´e soustavˇe souˇradnic. Vyrovn´an´ı geodetick´ ych s´ıt´ı umoˇzn ˇuje program gama-local , kter´ y je souˇca´st´ı projektu GNU Gama. Zdrojov´e k´ody projektu GNU Gama jsou volnˇe dostupn´e pod licenc´ı GNU GPL. Vstupem i v´ ystupem programu gama-local jsou v souˇcasn´e dobˇe soubory ve znaˇckovac´ım jazyce XML (Extensible Markup Language). Vstupn´ı soubor obsahuje hodnoty mˇeˇren´ ych veliˇcin a v´ ystupn´ı soubor obsahuje vypoˇcten´e (tj. vyrovnan´e) hodnoty nezn´am´ ych veliˇcin. Program se ovl´ad´a z pˇr´ıkazov´e ˇra´dky. C´ılem t´eto bakal´aˇrsk´e pr´ace je umoˇznit programu gama-local ˇc´ıst data z datab´aze SQLite verze 3. Datab´aze SQLite je souborov´a datab´aze, coˇz znamen´a, ˇze data nejsou spravov´ana datab´azov´ ym serverem, ale jsou uloˇzena v jednom souboru. Form´at souboru je nez´avisl´ y na poˇc´ıtaˇcov´e platformˇe, coˇz z datab´aze SQLite ˇcin´ı vhodn´ y n´astroj pro spr´avu a pˇrenos strukturovan´ ych dat mezi r˚ uzn´ ymi syst´emy a programy (nejen v oboru geod´ezie). K tomuto souboru je moˇzn´e pˇristoupit pomoc´ı SQLite C/C++ API, coˇz je rozhran´ı, kter´e umoˇzn ˇuje programovat aplikace vyuˇz´ıvaj´ıc´ı datab´azi SQLite. Program gama-local je naps´an v jazyce C++, a tak je moˇzn´e toto rozhran´ı, urˇcen´e pro jazyky C a C++, pouˇz´ıt. Tv˚ urci datab´aze SQLite C/C++ API poskytuj´ı SQLite volnˇe k libovoln´emu pouˇzit´ı. Podpora datab´aze v programu gama-local bude velmi v´ yhodn´a z hlediska spolupr´ace s pˇripravovan´ ym programem QGama, kter´ y nab´ız´ı stejnou funkcionalitu jako program gama-local . Program QGama nen´ı pro pˇr´ıkazovou ˇra´dku, ale poskytuje grafick´e uˇzivatelsk´e rozhran´ı. V souˇcasn´e dobˇe je program QGama ve st´adiu v´ yvoje. V programu gama-local bude spolupr´aci s datab´az´ı SQLite zajiˇst’ovat nov´a tˇr´ıda SqliteReader. Tato pr´ace se zab´ yv´a implementac´ı t´eto tˇr´ıdy a tak´e funkcemi, kter´e tˇr´ıda pouˇz´ıv´a. Pˇri implementaci je tˇreba db´at na omezen´ı, kter´a jsou zp˚ usobena t´ım, ˇze SQLite C/C++ API je prim´arnˇe urˇceno pro jazyk C, zat´ımco program gama-local je v jazyce C++. 9
ˇ CVUT Praha
´ UVOD
Novou funkcionalitu je nutn´e po zaˇclenˇen´ı do programu gama-local n´aleˇzitˇe otestovat. Projekt GNU Gama obsahuje mnoˇzstv´ı pˇr´ıklad˚ u geodetick´ ych s´ıt´ı, kter´e je moˇzn´e pouˇz´ıt jako vstupn´ı data pˇri testov´an´ı. V´ yvoj projektu GNU Gama prob´ıh´a na syst´emech z rodiny GNU/Linux . K testov´an´ı je tedy moˇzn´e pouˇz´ıt ˇradu volnˇe dostupn´ ych program˚ u, takzvan´ ych unixov´ ych utilit. D´ale je pro testov´an´ı moˇzn´e pouˇz´ıt nov´ y program gama-local-cmp. Ten doplˇ nuje projekt GNU Gama o moˇznost porovnat v´ ystupy z programu gama-local . Program gama-local-cmp vylouˇc´ı n´ahodn´e rozd´ıly v´ ysledk˚ u vznikaj´ıc´ı pˇri v´ ypoˇctu na poˇc´ıtaˇci (numerick´ y ˇsum) a je vhodn´ y pro testov´an´ı nov´ ych verz´ı programu gama-local .
10
ˇ CVUT Praha
1
1 PROGRAM GAMA-LOCAL
Program gama-local
Program gama-local je souˇc´ast´ı projektu GNU Gama [14], kter´ y je zamˇeˇren na vyrovn´an´ı geodetick´ ych s´ıt´ı. Projekt GNU Gama je ˇs´ıˇren pod licenc´ı GNU GPL [7]. Program gama-local je programem pro pˇr´ıkazovou ˇr´adku. Je s n´ım moˇzn´e vyrovn´avat geodetick´e s´ıtˇe, kter´e obsahuj´ı r˚ uzn´e typy mˇeˇren´ı jako napˇr´ıklad d´elky, u ´hly ˇci v´ yˇskov´e rozd´ıly. V t´eto souvislosti se hod´ı zm´ınit, ˇze se v souˇcasn´e dobˇe pro projekt GNU Gama tak´e vyv´ıj´ı multiplatformn´ı grafick´e uˇzivatelsk´e rozhran´ı QGama [15]. Projekt QGama je vyv´ıjen na platformˇe Qt [13]. Zdrojov´e k´ody projektu GNU Gama lze z´ıskat stejnˇe jako dokumentaci prostˇrednictv´ım internetov´e str´anky projektu: http://www.gnu.org/software/gama/ Samotn´e zdrojov´e k´ody jsou dostupn´e pˇr´ımo ze str´anky: http://ftpmirror.gnu.org/gama Projekt GNU Gama pouˇz´ıv´a pro spr´avu verz´ı syst´em Git1 . Nejlepˇs´ı moˇznost´ı, jak z´ıskat aktu´aln´ı verzi, je proto pˇr´ıkaz, kter´ y vytvoˇr´ı lok´aln´ı kopii Git repozit´aˇre.2 $ git clone git://git.sv.gnu.org/gama.git Aktu´aln´ı verze programu gama-local , kterou je moˇzn´e z´ıskat z v´ yˇse uveden´ ych m´ıst, jiˇz obsahuje i zdrojov´e k´ody, kter´e jsou v´ ysledkem t´eto bakal´aˇrsk´e pr´ace. Na syst´emech GNU/Linux vypad´a kompilace a pˇr´ıpadnˇe i instalace projektu n´asledovnˇe: $ cd gama $ ./autogen.sh $ ./configure $ make $ make install 1 2
Ofici´ aln´ı str´ any syst´emu Git jsou na http://git-scm.com/ Pro z´ısk´ an´ı zdrojov´ ych k´ od˚ u z Git repozit´aˇre lze t´eˇz pouˇz´ıt odkaz http://git.savannah.gnu.
org/cgit/gama.git/snapshot/gama-master.tar.gz.
11
ˇ CVUT Praha
1 PROGRAM GAMA-LOCAL
Po vykon´an´ı pˇr´ıkazu make by se v adres´aˇri gama/bin mˇelo objevit nˇekolik spustiteln´ ych soubor˚ u, mezi nimi i program gama-local .
1.1
Podporovan´ e form´ aty
Vstupem pro program gama-local bylo doposud jen XML [8]. Jak soubor vypad´a, je pops´ano v dokumentaci projektu GNU Gama [9, str. 7 aˇz 20]. Souboru se obvykle d´av´a pˇr´ıpona .gkf (vznikla ze slov gama konfigurace“). ” V´ ystupem je bud’ prost´ y form´atovan´ y text, nebo opˇet XML. Soubor ve form´atu prost´eho textu je vhodn´ y pro rychl´e zjiˇstˇen´ı v´ ysledk˚ u. XML soubor je moˇzn´e s v´ yhodou pouˇz´ıt pro dalˇs´ı zpracov´an´ı.3 Je napˇr´ıklad moˇzn´e v´ ysledky pˇrev´est do jin´eho form´atu.
1.2
Pouˇ z´ıv´ an´ı
Pouˇz´ıv´an´ı gama-local vyˇzaduje od uˇzivatele z´akladn´ı znalost pr´ace s pˇr´ıkazovou ˇra´dkou a d´ale z´akladn´ı znalost XML. Obecnˇe se pˇredpokl´ad´a, ˇze vstupn´ı XML soubor uˇzivatel vytvoˇr´ı nˇejak´ ym programem. Je vˇsak tak´e moˇzn´e vyj´ıt z nˇekter´eho z pˇr´ıklad˚ u dostupn´ ych ze str´anek projektu GNU Gama [14] a pˇri mal´em objemu dat hodnoty do souboru zapsat ruˇcnˇe. Na m´em poˇc´ıtaˇci s operaˇcn´ım syst´emem Ubuntu4 m˚ uˇze vypadat spuˇstˇen´ı programu gama-local n´asledovnˇe. $ ./ gama - local input . gkf -- language cz -- txt output . txt
Pˇredpokl´ad´am, ˇze jsem pr´avˇe v adres´aˇri, kde m´am pˇr´ısluˇsn´ y spustiteln´ y soubor, a ˇze m´am vytvoˇren´ y soubor input.gkf, kde jsou pˇr´ısluˇsn´e vstupn´ı hodnoty. Kromˇe jazyka lze z pˇr´ıkazov´e ˇra´dky nastavit napˇr´ıklad tak´e algoritmus ˇci k´odov´an´ı soubor˚ u. Dalˇs´ı informace k pouˇz´ıv´an´ı lze nal´ezt v n´apovˇedˇe programu ˇci v dokumentaci [9]. 3
Pro ˇcten´ı v´ ysledk˚ u z XML je v projektu GNU Gama pˇripravena tˇr´ıda, jej´ıˇz popis lze nal´ezt
na http://geo.fsv.cvut.cz/gwiki/GNU_Gama_LocalNetworkAdjustmentResults. 4 Ubuntu je jedna z distribuc´ı operaˇcn´ıho syst´emu GNU/Linux. Distribuce je podporov´ana spoleˇcnost´ı Canonical Ltd. a je dostupn´a z http://www.ubuntu.com/
12
ˇ CVUT Praha
1.3
1 PROGRAM GAMA-LOCAL
Rozˇ s´ıˇ ren´ı o podporu SQLite datab´ aze
Mnoho aplikac´ı dnes nˇejak´ ym zp˚ usobem vyuˇz´ıv´a datab´azi SQLite, coˇz je souborov´a datab´aze, kter´a pouˇz´ıv´a jazyk SQL. Pr´ace s SQL datab´az´ı m´a mnoh´e v´ yhody, mezi kter´e pˇrirozenˇe patˇr´ı pohodln´e formulov´an´ı dotaz˚ u. Jak jiˇz bylo ˇreˇceno, jedn´a se o souborovou datab´azi, coˇz znamen´a, ˇze nen´ı potˇreba, aby nˇekde bˇeˇzel datab´azov´ y server. Rozhran´ı mezi souborem a daty uloˇzen´ ymi v datab´azi obvykle zajiˇst’uje knihovna. K SQLite autoˇri dod´avaj´ı SQLite C/C++ API (viz ˇca´st 2). Mnoho tv˚ urc˚ u knihoven pro r˚ uzn´e programovac´ı jazyky vˇsak poskytuje vlastn´ı rozhran´ı ˇ k datab´azi SQLite. Sirok´ a podpora a dostupnost ˇcin´ı z datab´aze SQLite ide´aln´ı v´ ymˇenn´ y form´at. Podpora SQLite datab´aze by mˇela usnadnit spolupr´aci mezi gama-local a dalˇs´ımi programy. Projekt QGama poˇc´ıt´a s podporou r˚ uzn´ ych datab´az´ı d´ıky platformˇe Qt. Mezi nimi je i datab´aze SQLite. Pro zv´ yˇsen´ı vz´ajemn´e kompatibility je vhodn´e, aby i program gama-local podporoval datab´azi SQLite. V projektu GNU Gama se na rozd´ıl od projektu QGama Qt nevyuˇz´ıv´a. Pro komunikaci s datab´az´ı SQLite program gama-local vyuˇz´ıv´a nativn´ı SQLite C/C++ API. Datab´azov´e sch´ema, kter´e pouˇz´ıv´a QGama, lze pouˇz´ıt i pro gama-local , ˇc´ımˇz je dalˇs´ı pˇredpoklad vz´ajemn´e kompatibility splnˇen.
1.4
Datab´ azov´ e sch´ ema
Pro projekt QGama jiˇz bylo vyvinuto datab´azov´e sch´ema. Toto sch´ema obsahuje popis datab´azov´ ych tabulek, do kter´ ych je moˇzn´e uloˇzit z´aznam lok´aln´ı geodetick´e s´ıtˇe. Tento z´aznam odpov´ıd´a jednomu vstupn´ımu XML souboru a pouˇz´ıv´a se pro nˇej oznaˇcen´ı konfigurace (configuration). Kromˇe informac´ı, kter´e lze uloˇzit ve vstupn´ım XML souboru, obsahuje z´aznam tak´e nˇekter´e parametry pˇr´ıkazov´eho ˇra´dku, se kter´ ymi se spouˇst´ı program gama-local . Datab´azov´e sch´ema bylo vyvinuto s ohledem na to, ˇze QGama by mˇela umoˇznit propojen´ı s r˚ uzn´ ymi SQL datab´azemi. V datab´azov´em sch´ematu byly pouˇzity jen takov´e konstrukce, kter´e funguj´ı ve vˇsech podporovan´ ych datab´az´ıch. Sch´ema je pouˇziteln´e nebo alespoˇ n ˇc´asteˇcnˇe pouˇziteln´e pro tyto datab´aze: PostreSQL, MySQL, 13
ˇ CVUT Praha
2 SQLITE C/C++ API
Oracle Database, SQLite. Datab´azov´e sch´ema bylo jiˇz zaˇclenˇeno do projektu GNU Gama a je v souboru xml/gama-local-schema.sql v adres´aˇri zdrojov´ ych k´od˚ u GNU Gama. Kompletn´ı datab´azov´e sch´ema je v pˇr´ıloze A. Pro pˇrevod vstupn´ıho XML souboru do SQL pˇr´ıkaz˚ u (INSERT) slouˇz´ı program gama-local-xml2sql. Program je souˇc´ast´ı projektu GNU Gama. Program generuje SQL pˇr´ıkazy, tyto pˇr´ıkazy by (teoreticky) mˇely b´ yt nez´avisl´e na konkr´etn´ı datab´azi. Pouze pro u ´ˇcely testov´an´ı datab´azov´eho sch´ematu byl t´eˇz naps´an program, kter´ y u ´daje (jednu konfiguraci) z SQLite datab´aze pˇrevede do vstupn´ıho XML souboru. Pouˇz´ıv´a se pro datab´azi vytvoˇrenou pomoc´ı zmiˇ novan´eho sch´ematu a naplnˇenou d´avkou z programu gama-local-xml2sql.
2
SQLite C/C++ API
Datab´aze SQLite verze 3 a vˇse, co k n´ı patˇr´ı, je ˇs´ıˇreno pod Public Domain. To plat´ı i pro nativn´ı SQLite C/C++ API. Projekt GNU Gama se snaˇz´ı b´ yt co nejm´enˇe z´avisl´ y na jin´ ych knihovn´ach. V souˇcasnosti je z´avisl´ y pouze na knihovnˇe Expat a ta je dod´av´ana spolu s ostatn´ımi zdrojov´ ymi k´ody.5 Vzhledem k t´eto snaze bylo nativn´ı SQLite C/C++ API jasnou volbou, protoˇze s sebou nepˇrin´aˇs´ı z´avislost na ˇza´dn´e rozs´ahl´e knihovnˇe.6 SQLite C/C++ API [11] je aplikaˇcn´ı program´atorsk´e rozhran´ı pro jazyk C a je uzp˚ usobeno k tomu, aby mohlo b´ yt pouˇzito i v jazyce C++. Nicm´enˇe na pr´aci v jazyce C++ m´a toto jist´e dopady a to pˇredevˇs´ım pˇri pouˇzit´ı rozhran´ı popsan´eho v ˇca´sti 2.2. Tyto dopady jsou zevrubnˇe rozebr´any v ˇc´asti 3.
2.1
Klasick´ e rozhran´ı
Rozhran´ı tvoˇr´ı funkce sqlite3 open, kter´a pˇreb´ır´a ukazatel na ukazatel na objekt sqlite3. Funkce otevˇre datab´azi a uloˇz´ı do promˇenn´e na n´ıˇz ji byl pˇred´an ukaza5
Expat je knihovna pro ˇcten´ı XML. Projekt GNU Gama je standardnˇe sestavov´an se syst´emovou
knihovnou Expat. V pˇr´ıpadˇe, ˇze je knihovna nedostupn´a, je moˇzn´e pro sestaven´ı pouˇz´ıt dodan´e zdrojov´e k´ ody. 6 Nav´ıc je moˇzn´e sestavit projekt GNU Gama bez podpory SQLite datab´aze (v´ıce viz 6.3).
14
ˇ CVUT Praha
2 SQLITE C/C++ API
tel hodnotu ukazatele na nov´ y datab´azov´ y objekt typu sqlite3. Jedn´a se vlastnˇe o konstruktor objektu sqlite3, coˇz je objekt reprezentuj´ıc´ı spojen´ı s datab´az´ı. P´ateˇr´ı tohoto rozhran´ı jsou funkce sqlite3 prepare v2 a sqlite3 step. Funkci sqlite3 prepare v2 se pˇred´av´a ukazatel na datab´azov´ y objekt, SQL pˇr´ıkaz, kter´ y se m´a vykonat, ukazatel na ukazatel na objekt typu sqlite3 stmt a jeˇstˇe dalˇs´ı parametry, kter´e vˇsak nejsou d˚ uleˇzit´e (viz [11]). Jej´ı zavol´an´ı zp˚ usob´ı vytvoˇren´ı objektu typu sqlite3 stmt a lze o n´ı uvaˇzovat jako o konstruktoru. Objekt sqlite3 stmt reprezentuje jeden SQL pˇr´ıkaz. Funkce sqlite3 step pracuje jen s objektem sqlite3 stmt a je nutn´e ji zavolat, aby se pˇr´ıkaz vykonal, nebot’ funkce sqlite3 prepare v2 jej pouze pˇriprav´ı. Pokud byl vykonan´ y pˇr´ıkaz SELECT, tak kaˇzd´e zavol´an´ı funkce sqlite3 step n´as posune na dalˇs´ı ˇra´dek v´ ysledku pˇr´ıkazu. Jednotliv´e hodnoty atribut˚ u se pak z´ısk´avaj´ı vol´an´ım funkc´ı ze skupiny sqlite3 column... v z´avislosti na tom, jak´ y typ hodnoty chceme obdrˇzet. V pˇr´ıkladu, kter´ y bude n´asledovat jsou pouˇzity funkce sqlite3 column int a sqlite3 column text. To jestli jeˇstˇe zb´ yvaj´ı nˇejak´e nezpracovan´e ˇr´adky se zjiˇst’uje z n´avratov´eho k´odu. Pokud vykonan´ y pˇr´ıkaz nevrac´ı ˇza´dn´ y v´ ysledek, staˇc´ı volat funkci sqlite3 step jen jednou. Po vol´an´ı t´emˇeˇr kaˇzd´e z v´ yˇse jmenovan´ ych funkc´ı je nutn´e prov´adˇet kontrolu n´avratov´eho k´odu. Jen tak je moˇzn´e urˇcit, ˇze nastala chyba. Posledn´ı chybovou zpr´avu lze z´ıskat zavol´an´ım funkce sqlite3 errmsg, kter´a pˇreb´ır´a ukazatel na objekt sqlite3. Po zpracov´an´ı v´ ysledk˚ u SQL pˇr´ıkazu je tˇreba uvolnit pamˇet’ pˇridˇelenou objektu sqlite3 stmt. To zajist´ı funkce sqlite3 finalize (je to vlastnˇe destruktor objektu sqlite3 stmt). Kontrolu n´avratov´eho k´odu funkce sqlite3 finalize nen´ı nutn´e prov´adˇet. Ve vˇetˇsinˇe pˇr´ıpad˚ u zcela postaˇcuje zkontrolovat n´avratov´ y k´od funkce sqlite3 step. Po ukonˇcen´ı pr´ace je tˇreba zavolat funkci sqlite3 close. Kontrola n´avratov´eho k´odu funkce sqlite3 close m˚ uˇze upozornit mimo jin´e na aktivn´ı objekty typu sqlite3 stmt (coˇz znamen´a, ˇze nebylo zavol´ano sqlite3 finalize). N´asleduje pˇr´ıklad, kter´ y ukazuje pouˇzit´ı v´ yˇse uveden´ ych funkc´ı. Pro zjednoduˇsen´ı pˇr´ıklad pˇredpokl´ad´a jiˇz vytvoˇrenou a naplnˇenou datab´azi. 1
sqlite3 * db ;
15
ˇ CVUT Praha
2 SQLITE C/C++ API
2
sqlite3_stmt * stmt ;
3
int rc = sqlite3_open ( " test . sqlite " , & db ) ;
4
if ( rc )
5 6
std :: cerr << " database error \ n " ; else { rc = s ql it e3 _p re pa re _v 2 ( db ,
7 8
" SELECT id , name FROM people " ,
9
-1 , & stmt , 0) ; if ( rc )
10
std :: cerr <<
11
" error : "
<< sqlite3_errmsg ( db ) << std :: endl ;
12
else {
13
while (( rc = sqlite3_step ( stmt ) ) == SQLITE_ROW ) {
14 15
const int id = sq li te 3_ co lu mn _i nt ( stmt , 0) ;
16
const char * name = ( const char *) s q li t e 3 _c o l um n _ te x t ( stmt , 1) ;
17
std :: cout << id << " " << name << std :: endl ;
18
}
19
if ( rc != SQLITE_DONE )
20
std :: cerr << " error : "
21
<< sqlite3_errmsg ( db ) << std :: endl ;
22
sqlite3_finalize ( stmt ) ;
23
}
24
sqlite3_close ( db ) ;
25 26
}
Pouˇzit´ı prob´ıran´eho rozhran´ı vyˇzaduje hodnˇe pr´ace a k´odu, vzhledem k tomu, ˇze je st´ale potˇreba kontrolovat n´avratov´e k´ody, zpracov´avat pˇr´ıpadn´e chyby a zajiˇst’ovat vol´an´ı funkc´ı, kter´e pln´ı u ´lohu konstruktor˚ u a destruktor˚ u. Moˇzn´ ym ˇreˇsen´ım je zabalit tyto funkce do nˇekolika objekt˚ u, a t´ım automatizovat urˇcit´e akce. Dalˇs´ı moˇznost´ı je pouˇzit´ı rozhran´ı popsan´eho v n´asleduj´ıc´ı ˇc´asti. Zb´ yv´a dodat, ˇze zde bylo pops´ano pouze z´akladn´ı rozhran´ı. SQLite C/C++ API nab´ız´ı velk´e mnoˇzstv´ı funkc´ı, jejichˇz popis pˇresahuje r´amec t´eto pr´ace. Pˇr´ıkladem m˚ uˇze b´ yt skupina funkc´ı sqlite3 bind..., kter´e se vyuˇzij´ı pˇredevˇs´ım ve spojitosti s pˇr´ıkazem INSERT.
16
ˇ CVUT Praha
2.2
2 SQLITE C/C++ API
Rozhran´ı s callback funkcemi
SQLite C/C++ API poskytuje jeˇstˇe dalˇs´ı rozhran´ı, kter´e je vlastnˇe pouhou ob´alkou nad rozhran´ım popsan´ ym v ˇc´asti 2.1. Ostatnˇe to prav´ı i dokumentace [10, ˇca´st 2.0]. It is important to realize that neither sqlite3 exec nor sqlite3 get table do anything that cannot be accomplished using the core routines. In fact, these wrappers are implemented purely in terms of the core routines. Zmiˇ novan´a funkce sqlite3 get table funguje podobnˇe jako sqlite3 exec avˇsak je oznaˇcena jako legacy interface a je doporuˇceno ji nepouˇz´ıvat [11]. To je d˚ uvod, proˇc zde nen´ı toto rozhran´ı rozebr´ano. ˇuje vykonat SQL pˇr´ıkaz jedn´ım Funkce sqlite3 exec je rozhran´ım, kter´e umoˇzn jedin´ ym vol´an´ım. Po zavol´an´ı sqlite3 exec je nutn´e zkontrolovat n´avratov´ y k´od a pˇr´ıpadnˇe uvolnit pamˇet’ pˇridˇelenou pro chybovou zpr´avu. Pˇri pouˇz´ıv´an´ı jsme osvobozeni od prov´adˇen´ı mnoh´ ych kontrol n´avratov´ ych k´od˚ u (kontroluje se jen jeden). Pˇred zavol´an´ım funkce sqlite3 exec je samozˇrejmˇe nutn´e prov´est jiˇz popsan´e otevˇren´ı datab´aze a po ukonˇcen´ı pr´ace je nutn´e datab´azi zase uzavˇr´ıt. Odpad´a vˇsak povinnost volat pro kaˇzd´ y pˇr´ıkaz trojici funkc´ı sqlite3 prepare v2, sqlite3 step a sqlite3 finalize. V tomto rozhran´ı se pouˇz´ıvaj´ı callback funkce ˇcili zpˇetn´a vol´an´ı. Jiˇz zmiˇ novan´e funkci sqlite3 exec se pˇred´a ukazatel na funkci, kterou bude n´aslednˇe volat pro kaˇzd´ y ˇra´dek v´ ysledku dotazu do datab´aze. Tato callback funkce se zpravidla p´ıˇse pro zpracovan´ı jednoho pˇr´ıkazu. Pokud bude SQL pˇr´ıkaz pˇredan´ y funkci sqlite3 exec pˇr´ıkazem, kter´ y nevrac´ı ˇza´dn´ y v´ ysledek, nebude callback funkce zavol´ana. V tomto pˇr´ıpadˇe je vhodn´e pˇredat jako hodnotu ukazatele na callback funkci nulov´ y ukazatel (NULL, 0). Pro vykon´av´an´ı pˇr´ıkaz˚ u, jako jsou napˇr´ıkad INSERT ˇci UPDATE, nen´ı tˇreba ps´at ˇza´dnou callback funkci. Vzhledem k tomu, ˇze k´od callback funkce je jinde neˇz k´od, kter´ y zavolal funkci sqlite3 exec, a volaj´ıc´ı k´od zˇrejmˇe oˇcek´av´a nˇejak´e v´ ysledky, je nutn´e pˇredat callback funkci objekt, kter´ y bude moci pro uloˇzen´ı v´ ysledk˚ u pouˇz´ıt. Tento objekt se pˇred´a funkci sqlite3 exec, kter´a ho n´aslednˇe pˇred´a callback funkci. Pro pˇred´av´an´ı
17
ˇ CVUT Praha
2 SQLITE C/C++ API
objektu je pouˇzit ukazatel na void, je tedy moˇzn´e pˇredat ukazatel na libovoln´ y objekt. Aby mohl b´ yt objekt pouˇzit, je nutn´e void* pˇretypovat na pˇr´ısluˇsn´ y ukazatel (viz t´eˇz ˇca´st 3.4). Funkce sqlite3 exec pˇreb´ır´a pˇet parametr˚ u. Vˇsechny parametry jsou ukazatele. Prvn´ı dva je nutn´e zad´avat vˇzdy. Jako tˇret´ı, ˇctvrt´ y a p´at´ y parametr lze pˇredat nulov´ y ukazatel (NULL). Parametry jsou uvedeny v n´asleduj´ıc´ım seznamu. 1.
sqlite3*
2.
const char*
3.
int (*)(void*, int, char**, char**)
4.
void*
5.
char**
ukazatel na datab´azi C ˇretˇezec s SQL pˇr´ıkazem ukazatel na callback funkci ukazatel pˇred´avan´ y callback funkci ukazatel na chybovou zpr´avu
Jak jiˇz bylo naznaˇceno, callback funkce obsahuje k´od, kter´ y zpracov´av´a jednotliv´e ˇra´dky v´ ysledku SQL pˇr´ıkazu. Callback funkce zn´a obsah vˇzdy jen jednoho ˇra´dku. N´asleduje seznam parametr˚ u callback funkce. 1.
void*
ukazatel pˇredan´ y funkci sqlite3 exec
2.
int
3.
char**
ukazatel na pole atribut˚ u
4.
char**
ukazatel na pole jmen atribut˚ u
poˇcet atribut˚ u
N´asleduje v´ ypis, kter´ y ukazuje z´akladn´ı pouˇzit´ı rozhran´ı tvoˇren´eho pˇredevˇs´ım funkc´ı sqlite3 exec. Nejprve je otevˇrena datab´aze (kontrola n´avratov´eho k´odu je pro zjednoduˇsen´ı vynech´ana) a inicializov´an C ˇretˇezec pro chybovou zpr´avu. Pot´e zavol´ana funkce sqlite3 exec. Funkci je pˇred´an jako prvn´ı parametr ukazatel na datab´azov´ y objekt typu sqlite3, a jako druh´ y parametr SQL pˇr´ıkaz, kter´ y chceme vykonat. Jako tˇret´ı je pˇred´an ukazatel na callback funkci7 , kterou chceme pouˇz´ıt ke zpracov´an´ı v´ ysledku. Jako ˇctvrt´ y parametr je pˇred´an ukazatel na objekt, se kter´ ym bude callback funkce pracovat. V tomto pˇr´ıpadˇe je to standardn´ı v´ ystupn´ı proud. A jako p´at´ y parametr je pˇred´an ukazatel na ˇretˇezec s chybovou zpr´avou. 1
extern " C " int readPeople ( void * data , int argc ,
2
char ** argv , char **) ;
3
void test () { 7
Ukazatel na funkci v C++ z´ısk´ ame tak, ˇze nap´ıˇseme jm´eno dan´e funkce.
18
ˇ CVUT Praha
2 SQLITE C/C++ API
4
sqlite3 * db ;
5
int rc = sqlite3_open ( " test . sqlite " , & db ) ;
6
// ... check rc
7
char * errorMsg = 0;
8
rc = sqlite3_exec ( db , " SELECT id , name FROM people " , readPeople , & std :: cout , & errorMsg ) ;
9
if ( rc != SQLITE_OK ) {
10 11
std :: cerr << " error : " ;
12
if ( errorMsg ) {
13
std :: cerr << errorMsg ;
14
sqlite3_free ( errorMsg ) ; }
15
std :: cerr << std :: endl ;
16
}
17
sqlite3_close ( db ) ;
18 19
}
Po zavol´an´ı funkce sqlite3 exec se provede kontrola n´avratov´eho k´odu (ˇra´dek 10) a pokud signalizuje chybu pokraˇcuje se v´ ypisem chybov´e zpr´avy. Zde je d˚ uleˇzit´e, ˇze funkce sqlite3 exec alokuje pamˇet’ pro chybovou zpr´avu jen v pˇr´ıpadˇe, ˇze doˇslo k chybˇe. Hodnotu ukazatele na vytvoˇrenou chybovou zpr´avu pak uloˇz´ı do promˇenn´e definovan´e na ˇra´dku 7. To m˚ uˇze prov´est d´ıky tomu, ˇze j´ı byl pˇred´an ukazatel na tuto promˇenou jako p´at´ y parametr. Alokovanou pamˇet’ je tˇreba uvolnit (ˇr´adek 14). Nakonec je zavˇre datab´aze funkc´ı sqlite3 close. V pˇredch´azej´ıc´ım v´ ypisu byla callback funkce pouze deklarov´ana. Vzhledem k tomu, ˇze je callback funkci potˇreba napsat vˇzdy, kdyˇz chceme z´ıskat v´ ysledek SQL pˇr´ıkazu, uvedu zde i jej´ı definici. Na prvn´ım ˇr´adku funkce (ˇra´dek 3 v´ ypisu) je pˇretypov´an´ı ukazatele na void na ukazatel na std::ostream pomoc´ı oper´atoru static cast. Situace je zde m´ırnˇe zesloˇzitˇena t´ım, ˇze pˇretypovan´ y ukazatel je pouˇzit jeˇstˇe k z´ısk´an´ı reference na objekt typu std::ostream. Pot´e je vˇsak moˇzno s objektem pracovat, jak je pro nˇej bˇeˇzn´e. V pˇr´ıpadˇe v´ ystupn´ıho proudu je to vloˇzen´ı dat z´ıskan´ ych z datab´aze. Tˇret´ı parametr je ukazatel pole C ˇretˇezc˚ u s atributy, proto je pouˇzit oper´ator indexov´an´ı pro pˇr´ıstup k jednotliv´ ym atribut˚ um. V pˇr´ıkazu SELECT v pˇredch´azej´ıc´ım v´ ypise se vyb´ıraly dva atributy, proto je jasn´e, ˇze lze pouˇz´ıt indexy 0 a 1. Poˇcet atribut˚ u uloˇzen´ y ve druh´em parametru nebyl vyuˇzit stejnˇe jako ˇctvrt´ y 19
ˇ CVUT Praha
3 PROPOJEN´I C A C++
parametr. 1
extern " C " int readPeople ( void * data , int argc , char ** argv , char **) {
2 3
std :: ostream & out = * static_cast < std :: ostream * >( data ) ;
4
out << argv [0] << " " << argv [1] << std :: endl ;
5
return 0;
6
}
3
Propojen´ı C a C++
Tato ˇca´st uv´ad´ı, co je tˇreba dodrˇzet pˇri propojov´an´ı k´odu napsan´eho v C a v C++. Tˇechto z´asad se mus´ı drˇzet i implementace tˇr´ıdy SqliteReader, protoˇze pracuje s SQLite C/C++ API. V t´eto ˇca´sti pˇredpokl´ad´am z´akladn´ı znalosti programovac´ıho jazyka C++, kter´e lze z´ıskat napˇr´ıklad z [5].
3.1
Funkce
Jak jiˇz bylo naznaˇceno, pˇri souˇcasn´em pouˇz´ıv´an´ı jazyk˚ u C a C++ existuj´ı jist´a pravidla, kter´a je nutn´e respektovat. A to se t´ yk´a i funkc´ı. Funkce v jazyce C maj´ı jin´e linkovac´ı konvence (linkage conventions)8 neˇz funkce v jazyce C++. Pokud chceme volat C funkci v C++ k´odu je nutn´e specifikovat, ˇze funkce m´a C linkov´an´ı (C linkage). Specifikace linkov´an´ı lze dos´ahnout t´ım, ˇze se pˇred deklaraci funkce pˇrid´a kl´ıˇcov´e slovo extern n´asledovan´e ˇretˇezcov´ ym liter´alem oznaˇcuj´ıc´ım jazyk, v pˇr´ıpadˇe jazyka C tedy extern "C". Poznamenejme, ˇze dle standardu [2, 7.5.3] by kaˇzd´a implementace C++ mˇela poskytnout linkov´an´ı pro C funkce ("C") a pro C++ funkce ("C++"), kter´e je implicitn´ı. Podpora ostatn´ıch jazyk˚ u je z´avisl´a na implementaci. Stejnˇe jako ˇretˇezcov´e liter´aly, jenˇz je oznaˇcuj´ı. Linkov´an´ı je moˇzn´e urˇcit pro jednu funkci. extern " C " int fun ( int ) ;
Nebo je tak´e moˇzn´e vloˇzit deklaraci do bloku extern "C". 8
V ˇcesk´e literatuˇre se ˇcasto pracuje s pojmem zach´azen´ı se jm´eny funkc´ı.
20
ˇ CVUT Praha
3 PROPOJEN´I C A C++
extern " C " { int fun ( int ) ; }
Aby mohl b´ yt jeden hlaviˇckov´ y soubor pouˇzit pro C i C++, je nutn´e, aby funkce mˇely C linkovn´an´ı. Toho se dos´ahne pomoc´ı bloku extern "C" a direktiv preprocesoru. // mylib . h : # ifdef __cplusplus extern " C " { # endif void f ( int ) ; # ifdef __cplusplus } # endif
Tento postup ˇcasto pouˇz´ıvaj´ı knihovny pro C pro sv´e hlaviˇckov´e soubory, aby mohly b´ yt pouˇzity s C++. Uveden´e plat´ı to i pro SQLite C/C++ API (hlaviˇckov´ y soubor <sqlite3.h>).
3.2
Ukazatele na funkce
R˚ uzn´e druhy linkov´an´ı se t´ ykaj´ı i ukazatel˚ u na funkce a to jak typ˚ u deklarovan´ ych pomoc´ı typedef, tak i parametr˚ u, kter´e jsou ukazateli na funkce. Jak se urˇc´ı linkov´an´ı je uk´az´ano na pˇr´ıkladech. 1
// mixing C and C ++ function pointers :
2
typedef void (* pf_cpp ) () ; // pf_cpp is a pointer to a C ++ function ( has C ++ linkage )
3
void f_cpp () {} // f_cpp is a C ++ function ( has C ++ linkage )
4
extern " C " {
5
typedef void (* pf_c ) () ; // pf_c is a pointer to a C function ( has C linkage )
6 7
void f_c () {} // f_c is a C function ( has C linkage ) } // end of extern " C " block
8 9
void fun1 ( pf_cpp pf ) { // fun1 takes one parameter of type pointer to C ++ function
21
ˇ CVUT Praha
pf () ;
10 11
3 PROPOJEN´I C A C++
}
12 13
void fun2 ( pf_c pf ) { // fun2 takes one parameter of type pointer to C function pf () ;
14 15
}
16 17
void test_fun12 () {
18
fun1 ( f_cpp ) ; // ok
19
fun2 ( f_c ) ;
// ok
20
fun1 ( f_c ) ;
// error
21
fun2 ( f_cpp ) ; // error
22
}
Kdyˇz pˇred´ame funkci, oˇcek´avaj´ıc´ı ukazatel na C++ funkci, ukazatel na C funkci (ˇr´adek 20), nebo naopak pˇred´ame m´ısto ukazatele na C funkci ukazatel na C++ funkci (ˇra´dek 21), mˇelo by doj´ıt k chybˇe pˇri kompilaci. To odpov´ıd´a tomu, co je uvedeno ve standardu [2, 7.1.5]: Two function types with different language linkages are distinct types even if they are otherwise identical. Na druhou stranu B. Stroustrup uv´ad´ı, ˇze pokud to implementace umoˇzn ˇuje, je moˇzn´e, aby ukazatel na C funkci st´al na m´ıstˇe ukazatele na C++ funkci a obr´acenˇe, tedy aby tyto konverze byly rozˇs´ıˇren´ım jazyka [1, str. 208]. Kompil´ator GCC 9 v´ yˇse uveden´e opravdu umoˇzn ˇuje. Je tedy moˇzn´e ukazatele libovolnˇe zamˇen ˇovat. Dokonce lze do promˇenn´e typu ukazatel na C funkci pˇriˇradit ukazatel na statickou metodu tˇr´ıdy. Vzhledem k tomu, ˇze toto chov´an´ı nen´ı pˇrenositeln´e, bylo by dobr´e, aby GCC poskytlo pˇri kompilaci nˇejak´e varov´an´ı. Bohuˇzel tomu tak nen´ı, protoˇze GCC pro typy r˚ uzn´a linkov´an´ı nerozliˇsuje.10 Ze stejn´eho d˚ uvodu nen´ı 9
N´ azev GCC (GNU Compilers Collection) oznaˇcuje celou sadu kompil´ator˚ u. Kompil´ator pro C++ z t´eto sady se jmenuje g++. Nad´ale vˇsak budu pouˇz´ıvat souhrnn´ y n´azev GCC , nebot’ je to bˇeˇzn´ a praxe. 10 To, ˇze GCC nerozliˇsuje typy s r˚ uzn´ ym linkov´an´ım, je povaˇzov´ano za chybu, viz GCC Bugzilla – Bug 2316 http://gcc.gnu.org/bugzilla/show_bug.cgi?id=2316
22
ˇ CVUT Praha
3 PROPOJEN´I C A C++
napˇr´ıklad moˇzn´e pˇretˇeˇzovat funkce na z´akladˇe typ˚ u, kter´e se liˇs´ı pouze v linkov´an´ı.
3.3
Viditelnost funkc´ı
Definice funkc´ı maj´ı v C i C++ glob´aln´ı viditelnost. Deklarace funkce je vˇsak viditeln´a jen v dan´e pˇrekladov´e jednotce (ve zdrojov´em souboru s vloˇzen´ ymi hlaviˇckov´ ymi soubory). Funkci proto m˚ uˇzeme pouˇz´ıt pouze v n´ı. Pokud chceme pouˇz´ıt funkci v jin´e pˇrekladov´e jednotce, neˇz ve kter´e je funkce definov´ana, mus´ıme do zdrojov´eho k´odu vloˇzit jej´ı deklaraci. To se zpravidla uˇcin´ı vloˇzen´ım pˇr´ısluˇsn´eho hlaviˇckov´eho souboru. V n´asleduj´ıc´ıch pˇr´ıkladech je vˇsak pouˇzita moˇznost pˇr´ım´eho naps´an´ı deklarace funkce, tj. bez pouˇzit´ı hlaviˇckov´eho souboru. Nˇekdy je vˇsak v´ yhodn´e znemoˇznit pouˇzit´ı funkce mimo pˇrekladovou jednotku, kde je definov´ana, napˇr´ıklad kv˚ uli skryt´ı implementace ˇci prost´emu zabr´anˇen´ı konfliktu jmen. Skryt´ı funkce se v jazyce C++ provede tak, ˇze se funkce um´ıst´ı do bezejmenn´eho (nepojmenovan´eho, anonymn´ıho) prostoru jmen, jak je vidˇet na ˇr´adku 6 n´asleduj´ıc´ıho v´ ypisu (v´ ypis pˇredstavuje dva soubory). Funkci pak nen´ı moˇzn´e pouˇz´ıt, ani kdyˇz poskytneme pˇr´ısluˇsnou deklaraci. Pˇrekladaˇc ji totiˇz povaˇzuje za jinou funkci. R˚ uzn´e moˇznosti pˇr´ıstupu k funkc´ım jsou uk´az´any na n´asleduj´ıc´ım v´ ypisu. Na ˇra´dku 2 je definice funkce a v jin´em souboru je jej´ı odpov´ıdaj´ıc´ı deklarace (ve v´ ypisu ˇra´dek 10). Pokud se pokus´ıme pouˇz´ıt funkci, kterou jsme nedeklarovali, pˇrekladaˇc ohl´as´ı chybu. Chyba nastane tak´e v pˇr´ıpadˇe, ˇze se pokus´ıme poskytnout deklaraci funkce z bezejmen´eho prostoru jmen (ˇr´adek 12). Deklarovali jsme totiˇz novou funkci, jej´ıˇz definice nen´ı zn´ama. 1
// a . cpp :
2
int function_k ( int a ) { return a ; }
3
int function_l ( int a ) { return a ; }
4
int function_m ( int a ) { return a ; }
5
namespace { int function_n ( int a ) { return a ; }
6 7
}
8 9 10
// b . cpp : int function_k ( int ) ;
23
ˇ CVUT Praha
11
3 PROPOJEN´I C A C++
int function_m ( int b ) { return b ; } // error : multiple definition of function_m ( int ) 11
12
int function_n ( int ) ;
13 14
void test_kln () {
15
function_k (0) ;
16
function_l (1) ; // error : function_l was not declared in scope function_n (2) ; // error : undefined reference to
17
function_n ( int ) 18
}
V jazyce C se skryt´ı funkce provede tak, ˇze se do deklarace funkce dopln´ı kl´ıˇcov´e slovo static. static void function_a ( int ) { }
Viditelnost definice pak bude omezena pouze na danou pˇrekladovou jednotku. Funguje to tedy analogicky jako bezejmen´ y prostor jmen v C++. Nutno poznamenat, ˇze nem´a smysl vkl´adat funkce v bezejmenn´ y prostoru jmen do hlaviˇckov´ ych soubor˚ u. To sam´e plat´ı i pro funkce deklarovan´e jako static. 3.3.1
Deklarace extern "C" a prostor jmen
Pokud funkci deklarujeme jako extern "C" uvnitˇr nˇejak´eho prostoru jmen, mus´ıme se na ni odvol´avat pomoc´ı prostoru jmen, i kdyˇz m´a C linkov´an´ı (to je vidˇet na ˇra´dc´ıch 4, 12 a 18 n´asleduj´ıc´ıho v´ ypisu). Funkci nelze pouˇz´ıt bez specifikace prostoru jmen (ˇr´adek 19). Je vˇsak moˇzn´e poskytnout deklaraci bez specifikace prostoru jmen a to i pˇresto, ˇze funkce byla p˚ uvodnˇe deklarov´ana v prostoru jmen. Funkce se pak pouˇz´ıv´a bez specifikace prostoru jmen. Tento postup ukazuj´ı ˇra´dky 5, 14 a 20. Tento postup na funkce s C++ linkov´an´ım pouˇz´ıt nelze. Pokud se o to pokus´ıme, deklarujeme novou funkci. 1
// a . cpp :
2
namespace foo { 11
Chybov´e hl´ aˇsky poch´ az´ı z kompil´atoru GCC . Stejnˇe je tomu i v n´asleduj´ıc´ım textu.
24
ˇ CVUT Praha
3 PROPOJEN´I C A C++
extern " C " {
3 4
int bar () { return 1; }
5
int bar_d ( double ) { return 1; }
6
}
7
void bar_cpp () {}
8
}
9 10
// b . cpp :
11
namespace foo { extern " C " int bar () ;
12 13
}
14
extern " C " int bar_d ( double ) ;
15
void bar_cpp () ; // not namespace foo { void bar_cpp () ; }
16 17
void test_foo_bars () {
18
foo :: bar () ; // ok
19
bar () ;
// error : bar was not declared in this scope
20
bar_d (3) ;
// ok
21
bar_cpp () ;
// error : undefined reference to bar_cpp ()
22
}
Kdybychom v deklaraci na ˇra´dku 14 vynechali extern "C", deklarovali bychom novou funkci (s C++ likov´an´ım), ke kter´e by chybˇela definice. 3.3.2
Deklerace extern "C" a viditelnost
V C++ se deklarace extern "C" ˇcasto pouˇz´ıv´a pro pr´aci s rozhran´ım a spoleˇcn´ ymi hlaviˇckov´ ymi soubory pro C i C++ (viz ˇca´st 3.1). Pokud se deklarace extern "C" pouˇz´ıv´a kv˚ uli pˇred´av´an´ı ukazatel˚ u na funkce nˇejak´e C knihovnˇe, m˚ uˇzeme cht´ıt, aby definice funkce nebyla pˇr´ıstupn´a mimo danou pˇrekladovou jednotku. V C++ se pro omezen´ı pˇr´ıstupu pouˇzije bezejmenn´ y prostor jmen (viz ˇca´st 3.3). Funkce deklarovan´e jako extern "C" vˇsak mohou b´ yt pouˇzity i bez specifikace prostoru jmen (viz ˇca´st 3.3.1). N´asleduj´ıc´ı v´ ypis ukazuje, jak extern "C" funguje s bezejmenn´ ym prostorem jmen. 1
// a . cpp :
25
ˇ CVUT Praha
2
namespace { extern " C " int bar_i ( int ) { return 1; }
3 4
3 PROPOJEN´I C A C++
}
5 6
extern " C " double bar_d ( double ) { return 1; }
7 8
// b . cpp :
9
extern " C " {
10
int bar_i ( int ) ;
11
double bar_d ( double ) ;
12
}
13 14
void test_bars () {
15
bar_i (1) ;
16
bar_d (1.0) ; // ok
17
// ok ?
}
Na ˇr´adku 3 je deklarov´ana a definov´ana funkce uvnitˇr bezejmenn´eho prostoru jmen. Pokud by se jednalo pouze o C++ nebylo by moˇzn´e ji pouˇz´ıt v jin´e pˇrekladov´e jednotce. Avˇsak funkce se ˇr´ıd´ı pravidly jazyka C, a proto je bezejmenn´ y prostor jmen ignorov´an. Na ˇra´dku 10 poskytneme deklaraci funkce a na ˇr´adku 15 ji pouˇzijeme. K´od se sice pˇreloˇz´ı, ale v˚ ubec to nen´ı to, ˇceho jsme chtˇeli dos´ahnout, tedy skryt´ı funkce v pˇrekladov´e jednotce. Funkce deklarovan´a v bezejmenn´em prostoru jmen jako extern "C" se chov´a stejnˇe, jako kdyby byla deklarov´ana mimo bezejmenn´ y prostor jmen. Vzhledem k tomu, ˇze se jedn´a o C funkci, nab´ız´ı se ˇreˇsen´ı pomoc´ı kl´ıˇcov´eho slova static (viz ˇc´ast 3.3). To je uk´az´ano na n´asleduj´ıc´ım v´ ypisu. Pˇri pˇrekladu dojde k chybˇe (ˇr´adek 10), protoˇze definice funkce je nedostupn´a, a to je pˇresnˇe to, co jsme zam´ yˇsleli. 1
// a . cpp :
2
extern " C " { static void bar_c ( char ) { }
3 4
}
5 6
// b . cpp :
7
extern " C " int bar_c ( char ) ;
26
ˇ CVUT Praha
3 PROPOJEN´I C A C++
8 9
void test_bar_c () { bar_c ( ’a ’) ;
10 11
// error : undefined reference to bar_c
}
3.3.3
Pouˇ zit´ı pˇ ri implementaci tˇ r´ıdy SqliteReader
Callback funkce pouˇz´ıvan´e v SQLite C/C++ API (2.2) je nutn´e deklarovat jako extern "C", protoˇze SQLite C/C++ API oˇcek´av´a C funkci (viz 3.1). By bylo vhodn´e skr´ yt callback funkce v pˇrekladov´e jednotce (viz v´ yˇse). Nelze vˇsak pouˇz´ıt bezejmenn´ y prostor jmen, protoˇze se jedn´a o C funkce. Pro nˇe je moˇzn´e pouˇz´ıt deklaraci static. Bohuˇzel se nepodaˇrilo ovˇeˇrit, zda ˇreˇsen´ı, kter´e kombinuje deklarace extern "C" a static, je pˇrenositeln´e. V GCC dan´a kombinace funguje pˇresnˇe tak, jak to bylo uvedeno v pˇredch´azej´ıc´ıch ˇca´stech. V GCC je implementace C a C++ velmi tˇesnˇe propojena a to je nejsp´ıˇse d˚ uvod, proˇc deklarace extern "C" a static funguj´ı dohromady. Nelze vˇsak spol´ehat na to, ˇze s jin´ ymi kompil´atory tato kombinace bude tak´e fungovat, a proto bylo pˇri implementaci tˇr´ıdy SqliteReader pouˇzito ˇreˇsen´ı bez pouˇzit´ı kl´ıˇcov´eho slova static. Callback funkce, jejichˇz platnost by bylo dobr´e omezit jen na danou pˇrekladovou jednotku, jsou dostupn´e v cel´em programu. Konfliktu jmen je br´anˇeno prefixem yˇce rizika ˇspatn´eho pouˇzit´ı, spol´eh´a se na dissqlite db v n´azvu funkc´ı. Co se t´ cipl´ınu program´atora. Funkce vlastnˇe nen´ı moˇzn´e k niˇcemu pouˇz´ıt, protoˇze pracuj´ı s objektem ReaderData, kter´ y je jim nutn´e pˇredat. Jeho deklarace vˇsak nen´ı mimo implementaci tˇr´ıdy SqliteReader dostupn´a.
3.4
Pˇ red´ av´ an´ı objekt˚ u
Pˇri pr´aci se SQLite C/C++ API za pouˇzit´ı callback funkc´ı je nutn´e pˇred´avat ukazatel na objekt, se kter´ ym pracujeme a potˇrebujeme ho v callback funkci. Pˇred´an´ı se dˇeje pomoc´ı ukazatele na void (viz ˇca´st 2.2). Ukazatel na void m˚ uˇze v C a C++ ukazovat na libovoln´ y typ. Konverze libovoln´eho ukazatele na typ void* je v obou
27
ˇ CVUT Praha
3 PROPOJEN´I C A C++
jazyc´ıch implicitn´ı. Konverze z typu void* na jin´ y ukazatel se v C++ provede pomoc´ı oper´atoru static cast, v C je konverze implicitn´ı. V C se ukazatel na void pouˇz´ıv´a tam, kde je tˇreba pˇred´avat r˚ uzn´e typy objekt˚ u. Pˇred´an´ı parametru callback funkci tak, jak je tomu v SQLite C/C++ API, je typick´ ym pˇr´ıkladem. N´aslednˇe je nutn´e pˇretypovat void* zpˇet na ukazatel na poˇzadovan´ y typ. V C++ se tˇemto konstrukc´ım snaˇz´ıme vyhnout. Avˇsak pˇri pr´aci s knihovnou v C se jim vyhnout nem˚ uˇzeme. Jako pˇr´ıklad dobˇre poslouˇz´ı pouˇzit´ı SQLite C/C++ API (viz 2.2). Zavol´an´ı funkce sqlite exec, kter´a vol´a callback funkci vypad´a v pˇr´ıpadˇe funkce n´asledovnˇe. sqlite3_exec ( sqlite3Handle , queryString , readPoints , readerData , & errorMsg ) ;
Pouˇzit´a callback funkce readPoints pak m˚ uˇze vypadat tˇreba takto. extern " C " int readPoints ( void * data , int argc , char ** argv , char **) { ReaderData * d = static_cast < ReaderData * >( data ) ; // ... callback ’s code }
Vzhledem k tomu, ˇze funkce pracuje pˇredevˇs´ım s objektem ReaderData, mohla by se tato pr´ace prov´est v nˇejak´e metodˇe tˇr´ıdy (struktury) ReaderData. Callback funkce by pak pouze provedla pˇretypov´an´ı a zavolala pˇr´ısluˇsnou metodu. Takov´e funkci se ˇcasto ˇr´ık´a trampol´ınov´a a pokud se budeme ˇr´ıdit t´ım, co je uvedeno v ˇc´astech 3.1 a 3.5, stane se C ob´alkou nad metodou dan´eho objektu. extern " C " int readPoints ( void * data , int argc , char ** argv , char **) { ReaderData * d = static_cast < ReaderData * >( data ) ; d - > readPoints ( argc , argv ) ; }
Poznamenejme, ˇze naˇse C funkce m˚ uˇze zavolat metodu objektu, jen pokud je veˇrejn´a, nebo pokud je C funkce deklarovan´a jako pˇr´ıtel (friend)12 tˇr´ıdy dan´eho 12
V jazyce C++ mohou pˇr´ atel´e tˇr´ıd pˇristupovat k jejich soukrom´ ym ˇclen˚ um.
28
ˇ CVUT Praha
3 PROPOJEN´I C A C++
objektu. Pˇri deklaraci pˇra´telsk´e funkce s C linkov´an´ım mus´ıme nejprve deklarovat funkci jako extern "C" a pot´e ji teprve deklarovat jako pˇr´ıtele tˇr´ıdy. Obˇe deklarace nelze spojit do jedn´e a je nutn´e je prov´est pr´avˇe v tomto poˇrad´ı. extern " C " int readPoints ( void * , int , char ** , char **) ; class ReaderData { // ... friend int readPoints ( void * , int , char ** , char **) ; // ... }
Pˇri implementaci tˇr´ıdy SqliteReader (pˇresnˇeji pˇri implementaci pomocn´e struktury ReaderData) byla pouˇzita prvn´ı uveden´a moˇznost. Zmiˇ novan´a C funkce se tedy neomezuje na pouh´e zavol´an´ı metody pˇr´ısluˇsn´eho objektu, ale vykon´av´a vˇsechny ˇ ast 4 pojedn´av´a o tom, jak funkce pˇristupuje k datov´ potˇrebn´e ˇcinnosti. C´ ym sloˇzk´am objektu.
3.5
Zpracov´ an´ı v´ yjimek
Jazyk C++ poskytuje pro zpracov´an´ı chybov´ ych stav˚ u mechanismus v´ yjimek. Mezi jeho v´ yhody patˇr´ı lepˇs´ı oddˇelen´ı k´odu, kter´ y oˇsetˇruje chybov´ y stav, od k´odu, kter´ y zajiˇst’uje norm´aln´ı fungov´an´ı programu. Dalˇs´ı v´ yhodou v´ yjimek oproti jin´ ym zp˚ usob˚ um pr´ace s chybov´ ymi stavy je to, ˇze nut´ı program´atora je oˇsetˇrit (n´avratov´ y k´od lze ignorovat, zat´ımco nezachycen´a v´ yjimka zp˚ usob´ı p´ad programu). Jazyk C vˇsak mechanismus v´ yjimek nem´a a pro ohlaˇsov´an´ı a zjiˇst’ov´an´ı chybov´ ych stav˚ u pouˇz´ıv´a jin´e metody.13 Pokud v C++ pouˇz´ıv´ame knihovnu, kter´a je urˇcen´a pro jazyk C, mus´ıme se s rozd´ıln´ ym pˇr´ıstupem k chybov´ ym stav˚ um vyrovnat. Je totiˇz moˇzn´e, ˇze n´ami napsan´a funkce bude vol´ana v r´amci C knihovny. A to je pr´avˇe pˇr´ıpad callback funkc´ı, kdy ukazatel na naˇsi funkci pˇred´ame nˇejak´e C funkci, kter´a ji pak pomoc´ı ukazatele zavol´a. Pokud by v naˇs´ı funkci doˇslo k vyvol´an´ı v´ yjimky a ona v´ yjimka by opustila tˇelo naˇs´ı funkce, zp˚ usobilo by to p´ad programu z d˚ uvodu nezachycen´e v´ yjimky. 13
V jazyce C sice existuj´ı rozˇs´ıˇren´ı, kter´a umoˇzn ˇuj´ı pr´aci s v´ yjimkami, ale tato ˇreˇsen´ı rozhodnˇe
nejsou pˇrenositeln´ a.
29
ˇ CVUT Praha
3 PROPOJEN´I C A C++
Moˇzn´ y v´ ystup programu pˇreloˇzen´em pomoc´ı GCC je na n´asleduj´ıc´ım v´ ypisu. terminate called after throwing an instance of ’ std :: runtime_error ’ what () :
some error message
Z uveden´eho vypl´ yv´a, ˇze callback funkce mus´ı vˇsechny v´ yjimky, kter´e v n´ı byly vyvol´any, zachytit. K´od v tˇele callback funkce, kter´ y m˚ uˇze vyvolat v´ yjimku, mus´ı b´ yt obalen blokem try-catch. Bloky catch mus´ı zachytit kaˇzdou v´ yjimku vyvolanou v bloku try, takˇze posledn´ım catch blokem mus´ı b´ yt catch blok s v´ ypustkou. Jednoduch´a uk´azka je na n´asleduj´ıc´ım v´ ypisu. 1
int someCallback ( int a ) {
2
// can not throw exception
3
try { // can throw exception
4 5
}
6
catch ( std :: exception & e ) { // handle exception
7 8
}
9
catch (...) { // handle unknown exception
10
}
11 12
}
St´ale ovˇsem zb´ yv´a vyˇreˇsit, jak nahl´asit, ˇze doˇslo k v´ yjimce. Nahl´aˇsen´ı je nutn´e prov´est ve stylu jazyka C. Knihovna, se kterou pracujeme, by mˇela m´ıt definovan´ y nˇejak´ y zp˚ usob, jak callback funkce ohlaˇsuj´ı, ˇze doˇslo k chybˇe. V pˇr´ıpadˇe SQLite C/C++ API je to n´avratov´ y k´od (viz 2.2). Toto ˇreˇsen´ı vˇsak neobsahuje zp˚ usob, jak zjistit, ke kter´e v´ yjimce doˇslo. I kdyˇz v pˇr´ıpadˇe n´avratov´eho k´odu lze napˇr´ıklad pˇriˇradit r˚ uzn´ ym v´ yjimk´am r˚ uzn´e n´avratov´e k´ody. To ovˇsem nen´ı moc pohodln´e ˇreˇsen´ı a jeˇstˇe nen´ı zaruˇceno, ˇze n´am knihovn´ı funkce n´avratov´ y k´od callback funkce sdˇel´ı. Zcela jinou moˇznost´ı, jak se zbavit probl´em˚ u s v´ yjimkami pˇri pr´aci s C knihovnou, je v´ yjimky v˚ ubec nepouˇz´ıvat. T´ım bychom se ovˇsem pˇripravili o v´ yhody pouˇz´ıvan´ı v´ yjimek. A v pˇr´ıpadˇe, ˇze pouˇz´ıv´ame k´od, kter´ y nem˚ uˇzeme zmˇenit a v´ yjimky vyvol´avat m˚ uˇze (napˇr´ıklad standardn´ı knihovnu C++), nem´ame jinou moˇznost neˇz 30
ˇ CVUT Praha
3 PROPOJEN´I C A C++
v´ yjimky zachyt´avat (alespoˇ n pomoc´ı catch s v´ ypustkou). Jak se v´ yjimky oˇsetˇruj´ı v implementaci tˇr´ıdy SqliteReader a jak lze zabr´anit ztr´atˇe informace o tom, jak´a v´ yjimka byla vyvol´ana, pojedn´av´a ˇc´ast 5.
3.6
Komplexn´ı ˇ reˇ sen´ı pomoc´ı ˇ sablon a maker
Pouˇzit´ı SQLite C/C++ API (viz 2.2) s callback funkcemi vede k tomu, ˇze je tˇreba napsat nˇekolik podobn´ ych funkc´ı. Funkce jsou podobn´e v tom, ˇze tˇelo kaˇzd´e funkce mus´ı obsahovat tent´ yˇz k´od. Jedn´a se pˇredevˇs´ım o k´od, kter´ y zajist´ı odchyt´av´an´ı v´ yjimek a kter´ y nemus´ı b´ yt trivi´aln´ı (viz ˇca´st 5). D˚ uvody, proˇc tomu tak mus´ı b´ yt, jsou uvedeny v ˇca´stech 3.4 a 3.5. K´od callback funkc´ı lze tedy rozdˇelit na spoleˇcn´ y k´od a k´od, kter´ y vykon´av´a operace t´ ykaj´ıc´ı se pˇr´ımo u ´ˇcelu dan´e funkce. Spoleˇcn´ y k´od je tedy moˇzn´e pˇresunout do jedn´e funkce. Vzhledem k tomu, ˇze k´od chceme pˇridat jiˇz bˇehem kompilace, je ide´aln´ım ˇreˇsen´ım pouˇzit´ı ˇsablonov´e funkce. Jej´ım parametrem bude ukazatel na konkr´etn´ı funkci, kterou chceme obalit k´odem, kter´ y odchyt´av´a vˇsechny v´ yjimky. Nejprve uved’me dva typy ukazatel˚ u na funkce. Jak bylo uvedeno v ˇca´sti 3.2, jedn´a se o rozd´ıln´e typy. 1
extern " C " typedef int (* CCallbackType ) ( void * , int , char ** , char **) ;
2 3
typedef int (* CppCallbackType ) ( void * , int , char ** , char **) ;
4
N´asleduje k´od ˇsablonov´e funkce, kter´a zajist´ı odchycen´ı v´ yjimek. K´od je znaˇcnˇe zjednoduˇsen a ponech´av´am tak´e stranou, zda je ˇsablonov´ ym parametrem hodnota typu CCallbackType ˇci CppCallbackType. 1
template < CallbackType callback > int Data :: sa ve Ca llb ac kC al le r ( void * data , int argc ,
2 3
char ** argv ,
4
char ** cnames )
5
{
6
Data * p = static_cast < Data * >( data ) ;
7
try {
8
return callback (p , argc , argv , cnames ) ;
31
ˇ CVUT Praha
3 PROPOJEN´I C A C++
}
9
catch ( Exception & e ) {
10
// ... store an exception in p
11
}
12
catch (...) {
13
// ... store an exception in p
14
}
15
return 1;
16
}
17
V callback funkci lze bez jak´ ychkoli okolk˚ u vyvol´avat v´ yjimky. D´ıky tomu, ˇze je obalena uvedenou ˇsablonovou funkc´ı, nedojde k p´adu programu. Jak m˚ uˇze vypadat z´aklad callback funkce je uvedeno na n´asleduj´ıc´ım v´ ypisu. 1
int Data :: readPoints ( void * data , int argc , char ** argv , char ** cnames ) {
2 3
// ... do something
4
// can throw exception
5
return 0; }
6
Pokud funkce sqlite3 exec pomoc´ı n´avratov´eho k´odu nahl´as´ı, ˇze doˇslo k chybˇe, je nutn´e zpracovat informace o uloˇzen´e v´ yjimce. Vzhledem k tomu, bylo by vhodn´e obalit i funkci sqlite3 exec funkc´ı, kter´e se o zpracov´an´ı postar´a. Tato ob´alka m˚ uˇze zajistit napˇr´ıklad znovu vyvol´an´ı v´ yjimky ˇci vyvol´an´ı v´ yjimky v pˇr´ıpadˇe chyby datab´aze. Ve v´ ypisu funkce exec (ob´alky funkce sqlite3 exec) ponech´av´am jen k´od t´ ykaj´ıc´ı se pˇred´av´an´ı ukazatele na callback funkci (k´od, kter´ y zpracov´av´a v´ yjimky lze naj´ıt v ˇc´asti 5.3). 1
void Data :: exec ( sqlite3 * sqlite3Handle ,
2
const std :: string & query ,
3
CCallbackType callback , void * data )
4
{
5
// ...
6
int rc = sqlite3_exec ( sqlite3Handle , query . c_str () , callback , data , & errorMsg ) ;
7
// ...
8 9
}
32
ˇ CVUT Praha
3 PROPOJEN´I C A C++
Kdybychom zapomnˇeli, ˇze CCallbackType a CppCallbackType jsou jin´e datov´e typy, mohli bychom funkci exec pouˇz´ıvat n´asledovnˇe. exec ( sqlite3Handle , query , saveCallbackCaller < readPoints > , this ) ;
Dokonce bychom mohli zmˇenit i funkci exec na ˇsablonovou. Jako parametr ˇsablony by samozˇrejmˇe mˇela hodnotu ukazatele na callback funkci. Nic z toho vˇsak nen´ı ˇ moˇzn´e kv˚ uli rozd´ılnosti typ˚ u CCallbackType a CppCallbackType. Sablonov´ e funkce mus´ı m´ıt nutnˇe C++ linkov´an´ı, a proto ukazatel na nˇe nelze pouˇz´ıt jako ukazatel na C funkci. Pro pouˇzit´ı s C je nutn´e kaˇzdou instanci ˇsablonov´e funkce obalit do funkce, kter´e je deklarov´ana jako extern "C". 1
extern " C " { int c_readPoints ( void * data , int argc ,
2
char ** argv , char ** cnames ) {
3
return Data :: saveCallbackCaller < Data :: readPoints >
4
( data , argc , argv , cnames ) ;
5
}
6 7
}
Uveden´e obalen´ı nelze ze zˇrejm´ ych d˚ uvod˚ u automatizovat pomoc´ı ˇsablon. Lze vˇsak pouˇz´ıt makro. 1
# define I M P L E M E N T _ C _ C A L L B A C K ( FUNCTION_NAME ) int c_ \
2
# # FUNCTION_NAME \
3
( void * data , int argc , char ** argv , char ** cnames ) { return Data :: saveCallbackCaller < Data ::\
4
FUNCTION_NAME \
5
>( data , argc , argv , cnames ) ; }
Makro vytvoˇr´ı funkci s C linkov´an´ım, kter´a zavol´a funkci, jenˇz je instanc´ı ˇsablony zajiˇst’uj´ıc´ı, ˇze ˇz´adn´a v´ yjimka neopust´ı tˇelo dan´e funkce. Pouˇzit´ı makra je jednoduch´e (vol´an´ı makra nen´ı ukonˇceno stˇredn´ıkem vzhledem k tomu, ˇze definice funkce stˇredn´ıkem nekonˇc´ı). Parametrem je n´azev funkce, kterou potˇrebujeme obalit. IMPLE M E N T _ C _ C A L L B A C K ( c a l l b a c k _ r e a d C l u s t e r s )
Pouˇzit´ı funkce, kter´e je v´ ysledkem pr´ace makra je oˇcek´avan´e. 33
ˇ CVUT Praha
´ IMPLEMENTACE 4 SOUKROMA
exec ( sqlite3Handle , query , c_callback_readPoints , this ) ;
Nutno ˇr´ıci, ˇze vzhledem k tomu, ˇze bylo stejnˇe pouˇzito makro, by bylo moˇzn´e nepouˇz´ıt ˇsablonovou funkci v˚ ubec a nahradit ji makrem. To by ale makro velmi prodlouˇzilo. Pouˇzit´ı ˇsablony je bezpeˇcnˇejˇs´ı a obecnˇe doporuˇcovan´e ˇreˇsen´ı probl´emu tohoto typu. V´ yjimku zachycenou v tˇele callback funkce je vhodn´e nˇejak´ ym zp˚ usobem uloˇzit a pˇr´ıpadnˇe ji ve vhodn´ y okamˇzik zase vyvolat. O tom, jak to prov´est, pojedn´av´a ˇca´st 5. Toto ˇreˇsen´ı nebylo v koneˇcn´e implementaci tˇr´ıdy SqliteReader pouˇzito. D˚ uvodem je jeho obt´ıˇzn´a pochopitelnost pro ˇcten´aˇre k´odu.
4
Soukrom´ a implementace
V C++ je pravidlem, kter´e se vˇetˇsinou dodrˇzuje, ˇze datov´e ˇcleny tˇr´ıd se deklaruj´ı jako soukrom´e. Tyto soukrom´e ˇcleny jsou sice nedostupn´e, ale jsou st´ale viditeln´e. Pokud je chceme opravdu skr´ yt m˚ uˇzeme pouˇz´ıt soukromou implementaci. Soukrom´a imlementace (Private Implementation, Pimpl ) b´ yv´a nˇekdy zaˇrazov´ana mezi n´avrhov´e vzory. Tˇr´ıda SqliteReader m´a v objektu soukrom´e implementace vˇsechny datov´e ˇcleny. Avˇsak obecnˇe lze do soukrom´e implementace um´ıstit i vˇsechny soukrom´e metody. Tˇr´ıda SqliteReader obsahuje ukazatel na strukturu typu ReaderData, kter´ y je implementaˇcn´ım objektem. Pˇri deklaraci tˇr´ıdy se soukromou implementac´ı, lze vyuˇz´ıt dopˇredn´e deklarace (forward declaration). Pouˇzit´ı pak vypad´a n´asledovnˇe. 1
// sqlitereader . h :
2
struct ReaderData ; // forward declaration
3 4
class SqliteReader
5
{
6
public :
7
// ...
8
private :
9
// ...
34
ˇ CVUT Praha
ReaderData * readerData ; // pointer to private data
10 11
´ IMPLEMENTACE 4 SOUKROMA
};
D´ıky dopˇredn´e deklaraci je moˇzn´e pouˇz´ıt ukazatel na strukturu ReaderData, avˇsak to, jak skuteˇcnˇe struktura vypad´a, z˚ ust´av´a skryto. Deklarace struktury je aˇz v implementaˇcn´ım souboru. Pˇr´ıstup k jej´ım ˇclen˚ um je proto moˇzn´ y jen tam. Obecnˇe b´ yv´a v´ yhodn´e deklarovat strukturu v priv´atn´ı ˇca´sti tˇr´ıdy. Pro SqliteReader tomu tak ale nen´ı. Je totiˇz nutn´e, aby ke struktuˇre a jej´ım ˇclen˚ um mˇeli pˇr´ıstup callback funkce, kter´e nejsou a nem˚ uˇzou b´ yt jej´ımi metodami. Implementaˇcn´ı objekt je deklarov´an skuteˇcnˇe jako struktura (struct) se vˇsemi ˇcleny veˇrejn´ ymi. Mimo jin´e pr´avˇe kv˚ uli callback funkc´ım. Pokud bychom strukturu ReaderData deklarovali jako soukromou a vnoˇrenou ve tˇr´ıdˇe SqliteReader a ve struktuˇre ReaderData zavedli soukromou ˇca´st, callback funkce bychom museli deklarovat jako pˇra´telsk´e. Pˇr´atelstv´ı bychom museli deklarovat jak ve tˇr´ıdˇe SqliteReader, kv˚ uli pˇr´ıstupu k soukrom´e struktuˇre ReaderData, tak i ve struktuˇre ReaderData, kv˚ uli pˇr´ıstupu k jej´ım soukrom´ ym ˇclen˚ um. Alternativnˇe bychom mohli vybavit strukturu ReaderData sadou pˇr´ıstupov´ ych funkc´ı. To vˇse je vˇsak zbyteˇcn´a komplikace. Callback funkce vlastnˇe hraj´ı roli metod struktury ReaderData, a proto k n´ı maj´ı pˇr´ıstup. Ze stejn´eho d˚ uvodu maj´ı pˇr´ıstup i k jej´ım soukrom´ ym sloˇzk´am. Pˇri pouˇzit´ı soukrom´e implementace je nutn´e vyˇreˇsit kop´ırov´an´ı a pˇriˇrazen´ı, aby se nestalo to, ˇze dva objekty budou sd´ılet stejn´ y implementaˇcn´ı objekt (to obvykle totiˇz nen´ı naˇs´ım c´ılem). Tˇr´ıda SqliteReader m´a zak´azan´ y kop´ırovac´ı konstruktor a oper´ator pˇriˇrazen´ı. Kop´ırov´an´ı ned´av´a v jej´ım pˇr´ıpadˇe smysl, protoˇze reprezentuje jeden zdroj (jeden datab´azov´ y soubor a spojen´ı s n´ım). Kop´ırov´an´ı implementaˇcn´ıho objektu je t´ımto pro tˇr´ıdu SqliteReader vyˇreˇseno. Mezi v´ yhody soukrom´e implementace patˇr´ı velik´ y stupeˇ n oddˇelen´ı rozhran´ı od implementace. D´ıky pouˇzit´ı dopˇredn´e deklarace nen´ı nutn´e, aby hlaviˇckov´ y soubor vkl´adal jin´e hlaviˇckov´e soubory kv˚ uli tomu, aby mohl m´ıt datov´e sloˇzky dan´ ych typ˚ u. Tyto hlaviˇckov´e soubory se vkl´adaj´ı aˇz do implementaˇcn´ıho souboru. Dalˇs´ı ˇcasto uv´adˇenou v´ yhodou je rychlejˇs´ı kompilace. Tato v´ yhoda se vˇsak projev´ı jen u vˇetˇs´ıch tˇr´ıd a projekt˚ u. Nev´ yhodou soukrom´e implementace je pˇredevˇs´ım vˇetˇs´ı sloˇzitost k´odu.
35
ˇ CVUT Praha
´ ´ 5 POLYMORFN´I PRACE S VYJIMKAMI
Pouˇzit´e soukrom´e implementace se podob´a vytvoˇren´ı ob´alky kolem jin´e tˇr´ıdy. Soukrom´a impleplementace b´ yv´a tak´e oznaˇcov´ana jako Pointer to Implementation ˇci Private Class Data. Souvis´ı tak´e s n´avrhov´ ym vzorem Most (Bridge). Pˇri implementaci tˇr´ıdy SqliteReader byla vˇsak pouˇzita v´ yˇse popsan´a z´akladn´ı podoba soukrom´e implementace.
5
Polymorfn´ı pr´ ace s v´ yjimkami
Z´akladem pro polymorfn´ı pr´aci s v´ yjimkami je klonov´an´ı (cloning). Informace o nˇem lze z´ıskat napˇr´ıkad z [1, str. 424]
5.1
Klonov´ an´ı
V pˇr´ıpadˇe, ˇze potˇrebujeme kop´ırovat objekt, jehoˇz pˇresn´ y typ nezn´ame (m´ame jen referenci ˇci ukazatel), nem˚ uˇzeme pouˇz´ıt kop´ırovac´ı konstruktor. Ten totiˇz nen´ı a nem˚ uˇze b´ yt virtu´aln´ı a to n´am zabraˇ nuje pracovat polymorfnˇe. Jeho pouˇzit´ı, vˇedom´e ˇci nevˇedom´e, m˚ uˇze v´est k oˇrez´an´ı (dˇelen´ı) objektu (slicing). N´asleduj´ıc´ı v´ ypis ukazuje, jak snadno dojde k oˇrez´an´ı objektu, tj. zavol´a se kop´ırovac´ı konstruktor z´akladn´ı tˇr´ıdy, i kdyˇz pracujeme s odvozenou. K zavol´an´ı kop´ırovac´ıho konstruktoru dojde, kdyˇz objekt pˇred´av´ame hodnotou. Nov´ y objekt bude vˇzdy objektem z´akladn´ı tˇr´ıdy nehledˇe na to, jak´ ym byl ten p˚ uvodn´ı. Pˇriprav´ıme se tak o polymorfn´ı chov´an´ı. Jak je tak´e vidˇet, pˇred´av´ame-li objekt referenc´ı, ke kop´ırov´an´ı nedojde, a tak nedojde ani k oˇrez´an´ı. 1
class Base {
2
public :
3
virtual ~ Base () { }
4
virtual std :: string name () { return " Base " ; }
5
};
6
class Derived : public Base {
7
public : virtual std :: string name () { return " Derived " ; }
8 9
};
10
36
ˇ CVUT Praha
11
void print_val ( Base b ) { std :: cout << " name is " << b . name () << std :: endl ;
12 13 14
} void print_ref ( Base & b ) { std :: cout << " name is " << b . name () << std :: endl ;
15 16
´ ´ 5 POLYMORFN´I PRACE S VYJIMKAMI
}
17 18
void test () {
19
Derived d ;
20
print_val ( d ) ; // prints : name is Base
21
print_ref ( d ) ; // prints : name is Derived
22
}
M˚ uˇze se vˇsak st´at, ˇze objekt mus´ıme korektnˇe zkop´ırovat, i kdyˇz nev´ıme jeho pˇresn´ y typ. A tak tomu tak´e je, kdyˇz chceme ukl´adat v´ yjimky (viz t´eˇz 3.5). M´ısto kop´ırov´an´ı je v tomto pˇr´ıpadˇe nutn´e pouˇz´ıt klonov´an´ı (cloning). Pˇri klonovan´ı vol´ame m´ısto kop´ırovac´ıho konstruktoru virtu´aln´ı funkci, kter´a vol´a kop´ırovac´ı konstruktor nov´eho objektu. Virtu´aln´ı funkce zn´a skuteˇcn´ y typ objektu a zavol´a tedy spr´avn´ y kop´ırovac´ı konstruktor. Klonovac´ı funkce zpravidla vytv´aˇr´ı nov´ y objekt pomoc´ı new a vrac´ı ukazatel na nov´ y objekt (pˇridˇelenou pamˇet’ uvolˇ nuje volaj´ıc´ı). Pˇredchoz´ı pˇr´ıklad je v n´asleduj´ıc´ım v´ ypisu pˇrepsan´ y tak, aby funkce pouˇzit´a na ˇra´dku 23 (n´asleduj´ıc´ıho v´ ypisu) mˇela vlastn´ı kopii objektu. D´ıky klonov´an´ı ji m´a a z´aroveˇ n se jedn´a objekt spr´avn´eho typu (tedy opravdu o kopii p˚ uvodn´ıho objektu). 1
class Base {
2
public :
3
virtual ~ Base () { }
4
virtual Base * clone () { return new Base (* this ) ; }
5
virtual std :: string name () { return " Base " ; }
6
};
7
class Derived : public Base {
8
public : virtual Derived * clone () { return new Derived (* this ) ; }
9
virtual std :: string name () { return " Derived " ; }
10 11
};
12
37
ˇ CVUT Praha
13
void print_val ( Base * b ) { std :: cout << " name is " << b - > name () << std :: endl ;
14 15 16
} void print_ref ( Base & b ) { std :: cout << " name is " << b . name () << std :: endl ;
17 18
´ ´ 5 POLYMORFN´I PRACE S VYJIMKAMI
}
19 20
void test () {
21
Derived d ;
22
Base * b = d . clone () ;
23
print_val ( b ) ; // prints : name is Derived
24
print_ref ( d ) ; // prints : name is Derived
25
delete b ;
26
}
Pokud z´aroveˇ n v z´akladn´ı tˇr´ıdˇe oznaˇc´ıme kop´ırovac´ı konstruktor za chr´anˇen´ y, znemoˇzn´ıme t´ım okol´ı kop´ırovat objekty a d´ıky tomu tak´e nem˚ uˇze doj´ıt k nechtˇen´emu oˇrez´an´ı objektu. Kaˇzd´ y potomek z´akladn´ı tˇr´ıdy mus´ı definovat klonovac´ı metodu. Vzhledem k tomu, ˇze z´akladn´ı tˇr´ıda bude nejsp´ıˇse tˇr´ıdou abstraktn´ı nic nebr´an´ı tomu, aby klonovac´ı metoda byla ˇcistˇe virtu´aln´ı. D´ıky tomu kaˇzd´ y pˇr´ım´ y potomek naˇs´ı z´akladn´ı tˇr´ıdy bude muset definovat vlastn´ı klonovac´ı metodu.14 Obdobn´ ym postupem, jak´ y se pouˇzije pˇri klonov´an´ı, je moˇzn´e tvoˇrit nov´e objekty obecnˇe, nikoli jen kopie (klony). Dokonce tak lze ˇreˇsit i u ´plnˇe jin´e vˇeci, jako je napˇr´ıklad vyvol´av´an´ı v´ yjimek (viz n´asleduj´ıc´ı ˇca´st).
5.2
Ukl´ ad´ an´ı a vyvol´ av´ an´ı v´ yjimek
Standardn´ı zpracov´an´ı v´ yjimek vypad´a n´asledovnˇe. Nˇekde uvnitˇr bloku try se vyvol´a v´ yjimka, napˇr´ıklad uvnitˇr vol´an´ı nˇejak´e funkce. Samotn´eho vyvol´an´ı se dos´ahne pˇr´ıkazem throw. V´ yjimka pot´e putuje tak dlouho neˇz se dostane k pˇr´ısluˇsn´emu bloku catch, kter´ y ji zachyt´ı. D˚ uleˇzit´e pˇritom je, ˇze v´ yjimky by se mˇely vyvol´avat hodnotou a zachyt´avat odkazem. 14
Jak vidno, nen´ı zajiˇstˇeno, ˇze nepˇr´ım´ y potomek definuje svou vlastn´ı klonovac´ı metodu. Jak
zajistit s vyuˇzit´ım nevirtu´ aln´ıho rozhran´ı, ˇze potomci sv´e vlastn´ı klonovac´ı metody definuj´ı, je pops´ ano v [4, typy 39 a 54].
38
ˇ CVUT Praha
´ ´ 5 POLYMORFN´I PRACE S VYJIMKAMI
N´asleduje v´ ypis, kde jsou dvˇe tˇr´ıdy, kter´e budu pouˇz´ıvat v dalˇs´ıch pˇr´ıkladech. Budu je pouˇz´ıvat jako tˇr´ıdy v´ yjimek. Obsahuj´ı metody pro klonov´an´ı a pro snadn´e zjiˇstˇen´ı typu. 1
class Base {
2
public :
3
virtual ~ Base () { }
4
virtual Base * clone () { return new Base (* this ) ; }
5
virtual std :: string name () { return " Base " ; }
6
};
7
class Derived : public Base {
8
public : virtual Derived * clone () { return new Derived (* this ) ; }
9
virtual std :: string name () { return " Derived " ; }
10 11
};
Obˇe uveden´e tˇr´ıdy maj´ı veˇrejn´ y kop´ırovac´ı konstruktor (dopln´ı jej pˇrekladaˇc). Veˇrejn´ y kop´ırovac´ı konstruktor je nutn´ y k tomu, aby v´ yjimka mohla b´ yt vyvol´ana pomoc´ı throw. Jak vypad´a standardn´ı zpracov´an´ı v´ yjimek, je uvedeno na n´asleduj´ıc´ım jednoduch´em v´ ypise. Poznamenejme, ˇze na poˇrad´ı blok˚ u catch z´aleˇz´ı. 1
try { throw Derived () ;
2 3 4
} catch ( Derived & e ) { std :: cout << " Derived exception " << std :: endl ;
5 6 7
} catch ( Base & e ) { std :: cout << " Base exception " << std :: endl ;
8 9
}
Ted’ ovˇsem uvaˇzme moˇznost, uvedenou v ˇc´asti 3.5. Totiˇz tu, kdy potˇrebujeme vˇsechny v´ yjimky zachytit a jeˇstˇe pˇredat informaci o tom, jak´a v´ yjimka byla zachycena. Ide´aln´ım ˇreˇsen´ım je vytvoˇrit kopii zachycen´e v´ yjimky, nˇekam si ji uloˇzit (napˇr´ıklad do objektu, se kter´ ym pr´avˇe pracujeme) a posl´eze (aˇz bude kontrola nad bˇehem programu zase v naˇsich rukou) si ji odtud zase vyzvednout. Kopii vˇsak nem˚ uˇzeme vytvoˇrit kop´ırovac´ım konstruktorem, protoˇze nezn´ame pˇresn´ y typ 39
ˇ CVUT Praha
´ ´ 5 POLYMORFN´I PRACE S VYJIMKAMI
v´ yjimky. Ten bychom znali, jen kdyˇz bychom mˇeli blok catch pro kaˇzd´ y typ v´ yjimky. To ale nen´ı dobr´e ˇreˇsen´ı. Mus´ıme zn´at vˇsechny typy v´ yjimek a nav´ıc vˇzdy, kdyˇz se do hierarchie v´ yjimek pˇrid´a nov´a, mus´ıme ji pˇridat i sem. Klonov´an´ı je ˇreˇsen´ım naˇseho probl´emu. Aniˇz bychom museli zjiˇst’ovat pˇresn´ y typ v´ yjimky z´ısk´ame jej´ı pˇresnou kopii (viz t´eˇz ˇca´st 5.1). N´asleduj´ıc´ı v´ ypis demonstruje naklonov´an´ı v´ yjimky. 1
Base * a = 0;
2
try { throw Derived () ;
3 4 5
} catch ( Base & e ) { a = e . clone () ;
6 7 8
} if ( a ) { std :: cout << a - > name () << std :: endl ; // prints : Derived
9
delete a ;
10 11
}
Nicm´enˇe, to, ˇze v´ yjimku ukl´ad´ame a dok´aˇzeme z n´ı pˇreˇc´ıst u ´daje, jeˇstˇe nemus´ı staˇcit. Je totiˇz moˇzn´e, ˇze v´ yjimku budeme muset znovu vyvolat, a to proto, ˇze nen´ı jasn´e, jak s n´ı naloˇzit. Napˇr´ıklad v pˇr´ıpadˇe tˇr´ıdy SqliteReader nen´ı jasn´e, jak hl´asit chybu uˇzivateli programu. Pokus o vyvol´an´ı pomoc´ı pˇr´ımo pomoc´ı throw se vˇsak nesetk´a s u ´spˇechem. N´asleduj´ıc´ı v´ ypis vych´az´ı z pˇredchoz´ıho. Pˇrid´av´a jeˇstˇe dalˇs´ı blok try-catch. Ten pˇredstavuje klientsk´ y k´od. P˚ uvodn´ı, vnitˇrn´ı blok try-catch pˇredstavuje k´od, ze kter´eho nem˚ uˇzeme vyvolat v´ yjimku, avˇsak chceme ji zaznamenat. 1
try {
2
Base * a = 0;
3
try { throw Derived () ;
4 5 6
} catch ( Base & e ) { a = e . clone () ;
7 8 9 10
} if ( a ) { throw * a ;
40
ˇ CVUT Praha
delete a ;
11
}
12 13 14
} catch ( Derived & e ) { std :: cout << " Derived exception \ n " ; // unused
15 16 17
´ ´ 5 POLYMORFN´I PRACE S VYJIMKAMI
} catch ( Base & e ) { std :: cout << " Base exception \ n " ; // prints : Base
18
exception 19
}
D˚ uvodem proˇc byla zachycena z´akladn´ı v´ yjimka a ne odvozen´a je ten, ˇze pˇr´ıkaz throw zp˚ usob´ı vol´an´ı kop´ırovac´ıho konstruktoru. Pˇrekladaˇc vˇsak nezn´a skuteˇcn´ y typ objektu a pouˇzije kop´ırovac´ı konstruktor z´akladn´ı tˇr´ıdy. Dojde tedy k oˇrez´an´ı objektu (viz 5.1). Je tedy nutn´e v´ yjimku nejen polymorfnˇe zkop´ırovat, ale i vyvolat. Kv˚ uli tomu je potˇreba zav´est dalˇs´ı virtu´aln´ı metodu. Ta je v mnoh´em podobn´a klonovac´ı metodˇe. Vhodn´e jm´eno je raise. Mus´ı ji implementovat kaˇzd´a tˇr´ıda z hierarchie v´ yjimek. Implementace je pro kaˇzdou tˇr´ıdu stejn´a. virtual void raise () const { throw * this ; }
Pro opˇetovn´e vyvol´an´ı v´ yjimky pak pouˇzijeme nam´ısto pˇr´ıkazu throw metodu raise. ˇ adek ˇc´ıslo 10 v´ R´ ypisu, kter´ y demonstroval opˇetovn´e vyvol´av´an´ı v´ yjimek, se zmˇen´ı, jak je uk´az´ano na n´asleduj´ıc´ım ˇra´dku k´odu. a - > raise () ;
V´ yjimka, kter´a bude vyvol´ana, bude m´ıt spr´avn´ y typ, tj. typ kter´ y odpov´ıd´a p˚ uvodn´ı, dˇr´ıve zachycen´e a uloˇzen´e v´ yjimce.
5.3
Implementace ve tˇ r´ıdˇ e SqliteReader
V gama-local je jedna z´akladn´ı tˇr´ıda, ze kter´e se odvozuj´ı vˇsechny ostatn´ı tˇr´ıdy v´ yjimek15 . Tou tˇr´ıdou je GNU gama::Exception::base. Identifik´atory GNU gama a 15
Stejn´ a tˇr´ıda je z´ akladn´ı i pro v´ yjimky knihovny Matvec
41
ˇ CVUT Praha
´ ´ 5 POLYMORFN´I PRACE S VYJIMKAMI
Exception oznaˇcuj´ı prostory jmen16 . Tato tˇr´ıda dˇed´ı z tˇr´ıdy std::exception, z´akladn´ı tˇr´ıdy v´ yjimek ve standardn´ı knihovnˇe. To znamen´a, ˇze stejnˇe jako std::exception m´a virtu´aln´ı destruktor a virtu´aln´ı metodu what a je tedy polymorfn´ı tˇr´ıdou. D´ale deklaruje dalˇs´ı dvˇe virtu´aln´ı metody, metodu clone a metodu raise. Obˇe metody jsou ˇcistˇe virtu´aln´ı a tˇr´ıda Exception::base je tedy abstraktn´ı tˇr´ıdou. D´ıky tomu, ˇze je tˇr´ıda Exception::base odvozena z std::exception, je moˇzn´e (pokud vyvstane potˇreba) odchyt´avat vˇsechny v´ yjimky, tj. v´ yjimky ze standardn´ı knihovny i knihovny GNU Gama, jedin´ ym blokem catch. A z´aroveˇ n bude dostupn´a metoda what a tedy i odpov´ıdaj´ıc´ı chybov´a zpr´ava. Tˇr´ıda v´ yjimek, kterou pouˇz´ıv´a tˇr´ıda SqliteReader a jej´ı implementace, se jmenuje Exception::sqlitexc. Tˇr´ıda Exception::sqlitexc nedˇed´ı pˇr´ımo z abstaktn´ı tˇr´ıdy Exception::base, ale je potomkem tˇr´ıdy Exception::string, kter´a pˇrid´av´a datov´ y ˇclen obsahuj´ıc´ı pˇred´avanou zpr´avu ve formˇe ˇretˇezce a d´ale implementuje funkci what tak, aby vr´atila tento ˇretˇezec. 1
// inderr . h :
2
namespace GNU_gama { namespace Exception {
3
class base : public std :: exception
4
{
5
public :
6
virtual base * clone () const = 0;
7
virtual void };
8 9
raise () const = 0;
}}
10 11
// exception . h :
12
namespace GNU_gama { namespace Exception {
13
class string : public base {
14
public :
15
const std :: string
16
string ( const std :: string & s ) : str ( s ) { } 16
str ;
Prostor jmen GNU gama nebudu d´ale uv´adˇet, prostor jmen Exception vˇsak ano.
42
ˇ CVUT Praha
´ ´ 5 POLYMORFN´I PRACE S VYJIMKAMI
17
~ string () throw () {}
18
string *
clone () const { return new string (* this ) ; }
19
void
raise () const { throw * this ; }
20
const char * what ()
const throw () { return
str . c_str () ; } };
21 22
}}
23 24
// sqlitereader . h :
25
namespace GNU_gama { namespace Exception {
26
class sqlitexc : public GNU_gama :: Exception :: string
27
{
28
public : sqlitexc ( const std :: string & message )
29 30
: string ( message )
31
{ }
32
sqlitexc * clone () const { return new sqlitexc (* this ) ; }
33
void };
34 35
raise () const { throw * this ; }
}}
Z´akladn´ı k´od callback funkce je na n´asleduj´ıc´ım v´ ypisu. Pokud dojde k vyvol´an´ı v´ yjimky, v´ yjimka bude zachycena jedn´ım ze tˇr´ı blok˚ u catch. V´ yjimky odvozen´e od Exception::base budou zachyceny prvn´ım blokem, kter´ y provede klonov´an´ı a uloˇz´ı ukazatel na novou v´ yjimku do dat tˇr´ıdy SqliteReader (pˇresnˇeji do struktury ReaderData). Pokud v´ yjimka nen´ı odvozena od Exception::base, ale je odvozena od std::exception (jej´ı rozhran´ı neumoˇzn ˇuje klonov´an´ı), bude m´ısto n´ı uloˇzena v´ yjimka typu Exception::string. Jako ˇretˇezec obsahuj´ıc´ı zpr´avu j´ı bude nastavena zpr´ava poskytnut´a metodou what. Blok catch s v´ ypustkou pak zachyt´ı vˇsechny ostatn´ı chyby. Uloˇzena bude opˇet v´ yjimka typu Exception::string. Jako zpr´ava ji bude nastaven ˇretˇezec ud´avaj´ıc´ı, ˇze doˇslo k nezn´am´e (neoˇcek´avan´e) v´ yjimce v callback funkci. 1
int readSomething ( void * data , int argc , char ** argv , char **)
2
{
3
ReaderData * d = static_cast < ReaderData * >( data ) ;
43
ˇ CVUT Praha
´ ´ 5 POLYMORFN´I PRACE S VYJIMKAMI
try {
4 5
// ... callback ’s code
6
return 0; }
7
catch ( GNU_gama :: Exception :: base & e ) {
8
d - > exception = e . clone () ;
9
}
10
catch ( std :: exception & e ) {
11
d - > exception =
12
new GNU_gama :: Exception :: string ( e . what () ) ;
13
}
14
catch (...) {
15
d - > exception =
16
new GNU_gama :: Exception :: string ( " unknown " ) ;
17
}
18
return 1;
19 20
}
Funkce exec z implementace tˇr´ıdy SqliteReader, kter´a obaluje vol´an´ı knihovn´ı yjimky. Ve v´ ypisu je vyfunkce sqlite3 exec, zajist´ı opˇetovn´e vyvol´an´ı uloˇzen´e v´ nech´an k´od, kter´ y se net´ yk´a opˇetovn´eho vyvol´an´ı v´ yjimky. 1
void exec ( sqlite3 * sqlite3Handle , const std :: string & query ,
2
S q l i t e R e a d e r C a l l b a c k T y p e callback ,
3
ReaderData * readerData )
4
{
5
char * errorMsg = 0;
6
int rc = sqlite3_exec ( sqlite3Handle , query . c_str () , callback , readerData , & errorMsg ) ;
7
if ( rc != SQLITE_OK ) {
8
if ( readerData - > exception != 0) {
9
readerData - > exception - > raise () ;
10
}
11
// ...
12
}
13 14
}
44
ˇ CVUT Praha
6
ˇ ´IDA SQLITEREADER A JEJ´I IMPLEMENTACE 6 TR
Tˇ r´ıda SqliteReader a jej´ı implementace
Tˇr´ıda SqliteReader zajiˇst’uje ˇcten´ı dat z datab´aze SQLite v programu gama-local . Tˇr´ıda je um´ıstˇena v prostoru jmen sqlite db, kter´ y je souˇca´st´ı prostoru jmen GNU gama::local. V prostoru jmen local, kter´ y je souˇc´ast´ı prostoru jmen GNU gama, je um´ıstˇena vˇetˇsina tˇr´ıd a funkc´ı, kter´e se t´ ykaj´ı programu gama-local . Definice tˇr´ıdy je v hlaviˇckov´em souboru sqlitereader.h. Jej´ı implementace je ve zdrojov´em souboru sqlitereader.cpp. Oba soubory lze z´ıskat spoleˇcnˇe s ostatn´ımy zdrojov´ ymi k´ody projektu GNU Gama (viz 1), nebo samostatnˇe z n´asleduj´ıc´ı adresy. http://git.savannah.gnu.org/cgit/gama.git/tree/lib/gnu_gama/local/ Dokumentace tˇr´ıdy SqliteReader a jej´ı implementace je v dokumentaˇcn´ıch koment´aˇr´ıch, kter´e jsou souˇca´st´ı zdrojov´ ych k´od˚ u. Dokumentace je v anglick´em jazyce. Pro vytvoˇren´ı dokumentace ve formˇe HTML str´anek a PDF dokumentu byl pouˇzit program Doxygen. Zkr´acen´a verze PDF dokumentu je v pˇr´ıloze B.
6.1
Rozhran´ı
Rozhran´ı tˇr´ıdy tvoˇr´ı konstruktor, kter´ y pˇreb´ır´a jeden parametr typu std::string reprezentuj´ıc´ı n´azev datab´azov´eho souboru, a metoda retrieve. Metoda retrieve naˇcte konfiguraci uloˇzenou v datab´azi do objektu typu LocalNetwork. Metoda pˇreb´ır´a jako parametr referenci na ukazatel na objekt typu LocalNetwork. Dalˇs´ı parametr je typu std::string a reprezentuje jm´eno konfigurace, kter´a je uloˇzena v datab´azi. To, ˇze je prvn´ı parametr (nekonstantn´ı) reference, umoˇzn ˇuje metodˇe zmˇenit hodnotu ukazatele. K tomu dojde ve chv´ıli, kdy je metodˇe pˇred´an nulov´ y ukazatel (NULL). V tomto pˇr´ıpadˇe bude vytvoˇren nov´ y objekt tˇr´ıdy LocalNetwork na z´akladˇe algoritmu, kter´ y je uloˇzen v datab´azi.17 T´ım je programu gama-local umoˇznˇeno, aby pokud uˇzivatel nezad´a algoritmus, pouˇzil ten, kter´ y je uloˇzen v datab´azi, nam´ısto toho, aby pouˇzil implicitn´ı algoritmus. Hlaviˇcky konstruktoru a metody retrieve ukazuje n´asleduj´ıc´ı v´ ypis. SqliteReader ( const std :: string & fileName ) ; 17
Tˇr´ıda LocalNetwork je abstraktn´ı. Konkr´etn´ı tˇr´ıdy se liˇs´ı podle pouˇzit´eho algoritmu, takˇze je
moˇzn´e je vytv´ aˇret, jen pokud ho zn´ ame.
45
ˇ CVUT Praha
ˇ ´IDA SQLITEREADER A JEJ´I IMPLEMENTACE 6 TR
void retrieve ( LocalNetwork *& lnet , const std :: string & configuration ) ;
Jako tˇr´ıda v´ yjimek pro hl´aˇsen´ı chyb vznikl´ ych pˇri ˇcten´ı z datab´aze slouˇz´ı tˇr´ıda Exception::sqlitexc.
6.2
Implementace
Ve tˇr´ıdˇe je vyuˇzita technika soukrom´e implementace, popsan´a v ˇca´sti 4, proto m´a pouze jeden datov´ y ˇclen, kter´ y je soukrom´ y. Tento datov´ y ˇclen je typu ReaderData. Tˇr´ıda ReaderData je definov´ana v souboru sqlitereader.cpp a je souˇc´as´ı prostoru jmen sqlite db. Soubor sqlitereader.h obsahuje pouze dopˇrednou deklaraci tˇr´ıdy ReaderData. Tˇr´ıda SqliteReader nem´a ˇza´dn´e soukrom´e metody. Do implementace tˇr´ıdy patˇr´ı kromˇe tˇr´ıdy ReaderData tak´e funkce v bezejmenn´em prostoru jmen (v souboru sqlitereader.cpp) a pˇredevˇs´ım callback funkce, pomoc´ı kter´ ych prob´ıh´a ˇcten´ı z datab´aze. Tˇr´ıda SqliteReader zajiˇst’uje spojen´ı s datab´az´ı. Pˇritom vyuˇz´ıv´a techniku RAII (resource acquisition is initialization) popsanou napˇr´ıklad v [1, str. 366]. Technika spoˇc´ıv´a v tom, ˇze se zdroj alokuje pˇri inicializaci objektu a uvoln´ı se pˇri jeho destrukci. V naˇsem pˇr´ıpadˇe to znamen´a, ˇze konstruktor tˇr´ıdy ReaderData otev´ır´a datab´azi (funkc´ı sqlite3 open) a destruktor datab´azi uzav´ır´a (funkc´ı sqlite3 close). A vzhledem k tomu, ˇze konstruktor v pˇr´ıpadˇe ne´ uspˇeˇsn´eho pokusu o otevˇren´ı datab´aze vyvol´a v´ yjimku, nem˚ uˇze se st´at, ˇze bychom nevˇedomky pracovali s neotevˇrenou datab´az´ı. Podobnˇe d´ıky tomu, ˇze uzavˇren´ı datab´aze probˇehne automaticky v destruktoru, nem˚ uˇze se st´at, ˇze bychom datab´azi nezavˇreli. K uzavˇren´ı dojde i v pˇr´ıpadˇe, ˇze byla vyvol´ana v´ yjimka. Destruktor bude zavol´an automaticky a d´ıky nˇemu i funkce sqlite3 close. Ukazatel na datab´azov´ y objekt obsahuje samozˇrejmˇe tˇr´ıda ReaderData. Protoˇze je ke komunikaci s datab´az´ı pouˇzito SQLite C/C++ API (rozhran´ı s callback funkcemi), bylo tˇreba db´at na spr´avn´e propojen´ı jazyk˚ u C a C++, kter´ ym se zab´ yv´a ˇc´ast 3. Konkr´etn´ı implementace pro tˇr´ıdy SqliteReader a ReaderData je podrobnˇeji rozebr´ana v ˇc´asti 3.3.3. N´azvy callback funkc´ı zaˇc´ınaj´ı sqlite db , aby se zabr´anilo konfliktu jmen a 46
ˇ CVUT Praha
ˇ ´IDA SQLITEREADER A JEJ´I IMPLEMENTACE 6 TR
pˇr´ıpadnˇe ˇspatn´emu poˇzit´ı. Prefix sqlite db je odvozen z n´azvu prostoru jmen, ve kter´em jsou um´ıstˇeny tˇr´ıdy SqliteReader a ReaderData.
18
Obecn´e informace o callback funkc´ıch pouˇz´ıvan´ ych v SQLite C/C++ API jsou uvedeny v ˇca´sti 2.2. Oˇsetˇren´ı v´ yjimek vznikl´ ych v callback funkc´ıch je udˇel´ano tak, jak popisuje ˇca´st 5.3. V´ yjimky zachycen´e v callback funkc´ıch jsou ukl´ad´any ve tˇr´ıdˇe ReaderData a znovu vyvol´av´any ve funkci exec. Ta je ob´alkou kolem funkce sqlite3 exec. Vˇsechny callback funkce v implementaci tˇr´ıdy SqliteReader oˇcek´avaj´ı, ˇze jim pomoc´ı ukazatele na void bude pˇred´an objekt typu ReaderData. Kaˇzd´a callback funkce vˇsak oˇcek´av´a objekt ReaderData v jin´em stavu. Podle toho lze callback funkce rozdˇelit do dvou skupin. V prvn´ı skupinˇe jsou n´asleduj´ıc´ı funkce (uveden je jen n´azev – n´avratovou hodnotu a parametry maj´ı vˇsechny callback funkce stejn´e). sqlite db readConfigurationInfo sqlite db readConfigurationText sqlite db readPoints sqlite db readClusters Funkce zapisuj´ı r˚ uzn´e hodnoty do objektu tˇr´ıdy LocalNetwork, na kter´ y je ve tˇr´ıdˇe ReaderData ukazatel. Funkce proto oˇcek´avaj´ı, ˇze tento ukazatel je platn´ y. Ve druh´e skupinˇe jsou funkce, kter´e zapisuj´ı u ´daje do urˇcit´ ych objekt˚ u v objektu tˇr´ıdy LocalNetwork. Pˇr´ıkladem takov´ ych objekt˚ u m˚ uˇzou b´ yt objekty typu StandPoint ˇci CovMat. Callback funkce samy o sobˇe vˇsak nev´ı, do jak´eho objektu maj´ı zapisovat. Proto je nutn´e pˇredat jim tuto informaci. K tomu dobˇre poslouˇz´ı ukazatel uloˇzen´ y v objektu tˇr´ıdy ReaderData, kter´ y se callback funkc´ım pˇred´av´a. Tento ukazatel mus´ı nastavit volaj´ıc´ı funkce exec (kter´ y je vlastnˇe nepˇr´ım´ ym volaj´ıc´ım callback funkce). Nab´ız´ı se ot´azka, proˇc nepˇred´avat do callback funkc´ı pomoc´ı ukazatele na void pˇr´ımo ukazatel na zpracov´avan´ y objekt. Odpovˇed’ je prost´a. Nebylo by totiˇz moˇzn´e v callback funkci ukl´adat v´ yjimky, protoˇze by nebylo by kam. N´asleduje seznam callback funkc´ı, kter´e jsou v popsan´e skupinˇe. 18
Callback funkce, kter´e jsou pouˇz´ıvan´e v SQLite C/C++ API a maj´ı tedy C linkov´an´ı, sice
mohou b´ yt um´ıstˇeny v prostoru jmen, ale nezabr´an´ı to pˇr´ıpadn´emu konfliktu jmen.
47
ˇ CVUT Praha
ˇ ´IDA SQLITEREADER A JEJ´I IMPLEMENTACE 6 TR
sqlite db readObservations sqlite db readVectors sqlite db readCoordinates sqlite db readHeightDifferences sqlite db readCovarianceMatrix
6.3
Integrace do gama-local
Aby mohla b´ yt tˇr´ıda SqliteReader zaˇclenˇena do projektu GNU Gama konkr´etnˇe v programu gama-local , bylo nutn´e prov´est prov´est urˇcit´e u ´pravy ve st´avaj´ıc´ım k´odu. V prvn´ı ˇradˇe musela b´ yt vylepˇsena hierarchie v´ yjimek. Pˇredevˇs´ım byly pˇrid´any metody clone a raise, pro umoˇznˇen´ı polymorfn´ı pr´ace s v´ yjimkami. Jak vypad´a ta ˇca´st hierarchie v´ yjimek, kter´a se t´ yk´a tˇr´ıdy SqliteReader, je uvedeno v ˇca´sti 5.3. Musela b´ yt tak´e zmˇena funkce main programu gama-local , kter´a se nach´az´ı v souboru bin/gama-local.cpp. Byl pˇrid´an k´od, kter´ y na z´akladˇe parametr˚ u pˇredan´ ych programu v pˇr´ıkazov´e ˇr´adce naˇcte u ´daje z datab´aze pomoc´ı tˇr´ıdy SqliteReader, nebo naˇcte u ´daje z XML souboru, tak jako tomu bylo v dˇr´ıvˇejˇs´ıch verz´ıch. Do projektu byla tak´e pˇrid´ana jednoduch´a tov´arn´ı metoda, chcete-li funkce, newLocalNetwork. Ta na z´akladˇe n´azvu algoritmu vytvoˇr´ı nov´ y objekt, kter´ y je instanc´ı jedn´e ze tˇr´ıd odvozen´ ych ze tˇr´ıdy LocalNetwork. Pr´avˇe na z´akladˇe n´azvu algoritmu je rozhodnuto, jak´a konkr´etn´ı tˇr´ıda bude pouˇzita. K zaveden´ı tov´arn´ı metody vedl fakt, ˇze v nˇekter´ ych pˇr´ıpadech mus´ı b´ yt nov´ y objekt vytvoˇren ve funkci main programu gama-local a jindy mus´ı b´ yt vytvoˇren v jedn´e z metod tˇr´ıdy SqliteReader. Deklarace tov´arn´ı metody newLocalNetwork n´asleduje. LocalNetwork * newLocalNetwork ( std :: string algorithm = " " ) ;
Parametrem je n´azev algoritmu. N´azvy se shoduj´ı s n´azvy, kter´e jsou pouˇzity v parametrech pˇr´ıkazov´e ˇr´adky. Pokud je ˇretˇezec pr´azdn´ y nebo je n´azev neplatn´ y, je ˇretˇezec nahrazen ˇretˇezcem "gso". Deklarace tov´arn´ı metody newLocalNetwork je v souboru newnetwork.h a je um´ıstˇena do prostoru jmen GNU gama::local. Definice je v souboru newnetwork.cpp. V´ yhodou tohoto ˇreˇsen´ı je, ˇze pro vytv´aˇren´ı objekt˚ u tˇr´ıd odvozen´ ych ze tˇr´ıdy LocalNetwork jiˇz nen´ı potˇreba vkl´adat definice vˇsech tˇechto tˇr´ıd. Dalˇs´ı v´ yhodou je to, ˇze k´od, kter´ y rozhoduje o vytvoˇren´ı konkr´etn´ı tˇr´ıdy na z´akladˇe n´azvu algoritmu, je na jednom m´ıstˇe a z´aroveˇ n m˚ uˇze b´ yt zavol´an 48
ˇ CVUT Praha
´ ´I 7 TESTOVAN
z v´ıce funkc´ı. Toto byl vlastnˇe d˚ uvod zaveden´ı jednoduch´e tov´arn´ı metody. Nutno jeˇstˇe poznamenat, ˇze volaj´ıc´ı k´od nemus´ı zn´at deklarace konkr´etn´ıch tˇr´ıd, protoˇze pouˇz´ıv´a tov´arn´ı metodu. Jak jiˇz bylo uvedeno v ˇc´asti 2, projekt GNU Gama se snaˇz´ı o minim´aln´ı z´avislost na jin´ ych knihovn´ach. Projekt GNU Gama pouˇz´ıv´a pro sestaven´ı na operaˇcn´ıch syst´emech Unixov´eho typu sestavovac´ı syst´em GNU Autotools 19 . Tento syst´em umoˇzn ˇuje sestavit projekt GNU Gama i v pˇr´ıpadˇe, ˇze knihovna SQLite nen´ı na dan´em poˇc´ıtaˇci nainstalov´ana. Pˇri samotn´em sestavov´an´ı zdrojov´ ych k´od˚ u se pak podpora datab´aze SQLite v˚ ubec nevytvoˇr´ı a to d´ıky podm´ınˇen´emu pˇrekladu. Vˇsechny ˇca´sti k´odu, kter´e se t´ ykaj´ı podpory datab´aze, jsou mezi direktivami preprocesoru #ifdef GNU GAMA LOCAL SQLITE READER a #endif. Jedn´a se o obsah hlaviˇckov´eho souboru sqlitereader.h, zdrojov´eho souboru sqlitereader.cpp a nˇekter´e ˇca´sti souboru bin/gama-local.cpp. V pˇr´ıpadˇe, ˇze nen´ı definov´ano makro (identifik´ator) GNU GAMA LOCAL SQLITE READER, nebudou ˇc´asti t´ ykaj´ıc´ı se podpory SQLite datab´aze do pˇrekladu zahrnuty. O tom, zda m´a b´ yt makro definov´ano rozhodne pr´avˇe syst´em GNU Autotools. Vˇetˇsinu k´odu potˇrebn´eho k integraci tˇr´ıdy SqliteReader napsal autor projektu ˇ GNU Gama a vedouc´ı t´eto pr´ace, prof. Ing. Aleˇs Cepek, CSc..
7
Testov´ an´ı
Testov´an´ı je d˚ uleˇzit´e pro kaˇzd´ y program. Pro program gama-local je d˚ uleˇzitˇejˇs´ı o to, ˇze si uˇzivatel programu vˇetˇsinou nem˚ uˇze nijak ovˇeˇrit, ˇze v´ ysledky, kter´e mu program poskytl, jsou spr´avn´e. V´ yvoj tˇr´ıdy SqliteReader – stejnˇe jako v´ yvoj cel´eho projektu GNU Gama – prob´ıh´a pod syst´emy z rodiny GNU/Linux . D´ıky lze pˇri testov´an´ı vyuˇz´ıt ˇradu moˇznost´ı, kter´e tyto syst´emy nab´ız´ı. Kontrola spr´avnosti byla provedena dvˇema zp˚ usoby. Jeden vyuˇz´ıv´a program diff (viz 7.2) a druh´ y vyuˇz´ıv´a program gama-local-cmp (viz 7.3). Pˇr´ımo bˇehem v´ yvoje se vyuˇzily moˇznosti kompil´atoru GCC (viz 7.1). D´ale se samozˇrejmˇe tak´e pouˇz´ıvaly, ne pˇr´ıliˇs uzn´avan´e, avˇsak hojnˇe vyuˇz´ıvan´e, orientaˇcn´ı testy pomoc´ı textov´ ych v´ ypis˚ u vˇseho druhu. 19
http://autotoolset.sourceforge.net/
49
ˇ CVUT Praha
7.1
´ ´I 7 TESTOVAN
Kontroly pomoc´ı kompil´ atoru GCC
Kompil´atory C++ nejen upozorˇ nuj´ı na chyby, kv˚ uli kter´ ym nelze k´od sestavit, ale jsou tak´e schopny poskytnout varov´an´ı, kter´a mohou upozornit na problematick´ y k´od.20 Jak je uvedeno v [4], v C++ je velmi vhodn´e pouˇz´ıvat kontroly bˇehem kompilace. Ty jsou moˇzn´e napˇr´ıklad proto, ˇze C++ je staticky typovan´ y jazyk. Autoˇri toto shrnuj´ı do dvou pravidel (1 a 14). Kompilujte bez varov´an´ı i pˇri vysok´e citlivosti kompil´atoru. Chyby pˇri sestavov´an´ı jsou lepˇs´ı neˇz chyby pˇri bˇehu. Kompil´ator GCC nab´ız´ı ˇradu nastaven´ı, kter´ ymi lze ˇr´ıdit kontroly bˇehem kompilace. Lze urˇcit, v jak´ ych pˇr´ıpadech m´a kompil´ator podat varovnou zpr´avu, a lze tak´e urˇcit, jak´a varov´an´ı se maj´ı st´at chybami, ˇc´ımˇz si lze na program´atorovi vynutit dodrˇzen´ı dodrˇzen´ı prvnˇe jmenovan´eho pravidla. Pˇri v´ yvoji tˇr´ıdy SqliteReader byla pouˇzita cel´a ˇrada nastaven´ı kompil´atoru GCC . Jejich kompletn´ı seznam je uveden v pˇr´ıloze C. Tato nastaven´ı vyvolaj´ı ˇradu varovn´ ych hl´aˇsen´ı, kter´a ˇcasto pouze upozorˇ nuj´ı na nedodrˇzen´ı jist´ ych dobr´ ych zvyk˚ u (napˇr´ıklad nastaven´ı -Weffc++). Projekt GNU Gama nen´ı kompilov´an s tak citliv´ ym nastaven´ım kompil´atoru, jako bylo pouˇzito pro v´ yvoj tˇr´ıdy SqliteReader. Niˇzˇs´ı citlivost kompil´atoru vˇsak nen´ı nutnˇe chybou. Napˇr´ıklad v´ yˇse zm´ınˇen´e nastaven´ı -Weffc++ nut´ı v nˇekter´ ych pˇr´ıpadech explicitnˇe inicializovat objekt typu std::string pr´azdn´ ym ˇret’ezcem (""), aˇckoli je tato inicializace implicitn´ı. Pouˇzit´ı zmiˇ novan´ ych nastaven´ı pˇri kompilaci projektu GNU Gama by zp˚ usobilo velk´e mnoˇzstv´ı varovn´ ych hl´aˇsen´ı, ve kter´ ych by se snadno ztratila varovn´a hl´aˇsen´ı zp˚ usoben´a nov´ ym, vyv´ıjen´ ym k´odem. Avˇsak tˇr´ıda SqliteReader byla vyv´ıjena v samostatn´em projektu a projekt GNU Gama byl pouˇzit jiˇz zkompilovan´ y v podobˇe knihovny. To by vˇsak samo o sobˇe nestaˇcilo ke skryt´ı vˇsech varov´an´ı zp˚ usoben´ ych st´avaj´ıc´ım k´odem projektu GNU Gama, protoˇze vˇzdy je tˇreba vkl´adat hlaviˇckov´e soubory. Nejnovˇejˇs´ı verze kompil´atoru GCC (4.6) umoˇzn ˇuje skr´ yt varov´an´ı z hlaviˇckov´ ych soubor˚ u pomoc´ı pomoc´ı direktivy #pragma. 20
Dnes uˇz podobn´e schopnosti maj´ı i editory, napˇr´ıklad program Qt Creator, kter´ y jsem pouˇz´ıval
pˇri v´ yvoji. Program Qt Creator vˇsak nenab´ız´ı takov´e moˇznosti kontroly jako kompil´ator GCC .
50
ˇ CVUT Praha
´ ´I 7 TESTOVAN
Ve verzi 4.4, kterou pouˇz´ıv´am j´a, tato moˇznost nen´ı, avˇsak je moˇzn´e pˇripojit pˇri kompilaci hlaviˇckov´e soubory jako syst´emov´e, coˇz skryje varov´an´ı z takto pˇripojen´ ych hlaviˇckov´ ych soubor˚ u. Hlaviˇckov´e soubory se pˇripoj´ı jako syst´emov´e tak, ˇze m´ısto obvykl´eho -I se pˇred cestu k soubor˚ um nap´ıˇse -isystem, v m´em pˇr´ıpadˇe tedy -isystem../gama/lib. Dalˇs´ı moˇznost´ı, jak se zbavit varovn´ ych hl´aˇsen´ı je filtrovat hl´aˇsen´ı pomoc´ı programu grep. Toto ˇreˇsen´ı vˇsak nefunguje tak dobˇre, jako kdyˇz se pouˇzije pˇr´ımo kompil´ator. V manu´alu ke kompil´atoru GCC [3] je pro programy v C a C++ doporuˇceno toto nastaven´ı: -ansi -pedantic -Wall -Wextra -Wconversion -Wshadow -Wcast-qual -Wwrite-strings Z tˇechto nastaven´ı zde pro zaj´ımavost vysvˇetl´ım pouze dvˇe. Nastaven´ı -Wconversion zp˚ usob´ı varov´an´ı, pˇri konverzi z double na int. A nastaven´ı -Wshadow zp˚ usob´ı varov´an´ı, kdyˇz lok´aln´ı promˇenn´a zakryje sv´ ym jm´enem jinou promˇenou. Jeˇstˇe je vhodn´e poznamenat, ˇze nastaven´ı -Wextra je stejn´e jako nastaven´ı -W. Oznaˇcen´ı -W je vˇsak zastaral´e. Jak jiˇz bylo uvedeno, pˇri v´ yvoji tˇr´ıdy SqliteReader bylo pouˇzito v´ıce nastaven´ı (viz pˇr´ıloha C). Nastaven´ı doporuˇcen´a manu´alem jsou vˇsak ta nejuˇziteˇcnˇejˇs´ı. Nicm´enˇe ˇz´adn´ y kompil´ator nem˚ uˇze zachytit vˇsechny chyby, proto je vˇzdy nutn´e prov´adˇet testov´an´ı.
7.2
Testov´ an´ı pomoc´ı programu diff
Program diff je jedn´ım z mal´ ych jedno´ uˇcelov´ ych program˚ u pouˇz´ıvan´ ych na unixov´ ych syst´emech, jin´ ymi slovy patˇr´ı mezi takzvan´e unixov´e utility. Program umoˇzn ˇuje porovnat mezi sebou dva textov´e soubory. Testov´an´ı programu gama-local spoˇc´ıv´a v tom, ˇze se programem gama-local spoˇc´ıt´a s´ıt’. Pˇri tom se je jako v´ ystupn´ı form´at nastav´ı prost´ y textov´ y soubor. V´ ypoˇcet se provede dvakr´at. Jednou se data ˇctou z XML souboru a jednou z datab´aze SQLite. Z kaˇzd´eho v´ ypoˇctu je tedy jeden textov´ y soubor. Tyto soubory se n´aslednˇe porovnaj´ı programem diff . Pro hromadn´e testov´an´ı byl vytvoˇren skript pro bash. Ten obsahuje i pˇr´ıkazy, kter´e vytvoˇr´ı a napln´ı SQLite datab´azi. Jako testovac´ı data byla pouˇzita sb´ırka 51
ˇ CVUT Praha
´ ´I 7 TESTOVAN
pˇr´ıklad˚ u, kter´a je souˇca´st´ı projektu GNU Gama, s v´ yjimkou tˇech pˇr´ıklad˚ u, kter´e zp˚ usobuj´ı probl´emy pˇri vyrovn´an´ı.21 Porovn´an´ı programem diff m´a vˇsak jist´e nev´ yhody. Nev´ yhodu, kter´a se projevuje nejˇcastˇeji, je to, ˇze porovn´av´a jednotliv´e znaky, nikoli hodnoty v souborech. V textov´em v´ ystupu se vˇsak nˇekdy st´av´a, ˇze nˇejak´a veliˇcina m´a sice v obou souborech hodnotu nula, ale tato nula m´a v jednom souboru znam´enko kladn´e a ve druh´em z´aporn´e (je to d´ano t´ım, jak funguje textov´ y v´ ystup v jazyce C++ a v programu gama-local ). Program diff takov´eto soubory oznaˇc´ı jako rozd´ıln´e a v´ ysledkem testu je, ˇze se v´ ypoˇcty liˇsily, aˇckoli byly stejn´e. D´ale se m˚ uˇze st´at, v d˚ usledku numerick´eho ˇsumu, ˇze v´ ysledn´e hodnoty nebudou v textov´ ych souborech ve stejn´em poˇrad´ı. V souborech se bude liˇsit poˇrad´ı ˇra´dk˚ ua program diff je opˇet oznaˇc´ı jako rozd´ıln´e, i kdyˇz se v´ ysledky v´ ypoˇct˚ u neliˇs´ı. Dalˇs´ı nev´ yhoda plyne z pouˇzit´ı textov´eho form´atu jako v´ ystupu z programu gama-local . Hodnoty v tomto v´ ystupu jsou totiˇz zaokrouhlen´e, a tak je moˇzn´e, ˇze nebudou odhaleny mal´e rozd´ıly ve v´ ysledn´ ych hodnot´ach. Mohlo by se zd´at, ˇze by probl´em byl vyˇreˇsen pouˇzit´ım soubor˚ u ve form´atu XML. To by ovˇsem zp˚ usobilo jen dalˇs´ı probl´emy. Hodnoty v XML souborech nejsou totiˇz v˚ ubec zaokrouhlen´e. To zp˚ usob´ı, ˇze ˇc´ıslo, kter´e m˚ uˇzeme vzhledem k pˇresnosti, s jakou prob´ıh´a v´ ypoˇcet v programu a s jakou se poˇc´ıt´a v geod´ezii, povaˇzovat za nulu, bude vyj´adˇreno dvˇema ˇ adky s tˇemito ˇc´ısly program diff samozˇrejmˇe oznaˇc´ı jako zcela odliˇsn´ ymi ˇc´ısly. R´ rozd´ıln´e. D˚ usledkem by opˇet bylo oznaˇcen´ı shodn´ ych v´ ypoˇct˚ u za neshodn´e. I pˇres jmenovan´e nev´ yhody je porovn´an´ı v´ ystupn´ıch soubor˚ u v textov´em form´atu pomoc´ı programu diff u ´ˇcinnou testovac´ı technikou a bylo doned´avna jedin´ ym zp˚ usobem, jak´ ym se prov´adˇelo hromadn´e testov´an´ı programu gama-local . Program diff naˇsel rozd´ıly v mnoha textov´ ych souborech, avˇsak vˇetˇsina tˇechto rozd´ıl˚ u byla d´ana pouze r˚ uzn´ ym z´apisem hodnot (viz v´ yˇse). V nˇekolika pˇr´ıpadech se vˇsak jednalo o skuteˇcn´e rozd´ıly ve v´ ysledc´ıch. N´asleduj´ıc´ı ˇca´st 7.3 tyto rozd´ıly rozeb´ır´a. 21
Pˇr´ıklady lze z´ıskat, stejnˇe jako zdrojov´e k´ody, ze str´anek projektu [14]. Problematick´ ym kon-
figurac´ım se vˇenuje pr´ ace [6].
52
ˇ CVUT Praha
7.3
´ ´I 7 TESTOVAN
Testov´ an´ı pomoc´ı programu gama-local-cmp
Program gama-local-cmp je souˇca´st´ı diplomov´e pr´ace [6]. Z n´ı a tak´e od jej´ıho autora Ing. Gabriela Gy¨oriho jsem ˇcerpal znalosti o funkcionalitˇe programu a zp˚ usobu pouˇzit´ı. Program gama-local-cmp umoˇzn ˇuje porovnat dva XML soubory s v´ ysledky vyrovn´an´ı z programu gama-local . Program porovn´av´a hodnoty naˇcten´e z XML soubor˚ u a urˇcuje jejich rozd´ıly. S pomoc´ı programu gama-local-cmp je moˇzn´e sledovat posuny v geodetick´ ych s´ıt´ı, avˇsak stejnˇe tak je vhodn´ y i k testov´an´ı v´ ysledk˚ u vyrovn´an´ı programem gama-local . Lze jej vyuˇz´ıt ke zjiˇstˇen´ı rozd´ıl˚ u ve v´ ypoˇctu r˚ uzn´ ymi algoritmy nebo r˚ uzn´ ymi verzemi programu gama-local (tak byl pouˇzit i ve zmiˇ novan´e diplomov´e pr´aci [6]). Zde je program pouˇzit pro zjiˇstˇen´ı rozd´ıl˚ u ve vyrovn´an´ı, kdy jednou je vstupem XML soubor a jednou data z datab´aze. Program gama-local-cmp vypisuje vˇsechny rozd´ıly, kter´e jsou vˇetˇs´ı neˇz 1e-09. Samotn´e testov´an´ı prob´ıhalo stejnˇe jako s programem diff (viz 7.2). Jen skript pro bash musel b´ yt ˇc´asteˇcnˇe pozmˇenˇen. Testy uk´azaly jist´e rozd´ıly mezi v´ ysledky vyrovn´an´ı, kde vstupem byl XML soubor, a vyrovn´an´ı, kde vstupem byla datab´aze SQLite. Tyto rozd´ıly byly pouze u prvk˚ u kovarianˇcn´ı matice. V´ ysledn´e souˇradnice a vyrovnan´a mˇeˇren´ı vˇcetnˇe jejich doplˇ nuj´ıc´ıch u ´daj˚ u se ve vˇsech testovan´ ych souborech shodovala. Absolutn´ı hodnoty rozd´ıl˚ u byly ˇr´adu 1e-04 a menˇs´ı. N´asledn´e zv´ yˇsen´ı pˇresnosti v´ ypisu prvk˚ u kovarianˇcn´ı matice pˇri generovan´ı v´ ystupn´ıho XML souboru22 rozd´ıly mezi hodnotami zmenˇsilo tak, ˇze byly nejv´ yˇse ˇr´adu 1e-06, vˇetˇsinou vˇsak 1e-09. Po celou dobu testov´an´ı byla pˇresnost v´ ypis˚ u nastavena na 20 desetinn´ ych m´ıst, coˇz je zbyteˇcnˇe vysok´a hodnota, avˇsak vyluˇcuje chybu pˇri v´ ypisu. Aby byl pˇri dalˇs´ım testov´an´ı pr´ace s datab´az´ı vylouˇcen vliv nov´eho k´odu (tj. tˇr´ıdy SqliteReader), byl pouˇzit program na pˇrevod u ´daj˚ u uloˇzen´ ych v datab´azi SQLite do vstupn´ı d´avky XML. Tento program se pouˇz´ıv´a pouze pro u ´ˇcely testov´an´ı a jmenuje se sql2xml. Testov´an´ı spoˇc´ıvalo v tom, ˇze nejprve se pˇrevedl testovan´a konfigurace uloˇzen´a v XML souboru do SQL d´avky (pˇrevod zajiˇst’uje program gamalocal-xml2sql ). Touto d´avkou byla n´aslednˇe naplnˇena datab´aze. Z datab´aze byla konfigurace pˇrenesena zpˇet do XML programem sql2xml. Obˇe vstupn´ı d´avky byly 22
Zmˇena se t´ yk´ a tˇr´ıdy LocalNetworkXML, metody write v souboru localnetowork.cpp.
53
ˇ CVUT Praha
´ ´I 7 TESTOVAN
postupnˇe vyrovn´any programem gama-local . XML soubory s v´ ysledky vyrovn´an´ı byly porovn´any pomoc´ı programu gama-local-cmp. Porovn´an´ı opˇet uk´azalo rozd´ıly. V dokumentace datab´aze SQLite [12] je uvedeno, byt’ ponˇekud v jin´e souvislosti, ˇze pˇresnost ˇc´ısel s plovouc´ı desetinou ˇc´arkou (REAL) je 15 platn´ ych cifer.23 Tak´e pokusy, kter´e jsem provedl s SQLite C/C++ API a v pˇr´ıkazov´e ˇr´adce SQLite, uk´azaly, ˇze pˇri uloˇzen´ı je ˇc´ıslo zaokrouhleno na 15 platn´ ych cifer. To uk´azalo na to, ˇze rozd´ıly by mohly vznikat pˇri ukl´ad´an´ı hodnot do datab´aze. Zkoum´an´ı jednotliv´ ych krok˚ u pˇri pˇrev´adˇen´ı dat toto tak´e potvrdilo. Pro zjiˇst’ov´an´ı chybn´ ych pˇrevod˚ u jsem vytvoˇril konfiguraci, kde je s´ıt’ tvoˇrena jen tˇremi body (dva jsou zn´am´e, jeden nezn´am´ y). Aby byl vyˇcerp´an poˇcet platn´ ych cifer, tak jsem jako souˇradnice zn´am´ ych bod˚ u jsem zvolil velk´e hodnoty. Zde je uk´azka jednoho bodu. <point id=’1’ x=’600154980.484654321’ y=’100644898.590654321’ fix=’xy’/> ˇ ısla maj´ı 18 platn´ C´ ych cifer. Do datab´aze se vˇsak uloˇz´ı pouze 15, zbytek se ztrat´ı. Tato oˇrezan´a ˇc´ısla se pˇri pˇrevodu uloˇz´ı do XML souboru (uk´azka n´asleduje). <point id=’1’ x=’600154980.484654’ y=’100644898.590654’ fix=’xy’/> V´ ysledky vyrovn´an´ı programem gama-local se kv˚ uli rozd´ıl˚ u ve vstupn´ıch souborech tak´e liˇs´ı. Program gama-local-cmp v tomto konkr´etn´ım pˇr´ıpadˇe zjistil rozd´ıly v ˇra´du 1e-07. Rozd´ıly jsou v souˇradnic´ıch vypoˇcten´eho bodu a samozˇrejmˇe tak´e v souˇradnic´ıch pevn´ ych bod˚ u. Na z´akladˇe v´ yˇse uveden´ ych poznatk˚ u lze rozd´ıly v´ ysledk˚ u vyrovn´an´ı (pˇri ˇcten´ı u ´daj˚ u z XML a pˇri ˇcten´ı u ´daj˚ u z datab´aze) vysvˇetlit t´ım, ˇze pˇri ukl´ad´an´ı ˇc´ısel do datab´aze SQLite doch´az´ı k zaokrouhlen´ı24 . To se n´aslednˇe projev´ı jak pˇri naˇc´ıt´an´ı u ´daj˚ u pomoc´ı tˇr´ıdy SqliteReader, tak i pˇri pouˇzit´ı soubor˚ u z´ıskan´ ych programem sql2xml. 23
K pˇresnosti uloˇzen´ı ˇc´ısel je nutn´e jeˇstˇe poznamenat, ˇze zp˚ usob uloˇzen´ı ˇc´ısel v C++ je z´avisl´ y
na implementaci. Norma C++ [2] pouze zaruˇcuje jist´e minim´aln´ı poˇzadavky, kter´e mus´ı splˇ novat dan´ y typ. 24 Jak uk´ azaly testy, datab´ aze SQLite ˇc´ısla pˇri ukl´ad´an´ı neoˇrez´av´a, ale zaokrouhluje.
54
ˇ CVUT Praha
´ ´I 7 TESTOVAN
Rozd´ıly v souˇradnic´ıch se vˇsak projevily pouze v testovac´ım souboru vytvoˇren´em pro tento u ´ˇcel. Ve skuteˇcn´ ych konfigurac´ıch (ze sb´ırky pˇr´ıklad˚ u projektu GNU Gama) se vˇsak rozd´ıly ve vypoˇcten´ ych hodnot´ach projevily pouze u prvk˚ u kovarianˇcn´ı matice. Rozd´ıly jsou mal´e a tak nejsou v´ ysledky vyrovn´an´ı znehodnoceny. K velikosti rozd´ıl˚ u prvk˚ u kovarianˇcn´ı matice je jeˇstˇe nutn´e poznamenat, ˇze program gama-local-cmp poˇc´ıt´a a posuzuje absolutn´ı rozd´ıl dvou prvk˚ u. Avˇsak hodnoty prvk˚ u v kovarianˇcn´ı matici jsou ˇr´adovˇe r˚ uzn´e, v z´avislosti na dan´e konfiguraci (vˇetˇsinou od 1e-02 do 1e+03). Posuzov´an´ı pˇresnosti t´ım zp˚ usobem, ˇze se prvky odeˇctou a rozd´ıl porovn´a s hodnotou 1e-09, m˚ uˇze v pˇr´ıpadˇe mal´ ych hodnot prvk˚ u v´est k pˇrehl´ednut´ı chyby a v pˇr´ıpadˇe velk´ ych hodnot naopak k oznaˇcen´ı chyby tam, kde nenastala.
55
ˇ CVUT Praha
´ ER ˇ ZAV
Z´ avˇ er C´ılem t´eto bakal´aˇrsk´e pr´ace bylo rozˇs´ıˇrit program gama-local o funkcionalitu, kter´a umoˇzn´ı ˇcten´ı dat pˇr´ımo z datab´aze SQLite. Data pˇreˇcten´a z datab´aze jsou ukl´ad´ana pˇr´ımo do datov´ ych struktur, kter´e program gama-local pouˇzije pro vyrovn´an´ı lok´aln´ı geodetick´e s´ıtˇe. D˚ uleˇzit´e bylo implementovat ˇcten´ı dat z datab´aze na takov´e u ´rovni, aby novou funkcionalitu bylo moˇzn´e zapojit do projektu GNU Gama. Pro ˇcten´ı dat z datab´aze SQLite bylo pouˇzito nativn´ı rozhran´ı (SQLite C/C++ API). Konkr´etnˇe ta ˇca´st rozhran´ı, kde se vyuˇz´ıvaj´ı callback funkce. Rozhran´ı je urˇceno jak pro jazyk C, tak i pro jazyk C++. Jeho pouˇzit´ı m´a jist´a specifika, kter´a plynou z rozd´ıl˚ u obou jazyk˚ u. Tato pr´ace se jim zevrubnˇe vˇenuje. Novou funkcionalitu bylo nutn´e otestovat. Pro u ´ˇcely testov´an´ı bylo vytvoˇreno nˇekolik skript˚ u pro bash, kter´e vyuˇz´ıvaj´ı programy diff a gama-local-cmp. Jako testovac´ı data byla pouˇzita t´emˇeˇr cel´a sb´ırka pˇr´ıklad˚ u, kter´e jsou souˇc´ast´ı projektu GNU Gama. Testy prob´ıhaly tak, ˇze se porovn´avaly v´ ysledky spoˇcten´e na z´akladˇe dat ze souboru XML s v´ ysledky spoˇcten´ ymi na z´akladˇe dat z datab´aze SQLite. Testy uk´azaly, ˇze pˇri naˇc´ıt´an´ı data z datab´aze nedoch´az´ı k ˇza´dn´ ym chyb´am, kter´e by zp˚ usobily rozd´ıly ve vyrovnan´ ych souˇradnic´ıch a vyrovnan´ ych mˇeˇren´ıch. Program gama-local-cmp vˇsak odhalil rozd´ıly mezi prvky kovarianˇcn´ı matice vyrovnan´ ych nezn´am´ ych. Dalˇs´ı testy uk´azaly, ˇze rozd´ıly jsou zp˚ usoben´e ukl´ad´an´ım dat do datab´aze SQLite. Zp˚ usob ukl´adan´ı ˇc´ısel je dan´ y datab´az´ı SQLite a nelze ho tud´ıˇz zmˇenit. Zjiˇstˇen´e rozd´ıly se t´ ykaj´ı smˇerodatn´ ych odchylek vyrovnan´ ych souˇradnic, nikoli souˇradnic samotn´ ych. Tyto rozd´ıly jsou mal´e a neznehodnocuj´ı v´ ysledky vyrovn´an´ı. Vzhledem k tomu, lze povaˇzovat rozˇs´ıˇren´ı programu gama-local za plnˇe funkˇcn´ı. D´ıky moˇznosti ˇcten´ı u ´daj˚ u z datab´aze SQLite maj´ı uˇzivatel´e programu gama-local alternativu k XML souboru. Mohou tak pouˇz´ıt to, co je pro jejich pr´aci v´ yhodnˇejˇs´ı. Jiˇz bˇehem v´ yvoje se uk´azalo, ˇze ˇcten´ı dat pˇr´ımo z datab´aze by pro uˇzivatele mohlo b´ yt velkou v´ yhodou. Dalˇs´ım krokem tedy jistˇe bude rozˇs´ıˇren´ı podpory datab´aze SQLite v programu gama-local o z´apis v´ ysledk˚ u vyrovn´an´ı do datab´aze. Pˇri tom bude v´ yhodn´e pouˇz´ıt opˇet SQLite C/C++ API. Pˇredt´ım vˇsak bude nutn´e navrhnout datab´azov´e sch´ema pro v´ ysledky vyrovn´an´ı, kter´e bude muset b´ yt – stejnˇe jako sch´ema pro vstupn´ı u ´daje – v souladu s potˇrebami projektu QGama. 56
ˇ CVUT Praha
ˇ E ´ ZDROJE POUZIT
Pouˇ zit´ e zdroje [1] STROUSTRUP, Bjarne. The C++ Programming Language – Special Edition. AT&T Labs, Florham Park, New Jersey. United States of America: AddisonWesley, 2000. 1020 s. ISBN 0-201-70073-5. [2] ISO/IEC 14882. INTERNATIONAL STANDARD: Programming languages – C++. 11 West 42nd Street, New York, New York 10036: American National Standards Institute, First edition, 1998-09-01. 748 s. [3] GOUGH, Brian J. An Introduction to GCC – for the GNU Compilers gcc and g++. Foreword by Richard M. Stallman. Network Theory Limited, United Kingdom. Revised August 2005. ISBN 0954161793. URL:
[4] SUTTER, Herb; ALEXANDRESCU, Andrei. 101 programovac´ıch technik. Prvn´ı vyd´an´ı. Brno: Zoner Press, 2005. 232 s. ISBN 80-86815-28-5. ˇ ´ [5] CEPEK, Aleˇs. Informatika: Uvod do C++. Prvn´ı vyd´an´ı. Praha: Nakladatelstv´ı ˇ CVUT, 2004. 265 s. ISBN 80-01-03074-1 ¨ [6] GYORI, Gabriel. Anal´yza a kontrola XML v´ysledk˚ u vyrovnania GNU Gama. ˇ Praha, 2011. 112 s. Diplomov´a pr´ace. CVUT v Praze, Fakulta stavebn´ı. [7] GNU General Public License [online]. Version 3, 29 June 2007. Free Software Foundation, Inc. 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA [cit. 2011-03-12]. URL:
[8] W3C. Extensible Markup Language (XML) [online]. Last modified 2011-03-08. c1996-2003 World Wide Web Consortium MIT/CSAIL in USA [cit. 2011-04-03]. URL: ˇ [9] CEPEK, Aleˇs. Manu´al k programu GNU Gama [online]. 2010-08-22 [cit. 201103-12]. URL: [10] An Introduction To The SQLite C/C++ Interface [online]. Modified 2011-03-28 [cit. 2011-03-30]. URL:
57
ˇ CVUT Praha
ˇ E ´ ZDROJE POUZIT
[11] C/C++ Interface For SQLite Version 3 [online]. Modified 2011-04-17 [cit. 201104-18]. URL: [12] Datatypes In SQLite Version 3 [online]. Modified 2011-04-17 [cit. 2011-04-19]. URL: URL: [13] Qt Development Frameworks. Qt – Cross-platform application and UI framework [online]. c2008-2011 Nokia Corporation [cit. 2011-04-18]. URL: . ˇ [14] CEPEK, Aleˇs. GNU Gama [program]. Edition 1.10, c2009-. URL: ´ [15] NOVAK, Jiˇr´ı. QGama [program]. Last update 2010-10-18. URL:
58
ˇ CVUT Praha
ˇ ıLOH SEZNAM PR´
Seznam pouˇ zit´ ych zkratek SQL
Structured Query Language
XML
Extensible Markup Language
HTML
HyperText Markup Language
PDF
Portable Document Format
API
Application Programming Interface
GCC
GNU Compiler Collection
GPL
General Public License
LGPL
Lesser General Public License
RAII
Resource Acquisition Is Initialization
PIMPL
Private Implementation, Pointer to Implementation
Seznam pˇ r´ıloh Datab´azov´e sch´ema gama-local-schema.sql . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 Dokumentace tˇr´ıdy SqliteReader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 Pouˇzit´a nastaven´ı kompil´atoru GCC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .75
59
A
Datab´ azov´ e sch´ ema gama-local-schema.sql
/* GNU Gama -- adjustment of geodetic networks Copyright ( C ) 2010 Ales Cepek < cepek@gnu . org > , 2010 Jiri Novak < jiri . novak@petriny . net > , 2010 Vaclav Petras < vaclav . petras@fsv . cvut . cz > This file is part of the GNU Gama C ++ library . This library is free software ; you can redistribute it and / or modify it under the terms of the GNU General Public License as published by the Free Software Foundation ; either version 3 of the License , or ( at your option ) any later version . This library is distributed in the hope that it will be useful , but WITHOUT ANY WARRANTY ; without even the implied warranty of M ER CH AN T AB IL IT Y or FITNESS FOR A PARTICULAR PURPOSE .
See the
GNU General Public License for more details . You should have received a copy of the GNU General Public License along with this library ; if not , write to the Free Software Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA
02110 -1301
$
*/
create table g n u _ g a m a _ l o c a l _ c o n f i g u r a t i o n s ( conf_id
integer primary key ,
conf_name varchar (60) not null unique , sigma_apr double precision default 10.0 not null check ( sigma_apr > 0) , conf_pr
double precision
tol_abs
double precision default 1000 not null check ( tol_abs > 0) ,
default 0.95 not null check ( conf_pr > 0 and conf_pr <1) , sigma_act varchar (11) default ’ aposteriori ’ not null check ( sigma_act in ( ’ apriori ’ , ’ aposteriori ’)) , update_cc varchar (3) default ’no ’ not null check ( update_cc in ( ’ yes ’ , ’no ’)) , axes_xy
varchar (2) default ’ne ’ not null check ( axes_xy in ( ’ ne ’ , ’sw ’ , ’es ’ , ’wn ’ , ’en ’ , ’nw ’ , ’se ’ , ’ws ’)) ,
angles
varchar (12) default ’ right - handed ’ not null check ( angles in ( ’ left - handed ’ , ’ right - handed ’)) ,
epoch
double precision default 0.0 not null ,
algorithm varchar (12) default ’svd ’ not null check ( algorithm in ( ’ svd ’ , ’gso ’ , ’ cholesky ’ , ’sm - env ’)) , ang_units int default 400 not null check ( ang_units in (400 , 360)) , latitude
double precision default 50 not null ,
ellipsoid varchar (20)
60
); create table g n u _ g a m a _ l o c a l _ d e s c r i p t i o n s ( conf_id
integer references g nu_ gam a_l oca l_ con fig ura tio ns ,
indx
integer check ( indx >= 1) ,
text
varchar (1000) not null ,
primary key ( conf_id , indx ) ); create table g n u _ g a m a _ l o c a l _ p o i n t s ( conf_id
integer references g nu_ gam a_l oca l_ con fig ura tio ns ,
id
varchar (80) ,
x
double precision ,
y
double precision ,
z
double precision ,
txy
varchar (11) check ( txy in ( ’ fixed ’ , ’ adjusted ’ , ’ constrained ’)) ,
tz
varchar (11) check ( tz
in ( ’ fixed ’ , ’ adjusted ’ , ’ constrained ’)) ,
primary key ( conf_id , id ) ); create table g n u _ g a m a _ l o c a l _ c l u s t e r s ( conf_id
integer references g nu_ gam a_l oca l_ con fig ura tio ns ,
ccluster
integer check ( ccluster > 0) ,
dim
integer not null check ( dim > 0) ,
band
integer not null ,
tag
varchar (18) not null check ( tag in ( ’ obs ’ , ’ coordinates ’ , ’ vectors ’ , ’ height - differences ’)) ,
check ( band between 0 and dim -1) , primary key ( conf_id , ccluster ) ); -- upper triangular variance - covariance band - matrix (0 <= bandwidth < dim ) create table g n u _ g a m a _ l o c a l _ c o v m a t ( conf_id
integer ,
ccluster
integer ,
rind
integer check ( rind > 0) ,
cind
integer check ( cind > 0) ,
val
double precision not null ,
foreign key ( conf_id , ccluster ) references gnu_gama_local_clusters , primary key ( conf_id , ccluster , rind , cind ) ); create table g n u _ g a m a _ l o c a l _ o b s ( conf_id
integer ,
ccluster
integer ,
indx
integer check ( indx > 0) ,
tag
varchar (10) check ( tag in
61
( ’ direction ’ , ’ distance ’ , ’ angle ’ , ’s - distance ’ , ’z - angle ’ , ’dh ’)) , from_id
varchar (80) not null ,
to_id
varchar (80) not null ,
to_id2
varchar (80) ,
val
double precision not null ,
stdev
double precision ,
from_dh
double precision ,
to_dh
double precision ,
to_dh2
double precision ,
dist
double precision , -- dh dist
rejected
integer default 0 not null ,
primary key ( conf_id , ccluster , indx ) , foreign key ( conf_id , ccluster ) references gnu_gama_local_clusters , check ( tag <> ’ angle ’ or to_id2 is not null ) , check ( tag = ’dh ’ or ( tag <> ’dh ’ and dist is null )) ); create table g n u _ g a m a _ l o c a l _ c o o r d i n a t e s ( conf_id
integer ,
ccluster
integer check ( ccluster > 0) ,
indx
integer check ( indx > 0) ,
id
varchar (80) ,
x
double precision ,
y
double precision ,
z
double precision ,
rejected
integer default 0 not null ,
foreign key ( conf_id , ccluster ) references gnu_gama_local_clusters , primary key ( conf_id , ccluster , indx ) ); create table g n u _ g a m a _ l o c a l _ v e c t o r s ( conf_id
integer ,
ccluster
integer check ( ccluster > 0) ,
indx
integer check ( indx > 0) ,
from_id
varchar (80) ,
to_id
varchar (80) ,
dx
double precision ,
dy
double precision ,
dz
double precision ,
from_dh
double precision ,
to_dh
double precision ,
rejected
integer default 0 not null ,
foreign key ( conf_id , ccluster ) references gnu_gama_local_clusters , primary key ( conf_id , ccluster , indx ) );
62
B
Dokumentace tˇ r´ıdy SqliteReader
Dokumentace tˇr´ıdy SqliteReader je v dokumentaˇcn´ıch koment´aˇr´ıch ve zdrojov´ ych k´odech, zdokumentov´ano bylo rozhran´ı i implementace. Dokumentace je generov´ana pomoc´ı n´astroje Doxygen, kter´ y z dokumentaˇcn´ıch koment´aˇr˚ u generuje dokumentaci (referenˇcn´ı manu´al) v nˇekolika form´atech (HTML, PDF, XML, . . . ). Dokumentaˇcn´ımi koment´aˇri se rozum´ı speci´alnˇe oznaˇcen´e bˇeˇzn´e koment´aˇre, kter´e obsahuj´ı nav´ıc jeˇstˇe form´atovac´ı a jin´e znaˇcky. Tato pˇr´ıloha obsahuje zkr´acenou verzi dokumentace ve form´atu PDF. Vynech´any byly napˇr´ıklad callback funkce, jejich jm´ena vˇsak dostateˇcnˇe vypov´ıdaj´ı o jejich u ´ˇcelu. Nav´ıc jsou callback funkce pops´any v hlavn´ım textu.
63
4 Namespace Documentation
4
2
Namespace Documentation
4.1
anonymous_namespace{sqlitereader.cpp} Namespace Reference
Functions • void exec (sqlite3 ∗sqlite3Handle, const std::string &query, SqliteReaderCallbackType callback, ReaderData ∗readerData) • double ToDouble (const char ∗s, const std::string &m=T_gamalite_conversion_to_double_failed) • int ToInteger (const char ∗s, const std::string &m=T_gamalite_conversion_to_integer_failed) Variables • • • • • • •
const char ∗ T_gamalite_database_not_open const char ∗ T_gamalite_invalid_column_value const char ∗ T_gamalite_conversion_to_double_failed const char ∗ T_gamalite_conversion_to_integer_failed const char ∗ T_gamalite_unknown_exception_in_callback const char ∗ T_gamalite_stand_point_cluster_with_multi_dir_sets const char ∗ T_gamalite_configuration_not_found
4.1.1 4.1.1.1
Function Documentation void anonymous_namespace{sqlitereader.cpp}::exec (sqlite3 ∗ sqlite3Handle, const std::string & query, SqliteReaderCallbackType callback, ReaderData ∗ readerData)
A C++ wrapper around sqlite3_exec function. For internal use only. Connection to database has to be open. If the callback is NULL pointer, then no callback function is called (result rows are ignored). If callback requests query execution abort (by returning non-zero value), no other callbacks is called and exception is thrown (sqlite3_exec returns a value which differs from SQLITE_OK, see also ). If callback stores pointer to an exception to GNU_gama::local::sqlite_db::ReaderData::exception (p. 9) and callback requests query execution abort, exception will be rethrown. If GNU_gama::local::sqlite_db::ReaderData::exception (p. 9) is NULL pointer, GNU_gama::Exception::sqlitexc (p. 11) will be thrown with SQLite error message. Parameters sqlite3Handle pointer to struct sqlite3 query query string callback pointer to callback function readerData pointer to struct ReaderData Exceptions GNU_gama::Exception::sqlitexc (p. 11) if error occurs when reading from database Generated on Sat Apr 23 20:58:43 2011 for GNU Gama: SqliteReader by Doxygen
4.1
anonymous_namespace{sqlitereader.cpp} Namespace Reference
3
GNU_gama::Exception::base if is error occurred by something another -- it depends on callback It can also throw any other exception derived from this class. Callback functions are expected to handle exceptions like this: \\ ... try block catch (GNU_gama::Exception::base& e) { d->exception = e.clone(); } catch (std::exception& e) { d->exception = new GNU_gama::Exception::string(e.what()); } catch (...) { d->exception = new GNU_gama::Exception::string("unknown"); } return 1;
See also GNU_gama::local::sqlite_db::ReaderData (p. 6), SqliteReaderCallbackType (p. 14), GNU_gama::Exception::sqlitexc (p. 11) Referenced by GNU_gama::local::sqlite_db::SqliteReader::retrieve(), and sqlite_db_readClusters(). 4.1.1.2
double anonymous_namespace{sqlitereader.cpp}::ToDouble (const char ∗ s, const std::string & m = T_gamalite_conversion_to_double_failed)
Converts string to double. Parameter s can not be NULL pointer. If conversion fails, exception is thrown. Parameters s string to convert m error message if conversion fails Exceptions GNU_gama::Exception::sqlitexc (p. 11) Referenced by sqlite_db_readConfigurationInfo(), sqlite_db_readCoordinates(), sqlite_db_readCovarianceMatrix(), sqlite_db_readHeightDifferences(), sqlite_db_readObservations(), sqlite_db_readPoints(), and sqlite_db_readVectors(). 4.1.1.3
int anonymous_namespace{sqlitereader.cpp}::ToInteger (const char ∗ s, const std::string & m = T_gamalite_conversion_to_integer_failed)
Converts string to integer. Parameter s can not be NULL pointer. If conversion fails, exception is thrown. Generated on Sat Apr 23 20:58:43 2011 for GNU Gama: SqliteReader by Doxygen
4.1
anonymous_namespace{sqlitereader.cpp} Namespace Reference
4
Parameters s string to convert m error message if conversion fails Exceptions GNU_gama::Exception::sqlitexc (p. 11) Referenced by sqlite_db_readClusters(), sqlite_db_readCoordinates(), sqlite_db_readCovarianceMatrix(), sqlite_db_readObservations(), and sqlite_db_readVectors(). 4.1.2 4.1.2.1
Variable Documentation const char∗ anonymous_namespace{sqlitereader.cpp}::T_gamalite_configuration_not_found Initial value:
"configuration not found"
error message, used in GNU_gama::local::sqlite_db::SqliteReader::retrieve (p. 11) Referenced by GNU_gama::local::sqlite_db::SqliteReader::retrieve(). 4.1.2.2
const char∗ anonymous_namespace{sqlitereader.cpp}::T_gamalite_conversion_to_double_failed Initial value:
"conversion to double failed"
error message, used in conversion function when no better message can be used 4.1.2.3
const char∗ anonymous_namespace{sqlitereader.cpp}::T_gamalite_conversion_to_integer_failed Initial value:
"conversion to integer failed"
error message, used in conversion function when no better message can be used 4.1.2.4
const char∗ anonymous_namespace{sqlitereader.cpp}::T_gamalite_database_not_open Initial value:
"database not open"
error message, used in GNU_gama::local::sqlite_db::SqliteReader::SqliteReader (p. 10) Referenced by GNU_gama::local::sqlite_db::SqliteReader::SqliteReader(). Generated on Sat Apr 23 20:58:43 2011 for GNU Gama: SqliteReader by Doxygen
4.2
GNU_gama Namespace Reference
4.1.2.5
5
const char∗ anonymous_namespace{sqlitereader.cpp}::T_gamalite_invalid_column_value Initial value:
"invalid column value"
error message, used in callbacks’ to indicate bad value of database field Referenced by sqlite_db_readClusters(), sqlite_db_readConfigurationInfo(), sqlite_db_readConfigurationText(), sqlite_db_readCoordinates(), sqlite_db_readCovarianceMatrix(), sqlite_db_readHeightDifferences(), sqlite_db_readObservations(), sqlite_db_readPoints(), and sqlite_db_readVectors(). 4.1.2.6
const char∗ anonymous_namespace{sqlitereader.cpp}::T_gamalite_stand_point_cluster_with_multi_dir_sets Initial value:
"StandPoint cluster with multiple directions sets"
error message, used in sqlite_db_readObservations (p. 17) Referenced by sqlite_db_readObservations(). 4.1.2.7
const char∗ anonymous_namespace{sqlitereader.cpp}::T_gamalite_unknown_exception_in_callback Initial value:
"unknown exception in SqliteReader’s callback"
error message, used in callbacks’ catch(...) Referenced by sqlite_db_readClusters(), sqlite_db_readConfigurationInfo(), sqlite_db_readConfigurationText(), sqlite_db_readCoordinates(), sqlite_db_readCovarianceMatrix(), sqlite_db_readHeightDifferences(), sqlite_db_readObservations(), sqlite_db_readPoints(), and sqlite_db_readVectors().
4.2
GNU_gama Namespace Reference
Namespaces • namespace Exception • namespace local
4.3
GNU_gama::Exception Namespace Reference
Classes • class sqlitexc Exception (p. 5) class for GNU_gama::local::sqlite_db::SqliteReader (p. 9).
Generated on Sat Apr 23 20:58:43 2011 for GNU Gama: SqliteReader by Doxygen
4.4
GNU_gama::local Namespace Reference
4.4
GNU_gama::local Namespace Reference
Namespaces • namespace sqlite_db
4.5
GNU_gama::local::sqlite_db Namespace Reference
Classes • struct ReaderData GNU_gama::local::sqlite_db::SqliteReader (p. 9) class private data
• class SqliteReader Reads LocalNetwork from SQLite 3 database.
5
Class Documentation
5.1
GNU_gama::local::sqlite_db::ReaderData Struct Reference
GNU_gama::local::sqlite_db::SqliteReader (p. 9) class private data Public Member Functions • ReaderData () Public Attributes • • • • • • • • • • • • •
GNU_gama::local::LocalNetwork ∗ lnet std::string algorithm bool correction_to_ellipsoid double latitude GNU_gama::Ellipsoid ellipsoid GNU_gama::Exception::base ∗ exception sqlite3 ∗ sqlite3Handle std::string configurationId GNU_gama::local::StandPoint ∗ currentStandPoint GNU_gama::local::Vectors ∗ currentVectors GNU_gama::local::Coordinates ∗ currentCoordinates GNU_gama::local::HeightDifferences ∗ currentHeightDifferences GNU_gama::local::CovMat ∗ currentCovarianceMatrix
Private Member Functions • ReaderData (const ReaderData &) • ReaderData & operator= (const ReaderData &)
Generated on Sat Apr 23 20:58:43 2011 for GNU Gama: SqliteReader by Doxygen
6
5.1
GNU_gama::local::sqlite_db::ReaderData Struct Reference
5.1.1
7
Detailed Description
GNU_gama::local::sqlite_db::SqliteReader (p. 9) class private data For internal use only. Contains all private data. In file sqlitereader.h (p. 18) is forward declaration of this struct. But declaration is only available in this file (translation unit). All members are public. Functions especially (extern "C") callbacks can easy manipulate with this members. This is no OOP violation because we can think about functions in this file as ReaderData (p. 6) member functions. Functions outside this file can’t access this structure because they know only forward declaration and GNU_gama::local::sqlite_db::SqliteReader (p. 9) has declared pointer to this struct private of course. However, there are some problems with callbacks visibility (see SqliteReaderCallbackType (p. 14) or sqlite_db_readConfigurationInfo (p. 15) for details). Callbacks sqlite_db_readObservations (p. 17), ... and sqlite_db_readCovarianceMatrix (p. 17) have to share data between it’s invocations. So they needs access to the same StandPoint, Vectors, etc. They also needs access to exception (p. 9). This is the reason why ReaderData (p. 6) contains pointer to StandPoint etc. Pointers currentStandPoint (p. 8), currentVectors (p. 8), currentCoordinates (p. 8), currentHeightDifferences (p. 8) and currentCovarianceMatrix (p. 8) temporary points to objects which are in use at the moment by the exec() (p. 2) caller and corresponding callback invocations. When processing of one object is finished, this pointer should by set to NULL pointer. 5.1.2 5.1.2.1
Constructor & Destructor Documentation GNU_gama::local::sqlite_db::ReaderData::ReaderData () [inline]
It sets all member variables. Pointers are set to NULL pointers. Strings are initialised by "" to satisfied GCC -Weffc++ warning options. References ellipsoid. 5.1.2.2
GNU_gama::local::sqlite_db::ReaderData::ReaderData (const ReaderData &) [private] disabled copy constructor
5.1.3 5.1.3.1
Member Function Documentation ReaderData& GNU_gama::local::sqlite_db::ReaderData::operator= (const ReaderData &) [private] disabled assignment operator
5.1.4 5.1.4.1
Member Data Documentation std::string GNU_gama::local::sqlite_db::ReaderData::algorithm
Referenced by sqlite_db_readConfigurationInfo().
Generated on Sat Apr 23 20:58:43 2011 for GNU Gama: SqliteReader by Doxygen
5.1
GNU_gama::local::sqlite_db::ReaderData Struct Reference
5.1.4.2
8
std::string GNU_gama::local::sqlite_db::ReaderData::configurationId
configuration id in database Referenced by GNU_gama::local::sqlite_db::SqliteReader::retrieve(), sqlite_db_readClusters(), and sqlite_db_readConfigurationInfo(). 5.1.4.3
bool GNU_gama::local::sqlite_db::ReaderData::correction_to_ellipsoid
Referenced by sqlite_db_readConfigurationInfo(). 5.1.4.4
GNU_gama::local::Coordinates∗ GNU_gama::local::sqlite_db::ReaderData::currentCoordinates
Referenced by sqlite_db_readClusters(), and sqlite_db_readCoordinates(). 5.1.4.5
GNU_gama::local::CovMat∗ GNU_gama::local::sqlite_db::ReaderData::currentCovarianceMatrix
provides access to same covariance matrix for callback readCovarianceMatrix and caller of exec() (p. 2) function Referenced by sqlite_db_readClusters(), and sqlite_db_readCovarianceMatrix(). 5.1.4.6
GNU_gama::local::HeightDifferences∗ GNU_gama::local::sqlite_db::ReaderData::currentHeightDifferences
Referenced by sqlite_db_readClusters(), and sqlite_db_readHeightDifferences(). 5.1.4.7
GNU_gama::local::StandPoint∗ GNU_gama::local::sqlite_db::ReaderData::currentStandPoint
provides access to same stand point for callback readObservations and caller of exec() (p. 2) function Referenced by sqlite_db_readClusters(), and sqlite_db_readObservations(). 5.1.4.8
GNU_gama::local::Vectors∗ GNU_gama::local::sqlite_db::ReaderData::currentVectors
Referenced by sqlite_db_readClusters(), and sqlite_db_readVectors(). 5.1.4.9
GNU_gama::Ellipsoid GNU_gama::local::sqlite_db::ReaderData::ellipsoid
Generated on Sat Apr 23 20:58:43 2011 for GNU Gama: SqliteReader by Doxygen
5.2
GNU_gama::local::sqlite_db::SqliteReader Class Reference
9
Referenced by ReaderData(), and sqlite_db_readConfigurationInfo(). 5.1.4.10
GNU_gama::Exception::base∗ GNU_gama::local::sqlite_db::ReaderData::exception
an exception which was caught in callback or NULL if no exception was thrown Referenced by sqlite_db_readClusters(), sqlite_db_readConfigurationInfo(), sqlite_db_readConfigurationText(), sqlite_db_readCoordinates(), sqlite_db_readCovarianceMatrix(), sqlite_db_readHeightDifferences(), sqlite_db_readObservations(), sqlite_db_readPoints(), sqlite_db_readVectors(), and GNU_gama::local::sqlite_db::SqliteReader::∼SqliteReader(). 5.1.4.11
double GNU_gama::local::sqlite_db::ReaderData::latitude
Referenced by sqlite_db_readConfigurationInfo(). 5.1.4.12
GNU_gama::local::LocalNetwork∗ GNU_gama::local::sqlite_db::ReaderData::lnet
pointer to network object Referenced by GNU_gama::local::sqlite_db::SqliteReader::retrieve(), sqlite_db_readClusters(), sqlite_db_readConfigurationInfo(), sqlite_db_readConfigurationText(), and sqlite_db_readPoints(). 5.1.4.13
sqlite3∗ GNU_gama::local::sqlite_db::ReaderData::sqlite3Handle
pointer to struct sqlite3 Referenced by GNU_gama::local::sqlite_db::SqliteReader::retrieve(), GNU_gama::local::sqlite_db::SqliteReader::SqliteReader(), and db::SqliteReader::∼SqliteReader().
sqlite_db_readClusters(), GNU_gama::local::sqlite_-
The documentation for this struct was generated from the following file: • sqlitereader.cpp
5.2
GNU_gama::local::sqlite_db::SqliteReader Class Reference
Reads LocalNetwork from SQLite 3 database. #include <sqlitereader.h> Public Member Functions • SqliteReader (const std::string &fileName) • ∼SqliteReader () • void retrieve (LocalNetwork ∗&lnet, const std::string &configuration)
Generated on Sat Apr 23 20:58:43 2011 for GNU Gama: SqliteReader by Doxygen
5.2
GNU_gama::local::sqlite_db::SqliteReader Class Reference
10
Private Member Functions • SqliteReader (const SqliteReader &) • SqliteReader & operator= (const SqliteReader &) Private Attributes • ReaderData ∗ readerData 5.2.1
Detailed Description
Reads LocalNetwork from SQLite 3 database. 5.2.2 5.2.2.1
Constructor & Destructor Documentation SqliteReader::SqliteReader (const std::string & fileName)
Opens a database connection. Parameters fileName name of database file References readerData, GNU_gama::local::sqlite_db::ReaderData::sqlite3Handle, and anonymous_namespace{sqlitereader.cpp}::T_gamalite_database_not_open. 5.2.2.2
SqliteReader::∼SqliteReader ()
Closes a database connection. If function sqlite3_close returns another value then SQLITE_OK (there were some error), no action is performed. References GNU_gama::local::sqlite_db::ReaderData::exception, gama::local::sqlite_db::ReaderData::sqlite3Handle. 5.2.2.3
readerData,
and
GNU_-
GNU_gama::local::sqlite_db::SqliteReader::SqliteReader (const SqliteReader &) [private] disabled copy constructor
5.2.3 5.2.3.1
Member Function Documentation SqliteReader& GNU_gama::local::sqlite_db::SqliteReader::operator= (const SqliteReader &) [private] disabled assignment operator
Generated on Sat Apr 23 20:58:43 2011 for GNU Gama: SqliteReader by Doxygen
5.3
GNU_gama::Exception::sqlitexc Class Reference
5.2.3.2
11
void SqliteReader::retrieve (LocalNetwork ∗& lnet, const std::string & configuration)
Reads configuration configuration from database. If lnet is a NULL pointer, new LocalNetwork is created. Type of network depends on algorithm fetched from database. Exceptions GNU_gama::Exception::sqlitexc (p. 11) References GNU_gama::local::sqlite_db::ReaderData::configurationId, anonymous_namespace{sqlitereader.cpp}::exec(), GNU_gama::local::sqlite_db::ReaderData::lnet, readerData, GNU_gama::local::sqlite_db::ReaderData::sqlite3Handle, sqlite_db_readClusters(), sqlite_db_readConfigurationInfo(), sqlite_db_readConfigurationText(), sqlite_db_readPoints(), and anonymous_namespace{sqlitereader.cpp}::T_gamalite_configuration_not_found. 5.2.4
Member Data Documentation
5.2.4.1
ReaderData∗ GNU_gama::local::sqlite_db::SqliteReader::readerData [private]
pointer to private data Referenced by retrieve(), SqliteReader(), and ∼SqliteReader().
The documentation for this class was generated from the following files: • sqlitereader.h • sqlitereader.cpp
5.3
GNU_gama::Exception::sqlitexc Class Reference
Exception (p. 5) class for GNU_gama::local::sqlite_db::SqliteReader (p. 9). #include <sqlitereader.h> Public Member Functions • sqlitexc (const std::string &message) • virtual sqlitexc ∗ clone () const • virtual void raise () const 5.3.1
Detailed Description
Exception (p. 5) class for GNU_gama::local::sqlite_db::SqliteReader (p. 9).
Generated on Sat Apr 23 20:58:43 2011 for GNU Gama: SqliteReader by Doxygen
6 File Documentation
5.3.2
12
Constructor & Destructor Documentation
5.3.2.1
GNU_gama::Exception::sqlitexc::sqlitexc (const std::string & message) [inline]
Parameters message sqlite database error message or SqliteReader message Referenced by clone(). 5.3.3
Member Function Documentation
5.3.3.1
virtual sqlitexc∗ GNU_gama::Exception::sqlitexc::clone () const [inline, virtual] Clones an exception.
For internal use only. The way as it is used in callback functions: // ... try block catch (GNU_gama::Exception::base& e) { d->exception = e.clone(); } return 1;
References sqlitexc(). 5.3.3.2
virtual void GNU_gama::Exception::sqlitexc::raise () const [inline, virtual] Rethrows an exception polymorphically.
For internal use only. The way as it is used in function exec in file sqlitereader.cpp (p. 12): if (readerData->exception != 0) { readerData->exception->raise(); }
The documentation for this class was generated from the following file: • sqlitereader.h
6 6.1
File Documentation sqlitereader.cpp File Reference
Implementation of GNU_gama::local::sqlite_db::SqliteReader (p. 9). Generated on Sat Apr 23 20:58:43 2011 for GNU Gama: SqliteReader by Doxygen
C
Pouˇ zit´ a nastaven´ı kompil´ atoru GCC
Zde jsou uvedeny parametry urˇcuj´ıc´ı, v jak´ ych chv´ıl´ıch bude kompil´ator GCC hl´asit varov´an´ı. Parametry uveden´e v n´asleduj´ıc´ım seznamu byly pouˇzity pˇri v´ yvoji tˇr´ıdy SqliteReader. -ansi -Wall -pedantic -Wextra -Weffc++ -Wconversion -Wsign-conversion -Wfloat-equal -Wno-div-by-zero -Wmissing-declarations -Wlogical-op -Wabi -Wold-style-cast -Woverloaded-virtual -Wshadow -Wcast-qual -Wwrite-strings -Wredundant-decls -fno-nonansi-builtins -Wctor-dtor-privacy
75