Hlavní navigace

Programovací jazyk Lua v aplikacích II

21. 4. 2009
Doba čtení: 22 minut

Sdílet

V sedmé části seriálu o programovacím jazyce Lua budou popsány rozdíly v aplikačním programovém rozhraní verze 5.0 a 5.1, vysvětlíme si způsob použití vlastního alokátoru paměti a taktéž si ukážeme, jakým způsobem lze kontrolovat a převádět parametry předávané z Lua skriptu do volaných céčkových funkcí.

Obsah

1. Rozdíly v API mezi Lua 5.0 a Lua 5.1
2. První demonstrační příklad: nekorektní inicializace knihoven v Lua 5.1
3. Druhý demonstrační příklad: korektní inicializace knihoven v Lua 5.1
4. Použití vlastního alokátoru paměti
5. Třetí demonstrační příklad: alokátor paměti vypisující základní ladicí informace
6. Kontrola parametrů předávaných z Lua skriptu do C
7. Konverze parametrů předávaných z Lua skriptu do C
8. Zdrojové kódy všech demonstračních příkladů i testovacích skriptů
9. Odkazy na Internetu

1. Rozdíly v API mezi Lua 5.0 a Lua 5.1

V předchozí části seriálu o programovacím jazyku Lua byly popsány některé základní funkce aplikačního programového rozhraní (API) běhového prostředí. Vzhledem k tomu, že mezi verzí 5.0 a 5.1 došlo k několika změnám v API, může docházet k chybám při pokusu o spuštění aplikace používající „staré“ API spolu s Lua 5.1 (nejedná se pouze o teoretickou možnost, na Internetu je k dispozici poměrně velké množství demonstračních příkladů vytvořených pro Lua 5.0). Ve druhé a třetí kapitole si popíšeme rozdílné chování API při načítání a inicializaci standardních knihoven. Jeden z poměrně zásadních rozdílů mezi Lua 5.0 a Lua 5.1 spočívá také v odlišném způsobu vytvoření stavu běhového prostředí, který se ukládá do datové struktury lua_State. Zatímco ve verzi 5.0 se pro vytvoření a inicializaci stavu používala bezparametrická funkce lua_open() (dnes dostupná jako makro pro zachování zpětné kompatibility), ve verzi 5.1 lze pro tentýž účel použít buď taktéž bezparametrickou funkci luaL_newstate(), popř. funkci lua_newstate(), které lze předat ukazatel na alokátor paměti (tímto způsobem je možné ovlivnit způsob alokace paměti běhového prostředí Lua, čehož lze využít jak pro zvýšení efektivity správy paměti, tak i pro ladicí účely). Této problematice se budeme věnovat ve čtvrté a páté kapitole.

2. První demonstrační příklad: nekorektní inicializace knihoven v Lua 5.1

V prvním demonstračním příkladu je ukázán (dnes nekorektní) způsob inicializace knihoven, který již není možné ve verzi Lua 5.1 použít. Přesněji řečeno – některé knihovny sice je možné níže uvedeným způsobem načíst a inicializovat, ale chyba nastane v případě pokusu o načtení knihovny io pomocí přímého volání céčkové funkce luaopen_io(). Důvod, proč přímé otevření této knihovny není funkční, spočívá ve způsobu práce se soubory v knihovně io – pro otevření souboru se používá funkce popen(), pro uzavření by se tedy měla použít funkce pclose() a nikoli fclose() (zavolání fclose() na soubor otevřený pomocí popen() není dle POSIXu povoleno). Interpret Lua si sice pamatuje, kterou funkcí mají být soubory uzavřeny, ovšem pro korektní zapamatování tohoto příznaku je nutné luaopen_io() volat přes lua_call(), přičemž se korektně nastaví prostředí (environment), ve kterém jsou mj. uloženy i informace o tom, jakým způsobem mají být soubory uzavřeny. V případě, že interpret zjistí, že v prostředí nejsou tyto informace uloženy, vypíše se chybové hlášení „PANIC: unprotected error in call to Lua API (no calling environment)“ a aplikace je ukončena. Tímto způsobem se interpret brání situaci, ve které by mohlo dojít k porušení souborů. Jednodušší způsob práce s knihovnami, který je podporovaný v Lua 5.1, je uveden ve třetí kapitole. Následuje výpis zdrojového kódu prvního demonstračního příkladu:

/*
 * Prvni demonstracni priklad
 *
 * Nekorektni, ve verzi Lua 5.1 jiz nepodporovany,
 * zpusob nacteni knihoven.
 */

#include <stdio.h>
#include <stdlib.h>

/* Zakladni a doplnkove funkce interpretu jazyka Lua */
#include <lualib.h>
#include <lauxlib.h>

/* Skript napsany v programovacim jazyce Lua */
const char * SCRIPT =
"print('mocniny cisla 2')\n"\
"x=1\n"\
"for i=0, 16 do\n"\
"    print(i, x)\n"\
"    x=x*2\n"\
"end\n";

