Tik-76.115 Tekninen määrittely - Tcl/Tk-liittymä

Internet-TV

http://www.niksula.cs.hut.fi/project/ohtace/documents/te_tcl.html
CVS info: $Id: te_tcl.html,v 1.6 1996/11/25 13:02:35 tsalste Exp $


Yleistä

Tämä dokumentti on osa teknistä määrittelyä.

Tcl- ja C++ -ohjelmat voivat viestiä toisilleen kahteen suuntaan: C++ -ohjelmaa voi käyttää Tcl:stä käsin, ja Tcl-ohjelmaa voi puolestaan käyttää C++:sta käsin. Lisäksi Tcl-koodi voidaan kääntää yhtaikaa C++ -koodin kanssa siten, että molemmat sisältyvät samaan binääritiedostoon.

Tcl/Tk-liittymä (vic ja vat)

Seuraavassa käsitellään ohjelmien vic ja vat käyttämää Tcl/Tk-liittymää.

Ohjelmissa on luotu erityisiä luokkia Tcl-koodin kutsumiseen C++ -koodista käsin. Lisäksi niissä on välineitä myös C++ -funktioiden esittelyyn, jotta niitä voitaisiin kutsua Tcl-ohjelmasta.

Tiedostot

Liittymä koostuu 4 C++ -tiedostosta, jotka ovat:
  1. Tcl.h - Luokkien esittely
  2. Tcl.cc - Luokkien toteutus
  3. Tcl2.cc - Kaksi Tcl-luokan metodia omassa tiedostossaan (evalf ja resultf) DEC OSF:n C++ -kääntäjän optimointivirheen takia
  4. tcl2c++.c - Lyhyt ohjelma, joka kääntää Tcl-ohjelmatiedostoja C++ -määrittelyiksi
Koodi on ainakin näiden tiedostojen osalta erittäin huonosti kommentoitua ja melko sekavaa. Mm. luokkamäärittelyjä on sikin sokin sekä .cc- että .h-tiedostoissa. Tästä seuraa lähinnä työmäärän kasvua koodin selvittelyssä sekä lisääntyneitä virhemahdollisuuksia, kun ei tarkalleen tiedetä, mitä koodin tekijänsä mukaan pitäisi tehdä. Virheiden vähentämiseksi koodiin onkin tutustuttava erityisen huolellisesti.

Luokat

Liittymä koostuu seuraavista luokista:

Luokka Äitiluokka Tarkoitus Tiedostot
Tcl Tcl-koodin suoritus C++ -ohjelmasta käsin ja päinvastoin Tcl.h, Tcl.cc, Tcl2.cc
TclObject C++ -olioiden käsittely Tcl-ohjelmasta Tcl.h, Tcl.cc
CreateCommand TclObject C++/Tcl-olion luonti Tcl-koodista käsin käskyllä new Tcl.cc
DeleteCommand TclObject CreateCommandin avulla luodun olion tuhoaminen käskyllä delete Tcl.cc
Matcher CreateCommandin apuluokka Tcl.h, Tcl.cc
EmbeddedTcl Tcl-tiedostojen upotus C++ -ohjelmaan Tcl.h, Tcl.cc

Luokka Tcl

Tcl luokka on perusluokka, jota käytetään C++ - Tcl -linkin tekoon.

Tcl alustetaan luokkametodilla init, joka käynnistää yhden Tcl-tulkin. Kun Tcl:stä luodaan instansseja, ne käyttävät tätä samaa Tcl-tulkkia.

