29 Kaynak Yönetimi

29 Kaynak Yönetimi

Önceki bölümde boolean dizileri uygulamamızda kaynak yönetimi konusunda endişelenmemize gerek yoktu. Bu dizilerin ihtiyaç duyduğu tek şey bellek. Bir diziyi temsil eden her bir kullanıcı verisi Lua tarafından yönetilen kendi belleğine sahiptir. Bir dizi çöp olduğunda (yani program tarafından erişilmez) Lua akabinde onu toplar ve bellleği serbest bırakır. 

Hayat her zaman bu kadar kolay değil. Bazen, bir nesnenin ham belleği yanı sıra dosya tanımlayıcıları, pencere handleleri ve benzerleri diğer kaynaklara ihtiyaç vardır. (Genellikle bu kaynaklar bildiğimiz bellektir ancak sistemin başka bir kısmı tarafından yönetilir.) Bu gibi durumlarda nesne çöp haline gelip toplandığında bir şekilde bu diğer kaynaklar da serbest bırakılmalıdır. Çeşitli nesne yönelimli diller bu ihtiyaç için belli bir mekanizma (finalizer-nihayetlendirici olarak adlandırılan) sağlar. Lua, __gc metametot formunda finalizerler sunmaktadır. Bu metametot sadece kullanıcı verisi değerleri için çalışır. Bir kullanıcı verisi toplanmak üzere ve metatablosu __gc alanına sahip olduğunda  Lua , kullanıcı verisinin kendisinin bir argüman olarak geçildiği, bu alanın değerini (bir fonksiyon olmalı) çağırır. Bu fonksiyon akabinde bu kullanıcı verisiyle ilişkilendirilmiş  her kaynağı serbest bırakabilir.

Bu metametodun ve API'nin bir bütün olarak kullanımını göstermek için bu bölümde Lua'dan dış imkanlara iki bağlantı geliştireceğiz. İlk örnek, bir dizini dolaşmak için bir fonksiyonun farklı bir uyarlamasıdır. İkinci (ve daha önemli olan) örnek, açık kaynak kodlu bir XML ayrıştırıcısı olan Expat için bir bağlantıdır.

29.1 Dizin iteratörü

Daha önce verilen bir dizindeki tüm dosyaları bir tabloya döndüren bir dir fonksiyonu uyguladık. Yeni uygulamamız, her çağrıldığında yeni bir giriş getirecek bir iteratör döndürür. Bu yeni uygulama ile şöyle bir döngü ile bir dizini dolaşmamız mümkün olacak:

for fname in dir(".") do print(fname) end

C'de, bir dizin üzerinde iterasyon için DIR yapısına ihtiyacımız var. DIR'in örnekleri opendir ile oluşturulup, closedir çağrısı ile serbest bırakılmalı. Önceki dir uygulamamız, DIR örneğini yerel bir değişken olarak tuttu ve nihai dosya adını aldıktan sonra bu örneği kapattı. Yeni uygulamamız bu DIR örneğini yerel bir değişkende tutamaz çünkü bu değeri çeşitli çağrılar üzerinde sorgulamalı.
Dahası, yalnızca nihai adı aldıktan sonra dizini kapatamaz; program döngüyü keserse, iteratör bu nihai adı asla alamayacak. Bu nedenle, DIR örneğinin her zaman serbest bırakıldığından emin olmak için adresini bir kullanıcı verisinde saklarız ve dizin yapısını serbest bırakmak için bu kullanıcı verisinin __gc metametodunu kullanırız. 

Uygulamamızdaki merkezi rolüne rağmen, bir dizini temsil eden bu kullanıcı verisinin Lua'dan görünür olması gerekmez. dir fonksiyonu bir iteratör fonksiyonu döndürür; Lua'nın gördüğü budur. Dizin, iteratör fonksiyonun bir üst değeri olabilir. Bu nedenle, iteratör fonksiyonunun bu yapıya doğrudan erişimi vardır, ancak Lua kodun yoktur (ve buna gerek yoktur).