/* Hlavni funkce konzolove aplikace */
int main(void)
{
    int result;

    /* vytisteni hlavicky */
    puts(LUA_RELEASE);
    puts(LUA_COPYRIGHT);
    puts(LUA_AUTHORS);
    putchar('\n');

    /* vytvoreni objektu, do nejz se uklada stav interpretu */
    lua_State* L = lua_open();
    /* nacteme zakladni knihovnu obsahujici mj. i funkci print() */
    luaopen_base(L);
    /* pokus o nacteni knihovny se vstupne-vystupnimi funkcemi */
    luaopen_io(L);
    /* nacteni retezce interpretem, jeho preklad a nasledne spusteni */
    result = luaL_dostring(L, SCRIPT);
    /* odstraneni vsech objektu asociovanych se stavem "Lua" */
    lua_close(L);
    if (result != 0)
    {
        printf("Error # %d\n", result);
    }
    /* vypocet navratoveho kodu */
    return (result != 0);
}

/* finito */ 

Po spuštění prvního demonstračního příkladu se vypíše chybové hlášení a program je následně ukončen, aniž by byl spuštěn vložený skript. V následujícím výpisu je sloučený standardní i chybový výstup, protože poslední řádek (s hlášením o chybě) je poslán na chybový výstup – stderr, zatímco řádky předchozí na výstup standardní – stdout:

Lua 5.1.3
Copyright (C) 1994-2008 Lua.org, PUC-Rio
R. Ierusalimschy, L. H. de Figueiredo & W. Celes

PANIC: unprotected error in call to Lua API (no calling environment) 

3. Druhý demonstrační příklad: korektní inicializace knihoven v Lua 5.1

Ve druhém demonstračním příkladu se standardní knihovny otevírají pomocí funkce luaL_openlibs(L) (proměnná L reprezentuje stav interpretu – viz předchozí část tohoto seriálu) a nikoli přes luaopen_base() a luaopen_io(), což je již korektní – nedojde k běhové chybě. Funkce luaL_openlibs(L) automaticky otevře všechny standardní knihovny, tj. nahrazuje volání luaopen_base(), luaopen_packa­ge(), luaopen_string(), luaopen_table(), luaopen_math(), luaopen_io(), luaopen_os() a konečně luaopen_debug(). V případě, že skript nemá mít povolený přístup k některé z těchto knihoven (například budeme chtít zakázat volání API operačního systému či možnost manipulace se soubory; na mikrořadičích nemusí být dostupná knihovna math atd.), může se místo „universální“ funkce luaL_openlibs() použít například následující fragment kódu. Knihovny, které se nemají otevřít, lze jednoduše z tabulky lualibs odstranit či zapoznámkovat (struktura luaL_Reg je definována v souboru lauxlib.h):

/* Seznam knihoven, ktere se maji otevrit */
static const luaL_Reg lualibs[] = {
    {"", luaopen_base},
    {LUA_LOADLIBNAME, luaopen_package},
    {LUA_TABLIBNAME, luaopen_table},
    {LUA_IOLIBNAME, luaopen_io},
    {LUA_OSLIBNAME, luaopen_os},
    {LUA_STRLIBNAME, luaopen_string},
    {LUA_MATHLIBNAME, luaopen_math},
    {LUA_DBLIBNAME, luaopen_debug},
    {NULL, NULL}
};

/* Otevreni vsech knihoven ulozenych v poli lualibs */
LUALIB_API void open_custom_libs(lua_State *L)
{
    const luaL_Reg *lib = lualibs;
    for (; lib->func; lib++)
    {
        /* nevola se primo lib->func, ale lua_call() s nastavenymi parametry */
        /* (volani lib->func je nekorektni, jedna se o stejny kod, */
        /* jaky byl pouzit v prvnim demonstracnim prikladu) */
        lua_pushcfunction(L, lib->func);
        lua_pushstring(L, lib->name);
        lua_call(L, 1, 0);
    }
} 

Zdrojový kód druhého demonstračního příkladu má tvar:

/*
 * Druhy demonstracni priklad
 *
 * Korektni zpusob prace s knihovnami, ktery je nutne pouzit
 * v Lua 5.1
 */

#include <stdio.h>
#include <stdlib.h>

/* Zakladni a doplnkove funkce interpretu jazyka Lua */
#include <lualib.h>
#include <lauxlib.h>

/* Skript napsany v programovacim jazyce Lua */
const char * SCRIPT =
"print('mocniny cisla 2')\n"\
"x=1\n"\
"for i=0, 16 do\n"\
"    print(i, x)\n"\
"    x=x*2\n"\
"end\n";

/* Hlavni funkce konzolove aplikace */
int main(void)
{
    int result;

    /* vytisteni hlavicky */
    puts(LUA_RELEASE);
    puts(LUA_COPYRIGHT);
    puts(LUA_AUTHORS);
    putchar('\n');

    /* vytvoreni objektu, do nejz se uklada stav interpretu */
    lua_State* L = lua_open();
    /* nacteni vsech knihoven - korektni zpusob */
    luaL_openlibs(L);
    /* nacteni retezce interpretem, jeho preklad a nasledne spusteni */
    result = luaL_dostring(L, SCRIPT);
    /* odstraneni vsech objektu asociovanych se stavem "Lua" */
    lua_close(L);
    if (result != 0)
    {
        printf("Error # %d\n", result);
    }
    /* vypocet navratoveho kodu */
    return (result != 0);
}

/* finito */ 