Metodi Toiminta Paluuarvo
static void init(const char* application) Käynnistää Tcl-tulkin ja asettaa ohjelman nimen.
void eval(char *s) Suorittaa muuttujassa s olevan Tcl-koodin Tcl-koodin palauttama merkkijono on saatavissa funktiolla result. Virhetilanteessa suorittaa Tk-käskyn tkerror "virheilmoitus"
void evalc(const char *s) Kuten eval, mutta säilyttää koodin koskemattomana
void eval() Kuten eval, mutta suorittaa seuraavan käskyn bufferista. evalf käyttää tätä.
char* result() Viimeksi suoritetun Tcl-funktion paluumerkkijonon haku Palauttaa osoittimen merkkijonoon
void evalf(const char *fmt, ...) Kuten eval, mutta syntaksi kuten printf:ssä. Käyttö ilmeisesti näin:
evalf("%s %s", kasky, param)
Paluuarvo luetaan metodilla resultf
void resultf(const char *fmt, ...) Metodin evalf paluuarvon luku, käyttö vähän hämärä Paluuarvo bufferissa
void EvalFile(const char *file) Tcl-ohjelmatiedoston suoritus Virhetilanteessa kirjoittaa virheilmoituksen stderriin ja lopettaa koko ohjelman, muutoin ei palauta mitään
char* var(const char* varname, int flags = TCL_GLOBAL_ONLY) Tcl-muuttujan arvon hakeminen Osoitin muuttujan arvoon (merkkijono)
Seuraavia metodeja käytetään, kun halutaan kutsua C++ -funktiota Tcl:stä
void CreateCommand(Tcl_CmdProc* cproc, ClientData cd=0, Tcl_CdmDeleteProc* dproc=0) C++ -funktion esittely Tcl-ohjelmassa käytettäväksi
void result(const char* p) Asettaa C++ -funktion paluumerkkijonoksi merkkijonon p

Luokka TclObject

Tämä luokka tarjoaa peruspalvelut C++ -funktioiden kutsumiseen Tcl:stä käsin - eli siis Tcl-kielen laajentamisen uusilla käskyillä, jotka toteutetaan C++:lla. Lähestymistapana on olio-ohjelmointi. TclObjectin avulla C++ -olioita voidaan linkittää Tcl:ään siten, että Tcl:stä voidaan kutsua C++ -olion metodeja. Tcl-koodin tasolla tämä tapahtuu jotakuinkin näin: olio komento parametrit. Tämä kutsu välitetään C++ -olion metodille command, jolle annetaan parametreina komento ja parametrit merkkijonomuodossa.

TclObject on virtuaalinen kantaluokka, josta johdetaan muota luokkia.

vicissä TclObjectista on johdettu seuraavat luokat: ColorHist, ColorModel, Crypt, InputDevice, OutputDevice, Grabber, Module, Network, PacketHandler, Source, SourceManager, Transmitter ja BareWindow. Näistä on johdettu edelleen aliluokkia.

TclObject ylläpitää luokan sisäistä listaa kaikista määritellyistä olioista.

TclObjectin käyttö

TclObjectista siis johdetaan aliluokkia, joissa toteutetaan itse komennot. Aliluokista synnytetään yleensä vain yksi instanssi, vaikka mitään rajoitusta määrälle ei ole.

Käytön kulku: Luodaan olio ja annetaan sille nimi konstruktorilla TclObject::TclObject(name). name voi olla myös NULL, jolloin nimi tulee automaattisesti muotoon _o4321.

Kun kaikki oliot on luotu, kutsutaan luokkametodia TclObject::define(), joka luo ilmoittaa Tcl-tulkille uudet komennot. Tcl-komennon nimeksi tulee yllä mainittu name, joka on siis oliota vastaava Tcl-komennon nimi. Tcl-komento on muotoa name command param, jota C++:n syntaksissa vastaisi jotakuinkin name.command(param).

Tcl-komentojen toteuttamiseksi aliluokkaan määritellään command(argc, argv)-metodi, jolla Tcl:stä tulevat parametrit tulkitaan ja toteutetaan. Metodin parametreina tulee C:n käytännön mukaisesti argc, joka kertoo argv:n koon. argv on merkkijonotaulu, jonka sisältö on seuraava:
argv[0] Olion nimi (tarvitaan vain virheilmoituksia näytettäessä)
argv[1] Komennon nimi
argv[2]... Parametrit