Sonuçta, üç C fonksiyonuna ihtiyacımız var. İlk olarak, Lua iteratörler oluşturmak için çağırdığı bir fabrika olan dir fonksiyonuna ihtiyacımız var; bir DIR yapısı açmalı ve iteratör fonksiyonu bir değeri olarak koymalıdır. İkincisi, iteratör fonksiyonuna ihtiyacımız var. Üçüncü olarak, bir DIR yapısını kapatan __gc metamethod'a ihtiyacımız var. Her zamanki gibi, biz de dizinler için bir metatablo oluşturmak ve bu metatablo ilklemek için gibi ilk düzenlemeler yapmak için ekstra bir fonksiyona gereksiniriz.

Liste 29.1 gösterilen dir fonksiyonu ile kodlarımıza başlayalım. Bu fonksiyondaki ince nokta, dizini açmadan önce kullanıcı verisi oluşturması gerektiğidir. Önce dizini açarsa ve lua_newuserdata çağrısı bir hata oluşturursa, DIR yapısını kaybeder. Doğru sıra ile, bir kez oluşturulan DIR yapısı, kullanıcı verisiyle hemen ilişkilendirilir; bundan sonra ne olursa olsun, __gc metamethod sonunda yapıyı serbest bırakır.

Bir sonraki fonksiyon dir_iter (29.2 listesinde), iteratörün kendisidir. Kodu çok basit. kendi üstdeğerinden DIR yapısı adresini alır ve sonraki girdiyi okumak için readdir 'i çağırır.

dir_gc fonksiyonu (29.2 listesinde de) __gc metamethod. Bu metamethod bir dizini kapatır, ancak bir önlem almalıdır: dizini açmadan önce kullanıcı datası oluşturulduğundan, bu kullanıcı verisinin opendir'in sonucu ne olursa olsun toplanacağından. opendir başarısız olursa, kapatılacak bir şey olmayacaktır.

29.2 listesindeki son fonksiyon, luaopen_dir, bu, bir fonksiyon kütüphanesi açma fonksiyonudur.

Bu örnekte ilginç bir incelik var. İlk başta, dir_gc'nin argümanının bir dizin olup olmadığını kontrol etmesi gerekebilir. Aksi takdirde, kötü niyetli bir kullanıcı, felaket sonuçlarıyla başka bir tür kullanıcı verisi (örneğin bir dosya) ile çağırabilir. tabi, bir Lua programının bu işleve erişmesinin bir yolu yoktur: yalnızca dizinler metatablo'da saklanır ve Lua programları bu dizinlere asla erişmez.

Liste 29.1. dir fabrika fonksiyonu:

#include <dirent.h>
#include <errno.h>

/* iteratör fonksiyon için ön deklarasyon */
static int dir_iter(lua_State *L);

static int l_dir(lua_State *L){
  const char *path = luaL_checkstring(L, 1);

  /* DIR adresini depolamak için bir kullanıcı verisi yarat */
  DIR **d = (DIR **)lua_newuserdata(L, sizeof(DIR *));

  /* metatablosunu set et */
  luaL_getmetatable(L, "LuaBook.dir");
  lua_setmetatable(L, -2);

  /* verilen dizini açmayı dene */
  *d = opendir(path);
  if (*d == NULL) /* dizin açma hatası? */
    luaL_error(L, "cannot open %s: %s", path, strerror(errno));

  /* iteratör fonksiyonu yarat ve döndür;
  onun tek üstdeğeri, dizin kullanıcı verisi,
  artık stack üstünde */
  lua_pushcclosure(L, dir_iter, 1);
  return 1;
}

29.2 XML Ayrıştırıcı

Şimdi lxp'nin basitleştirilmiş bir uygulamasına, Lua ve Expat 1.2 sürümü arasında iletişime bakacağız. Expat, C ile yazılmış açık kaynak kodlu bir XML 1.0 ayrıştırıcısıdır. bir SAX uygulamasıdır(Simple Api Xml- XML için temel API). SAX bir olay tabanlı API'dir. Bu, bir SAX ayrıştırıcısının bir XML belgesini okuduğu , callbackler ile bulduğunu uygulamaya rapor ettiği anlamına gelir. Örneğin, Expat'a "<tag cap="5">hi</tag>" gibi bir stringi ayrıştırmasını söylersek,
üç olay oluşturacaktır: "<tag cap="5" >" alt stringini okuduğunda bir başlangıç öğesi olayı; "hi" 'ı okuduğunda bir metin olayı (karakter veri olayı olarak da adlandırılır); ve "</tag>" 'ı okuduğunda bitiş öğe olayı. Bu olayların her biri uygulamada uygun bir callback işleyicisi çağırır.