Po spuštění druhého demonstračního příkladu se knihovny korektně načtou, provede se jejich inicializace a posléze se spustí i skript uložený v řetězci. Výstupem je tabulka druhých mocnin čísla 2:

Lua 5.1.3
Copyright (C) 1994-2008 Lua.org, PUC-Rio
R. Ierusalimschy, L. H. de Figueiredo & W. Celes

mocniny cisla 2
0       1
1       2
2       4
3       8
4       16
5       32
6       64
7       128
8       256
9       512
10      1024
11      2048
12      4096
13      8192
14      16384
15      32768
16      65536 

4. Použití vlastního alokátoru paměti

V první kapitole jsme si řekli, že jeden z rozdílů mezi verzí Lua 5.0 a Lua 5.1 spočívá v odlišném způsobu vytvoření stavu interpreteru, který se ukládá do datové struktury lua_State. Existují tři základní funkce pro vytvoření a prvotní inicializaci stavu – lua_open(), lua_newstate() a luaL_newstate(). Funkce lua_open() byla využívána v předchozích verzích jazyka Lua, dnes je udržována především kvůli zpětné kompatibilitě – pohledem do hlavičkového souboru lua.h se můžeme přesvědčit, že se ve skutečnosti jedná o makro preprocesoru volající funkci luaL_newstate(). Jak makro lua_open(), tak i funkce luaL_newstate() nevyžadují při svém volání žádné parametry a jejich návratovou hodnotou je ukazatel na strukturu lua_State, který je použit při všech dalších voláních aplikačního programového rozhraní. Funkce lua_newstate() je poněkud odlišná, neboť jí lze předat dvojici ukazatelů – ukazatel na funkci volanou pro alokaci, realokaci a uvolňování paměti a uživatelsky nastavovaný ukazatel, jehož hodnota je předávána do funkce, která provádí alokaci paměti (použití tohoto ukazatele není nijak specifikováno, může například obsahovat odkaz na strukturu popisující konfiguraci či stav aplikace). V následující kapitole si ukážeme, jak by mohla funkce provádějící alokaci, realokaci a uvolnění paměti vypadat.

5. Třetí demonstrační příklad: alokátor paměti vypisující základní ladicí informace

V dnešním třetím demonstračním příkladu je ukázáno, jakým způsobem je možné vytvořit funkci provádějící alokaci, realokaci i uvolňování paměti. Alokátor paměti je automaticky volán běhovým prostředím jazyka Lua, například ve chvíli, kdy jsou inicializovány knihovny, vytvářeny funkce či proměnné atd. Taktéž ve chvíli, kdy je nějaká proměnná již nepoužívaná (například blok, ve kterém je proměnná vytvořena, není platný), může garbage collector zavolat tuto funkci, aby provedla uvolnění některého z bloků paměti. Hlavička alokátoru paměti má čtyři parametry – uživatelsky nastavený ukazatel (viz funkce lua_newstate()), ukazatel na blok paměti, jenž se má uvolnit nebo jehož velikost se má změnit, původní délka bloku a nová (požadovaná) délka bloku. V případě, že je ukazatel na blok paměti nastavený na hodnotu NULL, znamená to, že se požaduje alokace nového bloku o zadané délce (čtvrtý parametr). Při uvolnění bloku paměti se z funkce musí vrátit hodnota NULL, v opačném případě ukazatel na alokovaný či realokovaný blok (realokace totiž může znamenat, že se blok dat přesune do nové oblasti paměti). Následuje výpis zdrojového kódu třetího demonstračního příkladu; popis, co příklad po svém spuštění vypíše, je uveden pod zdrojovým kódem:

/*
 * Treti demonstracni priklad
 *
 * Pouziti vlastni alokacni funkce, ktera
 * loguje veskere alokace, realokace ci uvolnovani pameti
 * (tyto operace jsou automaticky provadeny interpretem a GC)
 */

#include <stdio.h>
#include <stdlib.h>

/* Zakladni a doplnkove funkce interpretu jazyka Lua */
#include <lualib.h>
#include <lauxlib.h>

/* Skript napsany v programovacim jazyce Lua */
/* Ve skriptu se neustale zvetsuje pamet nutna pro ulozeni */
/* retezce 'x' */
const char * SCRIPT =
"x='hello '\n"\
"for i=0, 10 do\n"\
"    local z='world!'\n"\
"    x=x..z..' '\n"\
"end\n";

/* Vlastni alokacni funkce */
/* ud - uzivatelsky nastaveny ukazatel (vzdy konstantni) */
/* ptr - ukazatel na pamet, ktera se ma alokovat ci dealokovat */
/* osize - puvodni delka bloku */
/* nsize - nova (pozadovana) delka bloku */
static void *memory_allocator(void *ud, void *ptr, size_t osize, size_t nsize)
{
    printf("memory_allocator (ud=%p): ", ud);
    /* pokud je nsize == 0, znamena to, ze se ma pamet uvolnit */
    if (nsize == 0)
    {
        printf("uvolnuji %d bajtu pameti na adrese %p\n", osize, ptr);
        /* Muze nastat free(NULL), pokud osize==0, to je vsak v ANSI C korektni */
        free(ptr);
        return NULL;
    }
    else
    {
        if (ptr==NULL)
        {
            printf("alokuji %d bajtu pameti\n", nsize);
        }
        else
        {
            printf("realokuji %d bajtu pameti na adrese %p na delku %d\n", osize, ptr, nsize);
        }
        /* pokud ptr==NULL, provede se malloc() */
        return realloc(ptr, nsize);
    }
}