Metodi Toiminta Paluuarvo
static void define() Käy läpi kaikki tähän mennessä luodut TclObject-luokan (aliluokan) oliot ja luo niitä vastaavan Tcl-komennon Tcl-tulkkiin. defineä voidaan kutsua haluttaessa kuinka monta kertaa tahansa Tcl-komentojen päivittämiseen, jolloin uudet määrittelyt korvaavat vanhat.
void class_name(const char* s) Asettaa oliolle luokkanimen
static TclObject* lookup(const char* name) Hakee luokansisäisestä listasta olion nimen mukaan Osoitin löydettyyn olioon kai NULL
static int callback(...) Kun Tcl kutsuu määriteltyä uutta funktiota, kutsu ohjautuu ensimmäisenä tähän luokkafunktioon. callback ei tee muuta kuin kutsuu funktiota vastaavaa aliluokan commandia. TCL_OK, TCL_ERROR
virtual int command(int argc, const char*const* argv) Tänne ohjautuvat kaikki Tcl:stä tulevat kutsut. Tämä on virtuaalifunktio, jonka jokainen aliluokka saa toteuttaa omalla tavallaan. Kantaluokan TclObject::commandia kutsutaan vain silloin, jos aliluokan command on saanut virheellisiä parametrejä. Tällöin TclObject::command näyttää sopivan virheilmoituksen. TCL_ERROR

Luokka CreateCommand

CreateCommand-luokka luo Tcl-tulkkiin uuden käskyn new. New-käskyllä voidaan Tcl-koodista käsin luoda uusia olioita yhtä aikaa sekä C++ -ohjelmaan että Tcl-ohjelmaan. Systeemin toiminta on hyvin monivaiheinen, ja se seljinnee parhaiten esimerkin avulla.

Esimerkki:

set transcoder [new transcoder jpeg/dct]

Tämä kaunis Tcl-koodinpätkä luo uuden JpegTranscoder-luokan olion C++ -ohjelmaan. Samalla Tcl-ohjelmaan syntyy transcoder-niminen olio, jonka metodeja kutsumalla Tcl-ohjelma voi ohjata JpegTranscoder-luokan oliota C++ -ohjelmassa.

Tekninen toiminta

JpegTranscoder-luokka on johdettu TclObject-luokasta. Ollakseen tarpeeksi monimutkaista, sille on johdettu kaveriksi luokka JpegTranscoderMatcher kantaluokasta Matcher, jolle JpegTranscoderMatcher::classname = "transcoder".

Kun Tcl-ohjelma käskee new transcoder jpeg/dct, välittyy kutsu ensin CreateCommand.commandiin, joka puolestaan kutsuu Matcher::lookup("transcoder", "jpeg/dct"). Tämä metodi etsii käsiinsä JpegTranscoderMatcher-luokan ainoan instanssin nimeltä transcoder_matcher_jpeg, ja komentaa tälle transcoder_matcher_jpeg.match("jpeg/dct").

Nyt alkaa loppu jo häämöttää. transcoder_matcher_jpeg.match luo uuden JpegTranscoder-olion, ja loppujen lopuksi tulos on, että Tcl:ssä muuttuja transcoder viittaa tähän olioon.

Luokka DeleteCommand

Tämä luokka on CreateCommandin vastaluokka. Se luo Tcl-kieleen uuden käskyn, joka tuhoaa new-komennon avulla luodun olion.

Esimerkki:

delete transcoder

Luokka EmbeddedTcl

EmbeddedTcl on luokka, jolla .tcl-tiedostot voidaan sisällyttää C++ -ohjelmaan.

Systeemi toimii siten, että Tcl-koodia luodaan tavanomaisiin .tcl-tiedostoihin. Ohjelman käännösvaiheessa käytetään hyväksi tcl2c++ -ohjelmaa, joka lukee Tcl-koodin ja muokkaa sen C++ -merkkijonoksi .cc-tiedostoon. Tämä tiedosto sitten käännetään muun C++ -ohjelman mukana.