Burada tüm expat kütüphanesi ele alınmayacak. Sadece Lua ile etkileşim için yeni teknikler gösteren parçalar üzerinde yoğunlaşacağız. Expat, bir düzineden fazla farklı olayı ele alsa da, önceki örnekte gördüğümüz üç olayı (başlangıç öğeleri, bitiş öğeleri ve metin) ele alacağız. (Kepler projesinden LuaExpat paketi, Expat için oldukça eksiksiz tam bir arayüz sunuyor)

Liste 29.2. dir kütüphanesi için diğer fonksiyonlar:

static int dir_iter(lua_State *L){
  DIR *d = *(DIR **)lua_touserdata(L, lua_upvalueindex(1));
  struct dirent *entry;
  if ((entry = readdir(d)) != NULL)
  {
    lua_pushstring(L, entry->d_name);
    return 1;
  }
  else
    return 0; /* döndürülecek değer yok artık */
}

static int dir_gc(lua_State *L){
  DIR *d = *(DIR **)lua_touserdata(L, 1);
  if (d) closedir(d);
  return 0;
}

int luaopen_dir(lua_State *L){
  luaL_newmetatable(L, "LuaBook.dir");

  /* __gc alanını set et*/
  lua_pushstring(L, "__gc");
  lua_pushcfunction(L, dir_gc);
  lua_settable(L, -3);

  /* ’dir’ fonksiyonunu kaydet*/
  lua_pushcfunction(L, l_dir);
  lua_setglobal(L, "dir");

  return 0;
}

Bu örnek için ihtiyacımız olan Expat API kısmı küçük. İlk olarak, bir Expat ayrıştırıcısı oluşturma ve yok etme için fonksiyonlara ihtiyacımız var:

XML_Parser XML_ParserCreate (const char *encoding);
void XML_ParserFree (XML_Parser p);

encoding argümanı isteğe bağlı; etkileşimimizde NULL kullanacağız.

Ayrıştırıcıya sahip olduktan sonra callback işleyicileri kaydettirmeniz lazım :

XML_SetElementHandler(XML_Parser p,
                      XML_StartElementHandler start,
                      XML_EndElementHandler end);

XML_SetCharacterDataHandler(XML_Parser p,
                            XML_CharacterDataHandler hndl);

İlk fonksiyon, başlangıç ve bitiş elemanları için işleyicileri kaydeder. İkinci fonksiyon, metin (karakter veri, XML dilinde) için işleyicileri kaydeder.

Tüm callback işleyicileri bazı kullanıcı verilerini ilk parametresi olarak alır. Başlangıç öğesi işleyicisi de etiket(tag) adını ve nitelikleri(attribute) alır:

typedef void (*XML_StartElementHandler)(void *uData,
                                        const char *name,
                                        const char **atts);

Nitelikler, ardışık stringler  çiftlerinin her birinin nitelik adı ve değerini tuttuğu NULL ile sonlandırılmış bir string dizisi olarak gelir. Bitiş öğe işleyicisi yalnızca bir ekstra parametreye sahip, etiket adı(tag):

typedef void (*XML_EndElementHandler)(void *uData,
                                     const char *name);

Son olarak, metin işleyicisi ek bir parametre olarak yalnızca metni alır. Bu metin stringi null ile sonlandırılmış değil; bunun yerine, belli bir uzunluğa sahiptir:

typedef void (*XML_CharacterDataHandler)(void *uData,
                                        const char *s,
                                        int len);

Expat'a metin beslemesi için, aşağıdaki fonksiyonu kullanıyoruz :

int XML_Parse (XML_Parser p, const char *s, int len, int isLast);