/* Hlavni funkce konzolove aplikace */
int main(void)
{
    int result;

    /* vytisteni hlavicky */
    puts(LUA_RELEASE);
    puts(LUA_COPYRIGHT);
    puts(LUA_AUTHORS);
    putchar('\n');

    /* vytvoreni objektu, do nejz se uklada stav interpretu */
    lua_State* L = lua_newstate(memory_allocator, (void*)0x1234);
    /* nacteni retezce interpretem, jeho preklad a nasledne spusteni */
    result = luaL_dostring(L, SCRIPT);
    /* odstraneni vsech objektu asociovanych se stavem "Lua" */
    lua_close(L);
    if (result != 0)
    {
        printf("Error # %d\n", result);
    }
    /* vypocet navratoveho kodu */
    return (result != 0);
}

/* finito */ 

Po spuštění tohoto příkladu se nejprve provede alokace paměti pro samotný interpret i jeho běhové prostředí (včetně stavu interpretu) a následně též pro vlastní skript, ve kterém je neustále prodlužován řetězec x. Jakmile je skript ukončen a je zavolána funkce lua_close(), dojde k postupnému uvolnění všech bloků paměti. Požadavky běhového prostředí na alokace, realokace i uvolňování paměťových bloků se vypisují na standardní výstup. Na výpisu uvedeném pod odstavcem můžeme vidět, že i při běhu velmi jednoduchého skriptu obsahující pouze dvě proměnné, je provedeno poměrně velké množství volání paměťového alokátoru. Nutno však podotknout, že množství počátečních alokací na začátku programu (inicializace interpretu) i uvolnění paměti na konci programu je do značné míry nezávislé na délce či složitosti skriptu. Na výpisu si také povšimněte, že uživatelsky nastavený ukazatel má při volání alokátoru paměti stále stejnou hodnotu zadanou jako druhý parametr funkce lua_newstate():

Lua 5.1.3
Copyright (C) 1994-2008 Lua.org, PUC-Rio
R. Ierusalimschy, L. H. de Figueiredo & W. Celes