Syntyvä C++ -tiedosto luo EmbeddedTcl-olion, jonka tehtävä on säilyttää koodi. Kaikkien olioiden koodi suoritetaan ohjelman käynnistyksen yhteydessä luokkakutsulla EmbeddedTcl::init().

Metodi Toiminta Paluuarvo
static void init() Kutsuu ensin TclObject::definea, joka Käy läpi kaikki tähän mennessä luodut TclObject-oliot ja luo niitä vastaavan Tcl-komennon Tcl-tulkkiin. Tämän jälkeen suorittaa kaikkien EmbeddedTcl-olioiden sisältämän koodin.
EmbeddedTcl(int pass, const char* code) Konstruktori. Pass on kokonaisluku 0, 1, 2, ... joka määrää sen järjestyksen, jossa EmbeddedTcl-oliot suoritetaan (ne joiden pass=0, suoritetaan ensin jne.).

C++ -funktion kutsuminen Tcl:stä

Edellä TclObjectin kohdalla selitettiin yksi tapa kutsua C++:aa Tcl:stä käsin oliorajapinnan avulla. Seuraavassa puolestaan esitellään keino, jolla voidaan kutsua ihan "tavallisia" funktioita ilman TclObjectista periytyvää luokkaa.

C++ -funktio täytyy esitellä Tcl:lle edellämainitulla komennolla Tcl::CreateCommand(Tcl_CmdProc* cproc, ClientData cd=0, Tcl_CdmDeleteProc* dproc=0). Lisäksi C++ -funktion on noudatettava tiettyä rajapintaa.

Funktion muoto ja parametrit

C++ -funktion paluuarvo ja paramentrit on määriteltävä tyypin Tcl_CmdProc mukaan seuraavasti:
typedef int Tcl_CmdProc(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]);

Kaksi ensimmäistä parametria ovat Tcl:n käyttöä varten. clientData osoittaa Tcl::buffer_:iin ja interp Tcl::tcl_:ään. Merkityksellisiä parametreja ovat argc ja argv, jotka sisältävät perinteiset C++ -funktion parametrit, ja joita C++ -funktio siis käyttää hyväkseen.

Paluuarvot

C++ -funktion on palautettava jokin seuraavista int-tyyppisistä paluuarvoista: TCL_OK, TCL_ERROR, TCL_RETURN, TCL_BREAK, TCL_CONTINUE. Nämä määrittelevät, onnistuiko C++ -funktion suoritus.

Funktion Tcl-ohjelmalle palauttaman paluuarvon on oltava merkkijono (koska Tcl käsittelee vain merkkijonoja). Mikäli funktion arvona palautetaan TCL_OK, palautettava merkkijono käsitellään Tcl-ohjelmassa C++ -funktion paluuarvona. Mikäli puolestaan palautettiin TCL_ERROR, merkkijono käsitetään virheilmoitukseksi (joka näytetään käyttäjälle).

Palautettava merkkijono asetetaan metodilla Tcl::result("paluuarvo").

Mikäli ei haluta käyttää mainittua Tcl-luokan metodia, voi arvon toki asettaa suoraan:

Palautettava merkkijono asetetaan kutsulla Tcl_SetResult(interp, "paluuarvo", TCL_VOLATILE), jolloin Tcl huolehtii merkkijonon kopioimisesta sopivalle muistialueelle ja sen tuhoamisesta muistista. Palautettavaa merkkijonoa voi myös rakentaa komennolla Tcl_AppendResult(interp, "paluu", "arvo", ... , NULL) joko Tcl_SetResultin jälkeen tai myös sitä käyttämättä.

Seuraavat komennot ovat myös käytettävissä paluumerkkijonon käsittelyyn (eivät keskeisiä):


Tekniseen määrittelyyn