Expat, XML_Parse ardışık çağrılar aracılığıyla parça halinde ayrıştırılacak belgeyi alır. XML_Parser son argüman, isLast, o parçanın bir belgenin sonuncusu olup olmadığını Expat bildirir. Her bir metin parçasının sıfır ile sonlandırılması gerekmediğine dikkat edin; bunun yerine, belli bir uzunluk sağlıyoruz. Eğer ayrıştırma hatası algılarsa XML_Parse fonksiyonu sıfır döndürür. (Expat ayrıca hata bilgilerini almak için fonksiyonlarda sağlar, ancak basitlik uğruna bunları burada görmezden geleceğiz.)

Expat'tan ihtiyacımız olan son fonksiyon, işleyicilere aktarılacak kullanıcı verilerini ayarlamamızı sağlar:

void XML_SetUserData (XML_Parser p, void *uData);

Şimdi Lua'da bu kütüphaneyi nasıl kullanabileceğimize bir göz atalım. İlk yaklaşım doğrudan yaklaşımdır: tüm bu fonksiyonları Lua'ya aktarmanız yeterlidir. Daha iyi bir yaklaşım, işlevselliği Lua'ya uyarlamaktır. Örneğin, LUA tipsiz olduğundan, herbir tür callbackleri ayarlamak için farklı fonksiyonlara ihtiyacımız yok. Daha da iyisi, callback kayıt fonksiyonlarından tamamen sıyrılabiliriz.
Bunun yerine, bir ayrıştırıcı oluşturduğunuzda, her biri uygun bir anahtarla tüm callback işleyicileri içeren bir callback tablosu sağlarız. Örneğin, bir belgenin düzenini yazdırmak istiyorsak, aşağıdaki callback tablosunu kullanabiliriz:

local count = 0

callbacks = {
    StartElement = function(parser, tagname)
        io.write("+ ", string.rep(" ", count), tagname, "\n")
        count = count + 1
    end,

    EndElement = function(parser, tagname)
        count = count - 1
        io.write("- ", string.rep(" ", count), tagname, "\n")
    end
}

"<to> <yes/> </to >" girişi ile beslenen bu işleyiciler şunu yazdırır:

+   to
+       yes
-       yes
-   to

Bu API ile, callbackleri işlemek için fonksiyonlara ihtiyacımız yok. Onları doğrudan callback tablosunda manipüle ediyoruz. Böylece, tüm API'nin sadece üç fonksiyonuna ihtiyac vardır: biri ayrıştırıcılar oluşturmak için, biri bir metin parçasını ayrıştırmak için ve biri ayrıştırıcıyı kapatmak için. Aslında, son iki fonksiyonu ayrıştırıcı nesnelerinin metotları olarak uygulayacağız. API'nin tipik bir kullanımı şöyle olabilir:

p = lxp.new(callbacks) -- yeni ayrıştırıcı oluştur

for l in io.lines() do -- girdi satıları üzerinde dolaş
    assert(p:parse(l)) -- satırı ayrıştır
    assert(p:parse("\n")) -- yeni bir satır ekle
end

assert(p:parse()) -- döküman bitti
p:close()

Şimdi dikkatimizi uygulamaya çevirelim. İlk karar, Lua'da bir ayrıştırıcının nasıl temsil edileceğidir.
Bir kullanıcı verisi kullanmak oldukça doğaldır, ancak içine ne koymamız gerekir? En azından, aktüel Expat ayrıştırıcısını ve callback tablosunu tutmalıyız. Bir kullanıcı verisi (veya herhangi bir C yapısı içinde) içinde bir Lua tablosu saklayamayız. Lua 5.1'de,  kullanıcı verisinin çevresi olarak tabloyu ayarlayabiliriz.
Ayrıca LUA durumunu bir ayrıştırıcı nesnesine saklamalıyız, çünkü bu ayrıştırıcı nesneleri, Expat callback çağrısı alan tek şeydir ve callbackler Lua'ı çağırması gerekir. Bu nedenle, ayrıştırıcı nesnesi için tanımlama aşağıdaki gibidir:

#include <stdlib.h>
#include "xmlparse.h"
#include "lua.h"
#include "lauxlib.h"

typedef struct lxp_userdata{
  lua_State *L;
  XML_Parser *parser; /* ilgili expat ayrıştırıcı*/
} lxp_userdata;