memory_allocator (ud=00001234): alokuji 376 bajtu pameti
memory_allocator (ud=00001234): alokuji 192 bajtu pameti
memory_allocator (ud=00001234): alokuji 720 bajtu pameti
memory_allocator (ud=00001234): alokuji 32 bajtu pameti
memory_allocator (ud=00001234): uvolnuji 0 bajtu pameti na adrese 00000000
memory_allocator (ud=00001234): alokuji 64 bajtu pameti
memory_allocator (ud=00001234): alokuji 32 bajtu pameti
memory_allocator (ud=00001234): uvolnuji 0 bajtu pameti na adrese 00000000
memory_allocator (ud=00001234): alokuji 64 bajtu pameti
memory_allocator (ud=00001234): alokuji 128 bajtu pameti
memory_allocator (ud=00001234): uvolnuji 0 bajtu pameti na adrese 00000000
memory_allocator (ud=00001234): alokuji 24 bajtu pameti
memory_allocator (ud=00001234): alokuji 27 bajtu pameti
memory_allocator (ud=00001234): alokuji 21 bajtu pameti
memory_allocator (ud=00001234): alokuji 23 bajtu pameti
memory_allocator (ud=00001234): alokuji 21 bajtu pameti
memory_allocator (ud=00001234): alokuji 22 bajtu pameti
memory_allocator (ud=00001234): alokuji 22 bajtu pameti
memory_allocator (ud=00001234): alokuji 22 bajtu pameti
memory_allocator (ud=00001234): alokuji 22 bajtu pameti
memory_allocator (ud=00001234): alokuji 22 bajtu pameti
memory_allocator (ud=00001234): alokuji 22 bajtu pameti
memory_allocator (ud=00001234): alokuji 22 bajtu pameti
memory_allocator (ud=00001234): alokuji 22 bajtu pameti
memory_allocator (ud=00001234): alokuji 21 bajtu pameti
memory_allocator (ud=00001234): alokuji 21 bajtu pameti
memory_allocator (ud=00001234): alokuji 25 bajtu pameti
memory_allocator (ud=00001234): alokuji 23 bajtu pameti
memory_allocator (ud=00001234): alokuji 20 bajtu pameti
memory_allocator (ud=00001234): alokuji 22 bajtu pameti
memory_allocator (ud=00001234): alokuji 19 bajtu pameti
memory_allocator (ud=00001234): alokuji 21 bajtu pameti
memory_allocator (ud=00001234): alokuji 23 bajtu pameti
memory_allocator (ud=00001234): alokuji 20 bajtu pameti
memory_allocator (ud=00001234): alokuji 22 bajtu pameti
memory_allocator (ud=00001234): alokuji 20 bajtu pameti
memory_allocator (ud=00001234): alokuji 25 bajtu pameti
memory_allocator (ud=00001234): alokuji 19 bajtu pameti
memory_allocator (ud=00001234): alokuji 19 bajtu pameti
memory_allocator (ud=00001234): alokuji 22 bajtu pameti
memory_allocator (ud=00001234): alokuji 20 bajtu pameti
memory_allocator (ud=00001234): alokuji 20 bajtu pameti
memory_allocator (ud=00001234): alokuji 19 bajtu pameti
memory_allocator (ud=00001234): alokuji 23 bajtu pameti
memory_allocator (ud=00001234): alokuji 256 bajtu pameti
memory_allocator (ud=00001234): uvolnuji 128 bajtu pameti na adrese 003D27D8
memory_allocator (ud=00001234): alokuji 23 bajtu pameti
memory_allocator (ud=00001234): alokuji 21 bajtu pameti
memory_allocator (ud=00001234): alokuji 21 bajtu pameti
memory_allocator (ud=00001234): alokuji 22 bajtu pameti
memory_allocator (ud=00001234): alokuji 22 bajtu pameti
memory_allocator (ud=00001234): alokuji 34 bajtu pameti
memory_allocator (ud=00001234): alokuji 84 bajtu pameti
memory_allocator (ud=00001234): alokuji 32 bajtu pameti
memory_allocator (ud=00001234): alokuji 76 bajtu pameti
memory_allocator (ud=00001234): alokuji 32 bajtu pameti
memory_allocator (ud=00001234): uvolnuji 0 bajtu pameti na adrese 00000000
memory_allocator (ud=00001234): alokuji 18 bajtu pameti
memory_allocator (ud=00001234): alokuji 32 bajtu pameti
memory_allocator (ud=00001234): alokuji 64 bajtu pameti
memory_allocator (ud=00001234): alokuji 23 bajtu pameti
memory_allocator (ud=00001234): alokuji 64 bajtu pameti
memory_allocator (ud=00001234): uvolnuji 32 bajtu pameti na adrese 003D53D8
memory_allocator (ud=00001234): alokuji 128 bajtu pameti
memory_allocator (ud=00001234): uvolnuji 64 bajtu pameti na adrese 003D5468
memory_allocator (ud=00001234): alokuji 16 bajtu pameti
memory_allocator (ud=00001234): alokuji 16 bajtu pameti
memory_allocator (ud=00001234): alokuji 18 bajtu pameti
memory_allocator (ud=00001234): alokuji 28 bajtu pameti
memory_allocator (ud=00001234): alokuji 256 bajtu pameti
memory_allocator (ud=00001234): uvolnuji 128 bajtu pameti na adrese 003D27D8
memory_allocator (ud=00001234): alokuji 48 bajtu pameti
memory_allocator (ud=00001234): alokuji 28 bajtu pameti
memory_allocator (ud=00001234): alokuji 27 bajtu pameti
memory_allocator (ud=00001234): alokuji 512 bajtu pameti
memory_allocator (ud=00001234): uvolnuji 256 bajtu pameti na adrese 003D5500
memory_allocator (ud=00001234): realokuji 64 bajtu pameti na adrese 003D5400 na delku 128
memory_allocator (ud=00001234): realokuji 16 bajtu pameti na adrese 003D54B0 na delku 32
memory_allocator (ud=00001234): realokuji 16 bajtu pameti na adrese 003D54C8 na delku 32
memory_allocator (ud=00001234): alokuji 18 bajtu pameti
memory_allocator (ud=00001234): realokuji 48 bajtu pameti na adrese 003D5608 na delku 96
memory_allocator (ud=00001234): alokuji 23 bajtu pameti
memory_allocator (ud=00001234): alokuji 18 bajtu pameti
memory_allocator (ud=00001234): realokuji 32 bajtu pameti na adrese 003D5898 na delku 64
memory_allocator (ud=00001234): realokuji 32 bajtu pameti na adrese 003D58C0 na delku 64
memory_allocator (ud=00001234): realokuji 64 bajtu pameti na adrese 003D5400 na delku 56
memory_allocator (ud=00001234): realokuji 64 bajtu pameti na adrese 003D5468 na delku 56
memory_allocator (ud=00001234): realokuji 128 bajtu pameti na adrese 003D27D8 na delku 112
memory_allocator (ud=00001234): uvolnuji 0 bajtu pameti na adrese 00000000
memory_allocator (ud=00001234): realokuji 96 bajtu pameti na adrese 003D5908 na delku 60
memory_allocator (ud=00001234): uvolnuji 0 bajtu pameti na adrese 00000000
memory_allocator (ud=00001234): alokuji 20 bajtu pameti
memory_allocator (ud=00001234): uvolnuji 32 bajtu pameti na adrese 003D5310
memory_allocator (ud=00001234): alokuji 32 bajtu pameti
memory_allocator (ud=00001234): alokuji 30 bajtu pameti
memory_allocator (ud=00001234): alokuji 37 bajtu pameti
memory_allocator (ud=00001234): alokuji 44 bajtu pameti
memory_allocator (ud=00001234): realokuji 32 bajtu pameti na adrese 003D5310 na delku 34
memory_allocator (ud=00001234): alokuji 51 bajtu pameti
memory_allocator (ud=00001234): realokuji 34 bajtu pameti na adrese 003D59E0 na delku 41
memory_allocator (ud=00001234): alokuji 58 bajtu pameti
memory_allocator (ud=00001234): realokuji 41 bajtu pameti na adrese 003D5A50 na delku 48
memory_allocator (ud=00001234): alokuji 65 bajtu pameti
memory_allocator (ud=00001234): realokuji 48 bajtu pameti na adrese 003D5A50 na delku 55
memory_allocator (ud=00001234): alokuji 72 bajtu pameti
memory_allocator (ud=00001234): realokuji 55 bajtu pameti na adrese 003D5B20 na delku 62
memory_allocator (ud=00001234): alokuji 79 bajtu pameti
memory_allocator (ud=00001234): realokuji 62 bajtu pameti na adrese 003D5BB0 na delku 69
memory_allocator (ud=00001234): alokuji 86 bajtu pameti
memory_allocator (ud=00001234): realokuji 69 bajtu pameti na adrese 003D5C50 na delku 76
memory_allocator (ud=00001234): alokuji 93 bajtu pameti
memory_allocator (ud=00001234): realokuji 76 bajtu pameti na adrese 003D5D00 na delku 83
memory_allocator (ud=00001234): alokuji 100 bajtu pameti
memory_allocator (ud=00001234): uvolnuji 20 bajtu pameti na adrese 003D5950
memory_allocator (ud=00001234): uvolnuji 512 bajtu pameti na adrese 003D5690
memory_allocator (ud=00001234): uvolnuji 0 bajtu pameti na adrese 00000000
memory_allocator (ud=00001234): uvolnuji 32 bajtu pameti na adrese 003D5390
memory_allocator (ud=00001234): uvolnuji 56 bajtu pameti na adrese 003D5400
memory_allocator (ud=00001234): uvolnuji 0 bajtu pameti na adrese 00000000
memory_allocator (ud=00001234): uvolnuji 112 bajtu pameti na adrese 003D27D8
memory_allocator (ud=00001234): uvolnuji 56 bajtu pameti na adrese 003D5468
memory_allocator (ud=00001234): uvolnuji 60 bajtu pameti na adrese 003D5908
memory_allocator (ud=00001234): uvolnuji 0 bajtu pameti na adrese 00000000
memory_allocator (ud=00001234): uvolnuji 76 bajtu pameti na adrese 003D5338
memory_allocator (ud=00001234): uvolnuji 64 bajtu pameti na adrese 003D2790
memory_allocator (ud=00001234): uvolnuji 0 bajtu pameti na adrese 00000000
memory_allocator (ud=00001234): uvolnuji 32 bajtu pameti na adrese 003D2768
memory_allocator (ud=00001234): uvolnuji 64 bajtu pameti na adrese 003D2720
memory_allocator (ud=00001234): uvolnuji 0 bajtu pameti na adrese 00000000
memory_allocator (ud=00001234): uvolnuji 32 bajtu pameti na adrese 003D26F8
memory_allocator (ud=00001234): uvolnuji 18 bajtu pameti na adrese 003D5990
memory_allocator (ud=00001234): uvolnuji 84 bajtu pameti na adrese 003D52B0
memory_allocator (ud=00001234): uvolnuji 19 bajtu pameti na adrese 003D4F10
memory_allocator (ud=00001234): uvolnuji 65 bajtu pameti na adrese 003D5AD0
memory_allocator (ud=00001234): uvolnuji 22 bajtu pameti na adrese 003D5260
memory_allocator (ud=00001234): uvolnuji 22 bajtu pameti na adrese 003D4D88
memory_allocator (ud=00001234): uvolnuji 79 bajtu pameti na adrese 003D5BF8
memory_allocator (ud=00001234): uvolnuji 28 bajtu pameti na adrese 003D53D8
memory_allocator (ud=00001234): uvolnuji 18 bajtu pameti na adrese 003D54E0
memory_allocator (ud=00001234): uvolnuji 20 bajtu pameti na adrese 003D5058
memory_allocator (ud=00001234): uvolnuji 22 bajtu pameti na adrese 003D4DE8
memory_allocator (ud=00001234): uvolnuji 93 bajtu pameti na adrese 003D5D58
memory_allocator (ud=00001234): uvolnuji 27 bajtu pameti na adrese 003D2880
memory_allocator (ud=00001234): uvolnuji 21 bajtu pameti na adrese 003D4E68
memory_allocator (ud=00001234): uvolnuji 21 bajtu pameti na adrese 003D5220
memory_allocator (ud=00001234): uvolnuji 22 bajtu pameti na adrese 003D4DC8
memory_allocator (ud=00001234): uvolnuji 44 bajtu pameti na adrese 003D5608
memory_allocator (ud=00001234): uvolnuji 22 bajtu pameti na adrese 003D4E08
memory_allocator (ud=00001234): uvolnuji 100 bajtu pameti na adrese 003D5E20
memory_allocator (ud=00001234): uvolnuji 23 bajtu pameti na adrese 003D51E0
memory_allocator (ud=00001234): uvolnuji 22 bajtu pameti na adrese 003D4E28
memory_allocator (ud=00001234): uvolnuji 21 bajtu pameti na adrese 003D4E48
memory_allocator (ud=00001234): uvolnuji 18 bajtu pameti na adrese 003D53B8
memory_allocator (ud=00001234): uvolnuji 22 bajtu pameti na adrese 003D4DA8
memory_allocator (ud=00001234): uvolnuji 27 bajtu pameti na adrese 003D5668
memory_allocator (ud=00001234): uvolnuji 18 bajtu pameti na adrese 003D58E8
memory_allocator (ud=00001234): uvolnuji 21 bajtu pameti na adrese 003D5200
memory_allocator (ud=00001234): uvolnuji 22 bajtu pameti na adrese 003D5038
memory_allocator (ud=00001234): uvolnuji 23 bajtu pameti na adrese 003D50B8
memory_allocator (ud=00001234): uvolnuji 21 bajtu pameti na adrese 003D4F30
memory_allocator (ud=00001234): uvolnuji 23 bajtu pameti na adrese 003D4D08
memory_allocator (ud=00001234): uvolnuji 21 bajtu pameti na adrese 003D4D28
memory_allocator (ud=00001234): uvolnuji 58 bajtu pameti na adrese 003D5A88
memory_allocator (ud=00001234): uvolnuji 21 bajtu pameti na adrese 003D4CE8
memory_allocator (ud=00001234): uvolnuji 23 bajtu pameti na adrese 003D5970
memory_allocator (ud=00001234): uvolnuji 25 bajtu pameti na adrese 003D4FD0
memory_allocator (ud=00001234): uvolnuji 37 bajtu pameti na adrese 003D59B0
memory_allocator (ud=00001234): uvolnuji 30 bajtu pameti na adrese 003D58C0
memory_allocator (ud=00001234): uvolnuji 20 bajtu pameti na adrese 003D4F70
memory_allocator (ud=00001234): uvolnuji 19 bajtu pameti na adrese 003D5098
memory_allocator (ud=00001234): uvolnuji 28 bajtu pameti na adrese 003D5640
memory_allocator (ud=00001234): uvolnuji 24 bajtu pameti na adrese 003D2860
memory_allocator (ud=00001234): uvolnuji 34 bajtu pameti na adrese 003D5280
memory_allocator (ud=00001234): uvolnuji 23 bajtu pameti na adrese 003D4EB0
memory_allocator (ud=00001234): uvolnuji 72 bajtu pameti na adrese 003D5B60
memory_allocator (ud=00001234): uvolnuji 20 bajtu pameti na adrese 003D5078
memory_allocator (ud=00001234): uvolnuji 25 bajtu pameti na adrese 003D4E88
memory_allocator (ud=00001234): uvolnuji 23 bajtu pameti na adrese 003D4F50
memory_allocator (ud=00001234): uvolnuji 22 bajtu pameti na adrese 003D4F90
memory_allocator (ud=00001234): uvolnuji 19 bajtu pameti na adrese 003D4FF8
memory_allocator (ud=00001234): uvolnuji 20 bajtu pameti na adrese 003D4ED0
memory_allocator (ud=00001234): uvolnuji 22 bajtu pameti na adrese 003D4EF0
memory_allocator (ud=00001234): uvolnuji 22 bajtu pameti na adrese 003D4D48
memory_allocator (ud=00001234): uvolnuji 20 bajtu pameti na adrese 003D4FB0
memory_allocator (ud=00001234): uvolnuji 19 bajtu pameti na adrese 003D5018
memory_allocator (ud=00001234): uvolnuji 86 bajtu pameti na adrese 003D5CA0
memory_allocator (ud=00001234): uvolnuji 22 bajtu pameti na adrese 003D5240
memory_allocator (ud=00001234): uvolnuji 51 bajtu pameti na adrese 003D5A10
memory_allocator (ud=00001234): uvolnuji 22 bajtu pameti na adrese 003D4D68
memory_allocator (ud=00001234): uvolnuji 23 bajtu pameti na adrese 003D5448
memory_allocator (ud=00001234): uvolnuji 256 bajtu pameti na adrese 003D50D8
memory_allocator (ud=00001234): uvolnuji 83 bajtu pameti na adrese 003D5DC0
memory_allocator (ud=00001234): uvolnuji 192 bajtu pameti na adrese 003D2630
memory_allocator (ud=00001234): uvolnuji 720 bajtu pameti na adrese 003D4A10
memory_allocator (ud=00001234): uvolnuji 376 bajtu pameti na adrese 003D24B0 

