nyelvek szerepelnek, melyek közül nincs legjobb vagy legrosszabb, csak adott célra jobban és kevésbé megfelel˝ o. Egy-egy adott funkciót azonban mégis kizárólag egyetlen programozási nyelven szeretnénk megvalósítani, a felesleges munkát elkerülend˝ o. Ebb˝ ol adódik a probléma, hogy szeretnénk más nyelven megírt könyvtárakat is használni. Az el˝ oadás célja azt az esetet körüljárni, mikor scriptnyelvekb˝ ol (Python, Perl, PHP, stb.) C nyelven íródott függvényeket szeretnénk meghívni. A legtöbb programozási nyelvhez elérhet˝ o egy C API, ennek segítségével olyan modulokat írhatunk, amelyb˝ ol egyrészt hívhatunk más C könyvtárakat, másrészt olyan speciális követelményeknek megfelel˝ o C függvényeket hozhatunk létre, amelyeket már meghívhatunk közvetlenül a scriptnyelvb˝ ol. 2 / 20
Példa két szám összeadására, ha azt C-ben írtuk és PHP-b˝ ol szeretnénk használni: ZEND_NAMED_FUNCTION(wrap_cadd) { int arg1 ; int arg2 ; zval **args[2]; int result; if(ZEND_NUM_ARGS() != 2 || zend_get_parameters_array_ex(2, args) != SUCCESS) { WRONG_PARAM_COUNT; } 3 / 20
C fordító, amely a C-ben írt könyvtárunkból wrapper kódot fog gyártani, hogy ne kézzel kelljen azt. Ezt úgy valósítja meg, hogy a C nyelvet kiterjeszti, így plusz információkat adhatunk meg a kódban, és ezek segítségével már tényleg automatizálható a wrapper készítés. A legfontosabb plusz információk: Typemap-ek: pl. egyszer megadjuk, hogy egy int típusú változót hogyan kell átadni a scriptnyelvnek mikor a C függvény visszatér, és onnantól a SWIG az összes int visszatérési érték˝ u függvénynél alkalmazni fogja azt. Declaration annotation: átnevezhetünk függvényeket, egy változót csak olvashatóvá tehetünk, stb. Class extension: új függvényeket vagy metódusokat írhatunk a C könyvtárakhoz, például egy OO wrappert. 5 / 20
a deklarációkkal foglalkozik. A fordítás egy interfész file értelmezését jelenti, a kimenet pedig szintén programkód lesz, ami az adott nyelv C API-ját fogja használni. A SWIG nem foglalkozik azzal, hogy a generált kódot lefordítsa, bár ez nem egy bonyolult m˝ uvelet, csak doksit kell olvasni. :) 6 / 20
file-nak, de ha szeretnénk bármilyen testreszabást, akkor már külön interface file-ra lesz szükségünk. A legegyszer˝ ubb interface file: %module example %{ #include "example.h" %} %include "example.h" Példa átnevezésre: %rename(osszead) cadd; 7 / 20
esetben egy hello world függvény, valamint egy két számot összeadó függvény), egy interfész file-ból és olyan PHP kiterjesztést létrehozni, hogy utána ezeket egy PHP script meg tudja hívni. Példa: $ swig -php example.i $ gcc -c example.c example_wrap.c \ $(php-config --includes) $ gcc -shared example{,_wrap}.o -o example.so $ php -n -d extension_dir=. runme.php hello world! 3 A PHP script pedig csak ennyi volt: <?php require "example.php"; hello(); echo osszead(1, 2) . "\n"; ?> 8 / 20
nézzünk néhány problémát amivel még számolnunk kell! A legtöbb könyvtár nem csak primitív típusokat használ, azonban a legtöbb esetben nekünk csak át kell adni ezeket a pointereket a könyvtár egy másik függvényének. A SWIG ezt tökéletesen támogatja, és nem is foglalkozik azzal, hogy ténylegesen micsoda az adott adattípus. Korábban láttuk, hogy minden típusra typemap-eket kell írni, a fenti funkció azért jó, mert így elkerülhetjük a felesleges typemap írást. Ha classokról van szó és az interfész file tartalmazza az class metódusait, akkor viszont egy függvény visszatérési értékét a scriptnyelvben is objektumként látjuk, és az objektum metódusait meg tudjuk hívni. 9 / 20
láttuk, hogy a typemap segítségével hogyan lehetett adott esetben egy PHP-ból C-be konvertálni. A másik irány, mikor a C visszatérési értéket kell PHP-ba alakítani. Példa std::string esetére, mikor "befelé" (tehát PHP-ból C-be) ill. kifelé konvertálunk: %typemap(in) string %{ convert_to_string_ex($input); $1.assign(Z_STRVAL_PP($input), Z_STRLEN_PP($input)); %} %typemap(out) string %{ ZVAL_STRINGL($result, const_cast<char*>($1.data()), $1.size(), 1); %} 10/ 20
lehet, és nulla vagy több bemeneti paramétere. A C nyelv az érték és a cím szerinti paraméter-átadást is támogatja. A scriptnyelveknél szokatlan az explicit cím szerinti paraméter-átadás, viszont több visszatérési érték is megszokott. Így például lehetséges a következ˝ o C függvényt: int foo(double a, double b, double *OUT); A következ˝ oképpen használni Pythonból: iresult, dresult = foo(3.5, 2) 11/ 20
nem támogatja a globális változókat. Erre a célra a SWIG egy - a modul nevével megegyez˝ o nev˝ u - külön osztályt hoz létre, és azon belül érhet˝ oek el ezek a változók. Nem tér el sokban a konstansok esete sem, itt csak annyi a különlegesség, hogy a SWIG még a #define segítségével létrehozott "konstansokat" (amik valójában csak a preprocesszor számára léteznek) is támogatja, tudva azt, hogy a csak így lesznek elérhet˝ oek a scriptnyelvek számára. 12/ 20
semmi akadálya. Alapértelmezett esetben azonban ezek csak a scriptnyelv számára lesznek elérhet˝ oek. Így tehát ki terjeszteni a C++ osztályokat, felülírhatjuk a metódusokat, stb. - de ezeket az osztályokat nem adhatjuk át paraméterként C függvényeknek. A másik érdekes kapcsolódó funkció, hogy a SWIG lehet˝ ové teszi nem-OO nyelveknek számára is objektumok kezelését, úgynevezett kilapított függvényekkel. Egy tavalyi GSoC projekt odáig ment, hogy így lehet˝ ové tette C nyelv˝ u programokból C++ könyvtárak használatát! 13/ 20
public: int x; int spam(); }; Abban az esetben ha az adott nyelv nem támogat osztályokat, a következ˝ o függvények lesznek elérhet˝ oek: Foo *new_Foo(); void delete_Foo(Foo *f); int Foo_x_get(Foo *f); void Foo_x_set(Foo *f, int value); int Foo_spam(Foo *f); 14/ 20
örököltetett osztályokat át tudjuk adni paraméterként C függvényeknek. Akkor hasznos ez, hogy például egy speciális file-formátumot tud értelmezni egy könyvtár, viszont az egyes eseményeket mi pl. PHP-ból szeretnénk lekezelni, azonban az értelmez˝ o függvény egyetlen osztályt vár t˝ olünk paraméterként, amelynek a metódusait majd az értelmez˝ o hívogatni fogja. 15/ 20
néz ki: class FooParser { public: virtual void startDocument() = 0; virtual void endDocument() = 0; }; A SWIG segítségével ezt a C++ osztályt örököltethetjük, majd a SWIG olyan kódot fog generálni, amely elrejti a C++ könyvtár el˝ ol azt a problémát, hogy ténylegesen neki egy PHP objektum metódusait kell meghívnia. 16/ 20
wrapper generálásakor tipikus feladatok szoktak adódni. Példa a Frugalware pacman-g2 csomagkezel˝ ojéb˝ ol: void *pacman_pkg_getinfo(PM_PKG *pkg, unsigned char parm); A második paraméter mondja meg, hogy a csomagnak milyen információjára vagyunk kíváncsiak, és ennek értékét˝ ol függ˝ oen mi tudjuk, hogy mit fogunk visszakapni, viszont valahogy el kéne mondani a SWIG-nek, hogy szeretnénk azt a void pointert például PM_GRP pointernek látni. Ehhez kéne írni egy függvényt ami C-ben megteszi a cast-olást, de erre van beépített támogatás: %pointer_cast(void *, PM_GRP *, void_to_PM_GRP); 17/ 20
függvény különböz˝ o típusú objektumokat adhat vissza, tudjuk az összes lehetséges típust, de nem feltétlen mi határozzuk meg a létrehozott objektum típusát. Ilyenkor a %pointer_cast nem használható, viszont a %factory igen: %factory(Geometry *Geometry::create, Point, Circle); A fenti példa azt mondja, hogy a Geometry *Geometry::create függvény vagy Point vagy Circle objektumot ad vissza, ebb˝ ol tessék megfejteni, hogy jelenleg melyik típust kaptuk meg. Ilyen, és ehhez hasonló gyakran el˝ oforduló problémákra ad megoldást a SWIG könyvtár. 18/ 20
Ruby, PHP, Java, Scheme (Guile, MzScheme, CHICKEN), Ocaml, Lua, Pike, C#, Modula-3, Octave, R és Common Lisp (CLISP, Allegro CL, CFFI, UFFI). Jelenlegi fejleszt˝ ok száma: 43 Els˝ o kiadás dátuma: 1996 február Használt programozási nyelv: C, C++ 19/ 20