Bir sonraki adım ayrıştırıcı nesneleri oluşturan fonksiyon lxp_make_parser. Liste 29.3 kodunu göstermekte. Bu fonksiyonun dört ana adıma sahip:

*İlk adım ortak bir şablon izler: ilk önce bir kullanıcı data oluşturur; sonra kullanıcı data'ı uygun değerlerle ön-ilkler; ve son olarak onun metatablosunu ayarlar. Ön-ilklemenin sebebi ince: ilkleme sırasında herhangi bir hata varsa, finalizer (__gc metamethod) kullanıcı verisini uygun bir durumda bulacağından emin olmalıyız.

*2. adımda, fonksiyon bir Expat ayrıştırıcısı oluşturur, kullanıcı verisinde onu depolar ve hataları denetler.

*3. adım, fonksiyonun ilk argümanının aslında bir tablo (callback tablosu) olmasını sağlar ve yeni kullanıcı verisi için çevre olarak onu ayarlar.

*Son adım Expat ayrıştırıcını ilkler. callback fonksiyonlarına geçirilen nesne olarak kullanıcı datasını ayarlar ve callback fonksiyonlarını ayarlar. Bu callback fonksiyonları tüm ayrıştırıcılar için aynı olduğuna dikkat edin; sonuçta, C'de dinamik olarak yeni fonksiyonlar oluşturmak imkansızdır. bunun yerine, bu sabit C fonksiyonları, her seferinde hangi Lua fonksiyonlarının çağırması gerektiğine karar vermek için callback tablosunu kullanır.

Bir sonraki adım ayrıştırma metotu lxp_parse (Liste 29.4), XML verilerini parçaya ayrıştırır. İki argüman alır: ayrıştırıcı nesnesi (metodun kendi) ve isteğe bağlı bir XML veri parçası. Herhangi bir veri olmadan çağrıldığında, belgenin daha fazla parçaya sahip olmadığını Expat'a bildirir.

lxp_parse XML_Parse'i çağırdığında , sonraki fonksiyon, verilen belge parçasında bulduğu her ilgili öğe için işleyicileri çağırır. Bu nedenle, lxp_parse önce bu işleyicileri için bir çevre hazırlar. XML_Parse çağrısında bir ayrıntı daha var: bu fonksiyonun son argümanının, verilen metin parçasının sonuncusu olup olmadığını Expat'a söylediğini unutmayın. Bir argüman olmadan ayrıştırıcıyı çağırdığımız zaman s NULL olur, bu nedenle bu son argüman true olacaktır.

Şimdi dikkatimizi f_StartElement, f_EndElement ve f_CharData callback fonksiyonlarına çevirelim.
Tüm bu üç fonksiyon benzer bir yapıda: herbiri callback tablosunun belirli bir olay için bir Lua işleyicisi tanımlayıp tanımlamadığını kontrol eder ve öyleyse argümanları hazırlar ve sonra bu Lua işleyicisini çağırır.  Önce 29.5 listesinde f_CharData işleyicisini görelim. Kodu oldukça basittir. Bu işleyici (ve diğerleri de), ayrıştırıcıyı oluşturduğumuzda XML_SetUserData çağrımız nedeniyle ilk argümanı olarak bir lxp_userdata yapısı alır. Lua durumu alındıktan sonra lxp_parse ile set edilen erişebilir işleyici: yığının indeks 3'de callback tablosu ve yığın indeks 1'de ayrıştırıcının kendisi. Daha sonra karşılık gelen işleyicisini LUA 'da(mevcut olduğunda) çağırır, iki argümanla: ayrıştırıcı ve karakter verisi (bir string).

Liste 29.3. ayrıştırıcı nesneleri oluşturan fonksiyon:

/* callback fonksiyonları için ön deklarasyon */
static void f_StartElement(void *ud,
                           const char *name,
                           const char **atts);
static void f_CharData(void *ud, const char *s, int len);
static void f_EndElement(void *ud, const char *name);