6. Kontrola parametrů předávaných z Lua skriptu do C

V předchozí části tohoto seriálu jsme si řekli, že při volání céčkové funkce z Lua skriptu se všechny parametry předávají přes zvláštní zásobník, nikoli na standardní rámec (frame) procesu – na něj se ukládá pouze jeden ukazatel. Z toho vyplývá i poměrně velké zabezpečení aplikace, například proti nestandardní manipulaci s rámcem (útoky typu stack overflow). Na druhou stranu je však nutné parametry z výše uvedeného zvláštního zásobníku získat, zkonvertovat na standardní céčkové typy a teprve s nimi dále pracovat. Pro zjištění, kolik parametrů je na zásobník skutečně uloženo, slouží funkce lua_gettop(). Další funkce vypsané v tabulce je možné použít na zjištění typů jednotlivých parametrů – vzhledem k dynamičnosti jazyka Lua totiž není možné typ zjistit při překladu zdrojového textu do bajtkódu, ale až za běhu skriptu (runtime type identification). Všechny funkce určené pro zjištění typu předávané hodnoty mají dva argumenty – stav interpretu a index parametru, jehož typ se má zjistit, přičemž parametry jsou číslovány od jedničky (jedná se o jejich pozici v zásobníku).

Funkce Význam
lua_gettop(lu­a_State L) vrací obsazení zásobníku, tj. počet předaných parametrů
lua_isnumber(lu­a_State *L, int idx) vrací pravdivostní hodnotu dle toho, zda je parametr typu číslo
lua_isstring(lu­a_State *L, int idx) vrací pravdivostní hodnotu dle toho, zda je parametr typu řetězec
lua_iscfuncti­on(lua_State *L, int idx) vrací pravdivostní hodnotu dle toho, zda je parametr typu C-funkce
lua_isuserdata(lu­a_State *L, int idx) vrací pravdivostní hodnotu dle toho, zda je parametr typu uživatelská data
lua_type(lua_State *L, int idx) vrací typ parametru – konstanty typu LUA_T (viz lua.h)
lua_typename(lu­a_State *L, int tp) převádí číslo typu na jeho tisknutelné jméno (lze použít při výpisu chybových hlášení apod.)

7. Konverze parametrů předávaných z Lua skriptu do C

Parametry, se kterými je registrovaná céčková funkce volána z Lua skriptu, je nutné před jejich dalším zpracováním převést na nějaký známý céčkový typ. K tomuto účelu slouží jedno makro a osm funkcí, jež jsou vypsány v tabulce pod tímto odstavcem. Prvním argumentem všech konverzních funkcí i zmíněného makra je ukazatel na strukturu udržující stav interpretu a druhým argumentem je index parametru uloženého na zásobník před zavoláním céčkové funkce, přičemž parametry jsou, jak jsme se již dozvěděli v předchozí kapitole, číslovány od jedničky. Návratová hodnota funkce lua_tonumber() je typu LUA_NUMBER – tento typ je specifikován v hlavičkovém souboru luaconf.h. Změnou typu je možné nastavit, s jakými čísly bude interpret jazyka Lua pracovat. Implicitním číselným typem je double, poměrně jednoduše však lze provést změnu na short int či int (taktéž je nutné změnit konverzní makra lua_number2int a lua_number2in­teger). Funkce lua_tonumber() taktéž dokáže provést i konverzi řetězce na číselnou hodnotu, samozřejmě za předpokladu, že v řetězci jsou uloženy znaky představující platné cifry, popř. znaménko či desetinný oddělovač.