static int lxp_make_parser(lua_State *L){
  XML_Parser p;
  lxp_userdata *xpu;

  /* (1) ayrıştırıcı nesne oluştur */
  xpu = (lxp_userdata *)lua_newuserdata(L,
                                        sizeof(lxp_userdata));

  /*hata durumu için onu ön-ilkle,  */
  xpu->parser = NULL;

  /* metatablosunu set et */
  luaL_getmetatable(L, "Expat");
  lua_setmetatable(L, -2);

  /* (2) Expat ayrıştırıcı yarat */
  p = xpu->parser = XML_ParserCreate(NULL);
  if (!p)
    luaL_error(L, "XML_ParserCreate failed");

  /* (3) callback tablosu kontrol et depola */
  luaL_checktype(L, 1, LUA_TTABLE);
  lua_pushvalue(L, 1); /* put table on the stack top */
  lua_setfenv(L, -2); /* set it as environment for udata */

  /* (4) Expat ayrıştırıcıyı ayarla*/
  XML_SetUserData(p, xpu);
  XML_SetElementHandler(p, f_StartElement, f_EndElement);
  XML_SetCharacterDataHandler(p, f_CharData);
  return 1;
}

Liste 29.4. XML parçasını ayrıştıran fonksiyon:

static int lxp_parse(lua_State *L){
  int status;
  size_t len;
  const char *s;
  lxp_userdata *xpu;

  /* ilk argümanı al ve kontrol et (ayrıştırıcı olmalıdır) */
  xpu = (lxp_userdata *)luaL_checkudata(L, 1, "Expat");

  /* ikinci argümanı al (bir string) */
  s = luaL_optlstring(L, 2, NULL, &len);

  /* işleyiciler için çevreyi hazırla: */
  /* stack indeks 3'e callback tablosunu koy */
  lua_settop(L, 2);
  lua_getfenv(L, 1);
  xpu->L = L; /* set Lua state */

  /* stringi ayrıştırmak için Expat'ı çağır */
  status = XML_Parse(xpu->parser, s, (int)len, s == NULL);

  /* hata kodu döndür */
  lua_pushboolean(L, status);
  return 1;
}

Liste 29.5. karakter verisi için işleyici:

static void f_CharData(void *ud, const char *s, int len){
  lxp_userdata *xpu = (lxp_userdata *)ud;
  lua_State *L = xpu->L;

  /* işleyiciyi al */
  lua_getfield(L, 3, "CharacterData");
  if (lua_isnil(L, -1))  { /* işleyici yok mu? */
    lua_pop(L, 1);
    return;
  }

  lua_pushvalue(L, 1); /* ayrıştırıcıyı push et (’kendisi’) */
  lua_pushlstring(L, s, len); /* Char datayı push et */
  lua_call(L, 2, 0); /* işleyiciyi çağır */
}

Liste 29.6. bitiş öğesi için işleyici:

static void f_EndElement(void *ud, const char *name){
  lxp_userdata *xpu = (lxp_userdata *)ud;
  lua_State *L = xpu->L;

  lua_getfield(L, 3, "EndElement");
  if (lua_isnil(L, -1))  { /* işleyici yok? */
    lua_pop(L, 1);
    return;
  }

  lua_pushvalue(L, 1); /* ayrıştırıcıyı push et (’kendisi’) */
  lua_pushstring(L, name); /* tag ismini push et */
  lua_call(L, 2, 0); /* işleyiciyi çağır */
}

f_EndElement işleyicisi f_CharData'a oldukça benzer; 29.6 Listesine bakın. Ayrıca, karşılık gelen Lua işleyicisini iki argümanla __ayrıştırıcı ve etiket adı(yine bir string ancak şimdi null ile sonlandırılmış)  çağırır.

Liste 29.7 bitiş işleyicisini gösterir, f_StartElement. üç argümanla Lua'ı çağırır: ayrıştırıcı, etiket adı ve nitelikler listesi. Bu işleyici diğerlerinden biraz daha karmaşıktır, çünkü etiketin nitelikleri listesini Lua'ya çevirmesi gerekir. Oldukça doğal bir çeviri kullanır, nitelik adlarını değerlerine ilişkilendiren bir tablo oluşturur. Örneğin, şöyle bir başlangıç etiketi için

<to method="post" priority="high">

devamdaki nitelikler tablosunu oluşturur:

{method = "post", priority = "high"}

Ayrıştırıcılar için son yöntem 29.8 listesindeki close'dur. Bir ayrıştırıcıyı kapattığımızda, kaynaklarını, yani Expat yapısını boşaltmak zorundayız. Oluşturma sırasında ara sıra hatalar nedeniyle, bir ayrıştırıcının bu kaynağa sahip olmayabileceğini unutmayın. onu kapattığımızda tutarlı bir durumda ayrıştırıcıyı nasıl tuttuğumuza dikkat edin, bu nedenle tekrar kapatmaya çalışırsak veya çöp toplayıcı onu hallettiğinde sorun kalmaz. Aslında, tam olarak finalizer olarak bu fonksiyonu kullanacağız. Bu, programcı kapatmasa bile, her ayrıştırıcının sonunda kaynaklarını serbest bırakmasını sağlar.

29.9 listesi son adım: kütüphaneyi açar ve önceki tüm parçaları bir araya getirir. Burada, 28.3 bölümünden nesne yönelimli boolean-dizisi örneğindeki  aynı şemayı kullanıyoruz: bir metatablo oluşturuyoruz, içine tüm metotları koyuyoruz ve __index alanını kendisine işaret ettiriyoruz. Bunun için ayrıştırıcı metotları (lxp_meths) ile bir listeye ihtiyacımız var. Bu kütüphanenin fonksiyonlarına(lxp_funcs) sahip bir listeye de ihtiyacımız var . Nesne yönelimli kütüphanelerde ortak olduğu gibi, bu liste yeni ayrıştırıcılar oluşturan tek bir fonksiyona sahiptir. Son olarak, açma fonksiyonu luaopen_lxp, metatablo oluşturmalı, kendisine işaret etmelidir (__index aracılığıyla) ve fonksiyonlar ve metotları kaydeder.

Liste 29.7. başlangıç öğeleri işleyicisi:

static void f_StartElement(void *ud,
                           const char *name,
                           const char **atts){
  lxp_userdata *xpu = (lxp_userdata *)ud;
  lua_State *L = xpu->L;

  lua_getfield(L, 3, "StartElement");
  if (lua_isnil(L, -1))  { /* işleyici yok? */
    lua_pop(L, 1);
    return;
  }

  lua_pushvalue(L, 1); /* ayrıştırıcı push et (’kendisi’) */
  lua_pushstring(L, name); /* etiket ismini push et */

  /* nitelik tablosu oluştur ve doldur */
  lua_newtable(L);
  for (; *atts; atts += 2)  {
    lua_pushstring(L, *(atts + 1));
    lua_setfield(L, -2, *atts); /* table[*atts] = *(atts+1) */
  }

  lua_call(L, 3, 0); /* işleyiciyi çağır */
}

Liste 29.8. ayrıştırıcıyı kapama metodu:

static int lxp_close(lua_State *L){
  lxp_userdata *xpu =
      (lxp_userdata *)luaL_checkudata(L, 1, "Expat");

  /* Expat ayrıştırıyı serbest bırak(mevcutsa) */
  if (xpu->parser)
    XML_ParserFree(xpu->parser);
  xpu->parser = NULL;
  return 0;
}

Liste 29.9 lxp kütüphanesi için ilkleme kodu:

static const struct luaL_Reg lxp_meths[] = {
    {"parse", lxp_parse},
    {"close", lxp_close},
    {"__gc", lxp_close},
    {NULL, NULL}
};

static const struct luaL_Reg lxp_funcs[] = {
    {"new", lxp_make_parser},
    {NULL, NULL}
};

int luaopen_lxp(lua_State *L){
  /* metatablo oluştur */
  luaL_newmetatable(L, "Expat");

  /* metatable.__index = metatable */
  lua_pushvalue(L, -1);
  lua_setfield(L, -2, "__index");

  /* metotları kaydet*/
  luaL_register(L, NULL, lxp_meths);

  /* fonksiyonları kaydet (sadece lxp.new) */
  luaL_register(L, "lxp", lxp_funcs);
  return 1;
}

Hiç yorum yok:

Yorum Gönder