Funkce Význam
lua_tonumber(lu­a_State *L, int idx) převod parametru na číslo (typ záleží na nastavení luaconf.h)
lua_tointeger(lu­a_State *L, int idx) převod parametru na integer (celé číslo se znaménkem)
lua_toboolean(lu­a_State *L, int idx) převod parametru na pravdivostní hodnotu, při konverzi platí všechna pravidla jazyka Lua
lua_tolstring(lu­a_State *L, int idx, size_t *len) převod parametru na řetězec, naplní se i délka řetězce (místo platného ukazatele může být poslední parametr roven NULL)
lua_tostring(lu­a_State *L, int idx) převod parametru na řetězec (jedná se o makro, nikoli funkci)
lua_tocfuncti­on(lua_State *L, int idx) převod parametru na céčkovou funkci (ukazatel na tuto funkci)
lua_touserdata(lu­a_State *L, int idx) převod parametru na uživatelská data
lua_tothread(lu­a_State *L, int idx) převod parametru na vlákno jazyka Lua
lua_topointer(lu­a_State *L, int idx) převod parametru na generický céčkový pointer (void *)

root_podpora

8. Zdrojové kódy všech demonstračních příkladů i testovacích skriptů

V následující tabulce jsou uloženy odkazy na zdrojové kódy všech tří demonstračních příkladů popsaných v předchozích kapitolách. Taktéž jsou zde uvedeny výsledky běhu demonstračních příkladů (jedná se o standardní i chybový výstup přesměrovaný do souboru po převodu tabulátorů na mezery).

9. Odkazy na Internetu

  1. Programming in Lua (first edition)
    http://www.lu­a.org/pil/index­.html
  2. Lua home page
    http://www.lu­a.org/
  3. Lua: vestavitelný minimalista
    /clanky/lua-vestavitelny-minimalista/
  4. Lua
    http://www.li­nuxexpres.cz/pra­xe/lua
  5. CZ Wikipedia: Lua
    http://cs.wiki­pedia.org/wiki/Lua
  6. EN Wikipedia: Lua (programming language)
    http://en.wiki­pedia.org/wiki/Lu­a_(programmin­g_language)
  7. The Lua Programming Language
    http://www.ti­obe.com/index­.php/paperinfo/tpci/Lu­a.html
  8. Lua Programming Gems
    http://www.lu­a.org/gems/
  9. LuaForge
    http://luafor­ge.net/
  10. Forge project tree
    http://luafor­ge.net/softwa­remap/trove_lis­t.php

Byl pro vás článek přínosný?

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.