Con questo articolo tocchiamo il culmine di Lua. Scopriremo come implementare il paradigma ad oggetti in un linguaggio non Object Oriented come Lua. Scopriremo quindi come questo paradigma manuale lasci una personalizzazione enorme nel comportamento degli oggetti e nella definizione delle classi.

Continue reading »

 

Lo sviluppo di Lua procede lento ma costante. Ieri è stata infatti rilasciata la Beta di Lua 5.2. La nuova versione del linguaggio introduce, come è solito, grandi cambiamenti e una manciata di incompatibilità con le precedenti versioni. Vediamone alcune (la lista completa la trovate qui)

Continue reading »

 

Nulla è più importante di quello che è racchiuso nelle parentesi graffe. Se dovessi scegliere una frase per descrivere Lua sceglierei proprio questa. Volevo parlarvi di come utilizzare il paradigma a oggetti con Lua ma, rileggendo vari appunti, mi sono accorto che non posso assolutamente tralasciare una parte vitale del linguaggio: le tabelle.

Se in Lisp il tipo di dato base del linguaggio è la lista, se in Python è l’oggetto in Lua è la tabella. Non esiste nessun’altra struttura dati in Lua all’infuori della tabella: niente array, niente vettori, niente liste, solo ed esclusivamente tabelle.

Continue reading »

 

Mi hanno fatto una domanda nei commenti molto interessante: come posso sfruttare Lua con del codice Fortran? Bene, ho trovato una soluzione piuttosto semplice ed interessante per cui ho deciso di scriverci sopra un bel post.

Per prima cosa sappiate che non c’è un vero modo per collegare Lua a Fortran direttamente. Lua ha la sua libreria in C, pensata per il C e funziona per il C. Anche cercando in giro non ho trovato nulla che permetta il collegamento diretto. Tuttavia il Fortran e il C/C++ sono collegabili nativamente! Per questo motivo il procedimento si sintetizza in due punti

  • Collegare le funzioni Fortran alle funzioni C++
  • Collegare Lua alle funzioni C++

In questo modo il C++ assume il ruolo di collante fra Fortran e Lua e tutto funziona a meraviglia ma non solo: nonostante l’approccio a tre vie l’implementazione è formidabilmente trasparente e intuitiva!

Poiché sappiamo già come collegare Lua al C++ comincerò a far vedere come collegare Fortran a C++. Cominciamo con il codice C++ di esempio.

#include <iostream>

using namespace std;

extern "C" {
  void ffunction_(float *a, float *b);
}

int main()
{
    float a = 0;
    float b = 0;
   
    ffunction_(&a,&b);
   
    cout << a << " " << b << endl;
}

Il programma è stupido, lo so, ma rende l’idea. In pratica lasciamo che la funzione ffunction_ vada a modificare i valori delle due variabili a e b e poi stampiamo i nuovi valori.

Ma cosa fa ffunction_? Questa è la nostra funzione Fortran e nel codice C++ dobbiamo solo assicurarci che sia definita come extern. Il codice di questa funzione è semplice ed è salvato nel file ffunction.f.

      subroutine ffunction(a,b)
      a=3.0
      b=4.0
      end

Ora come compiliamo? Semplicissimo.

  • Prima compiliamo il codice C++ con g++ esempio.cpp -c ottenendo il file esempio.o
  • Poi compiliamo il codice fortran con gfortran ffunction.f -c ottenendo il file ffunction.o
  • Infine uniamo il tutto con g++ -o esempio esempio.o ffunction.o ottenendo l’eseguibile esempio

Niente di più semplice. In pratica, salvo definire le funzioni fortran come extern e l’uso di un compilatore diverso, l’integrazione fra fortran e C++ è praticamente trasparente!

A questo punto collegare Lua al Fortran è praticamente IDENTICO a collegare Lua al C++. Le nostre funzioni LuaGlue non faranno altro che chiamare funzioni Fortran come se stessero chiamando una funzione C++.

Supponiamo di voler chiamare una funzione Fortran che calcoli il fattoriale di un numero come nel caso di esempio di un articolo precedente:

LuaGlue _FortranFactorial(struct lua_State *pLuaState)
{
     double n;
     n = pLua->GetNumberArgument(1);
     
     /* Calcoliamo il fattoriale */
     int res = fortran_factorial_recursive_(n);
     
     /* Restituiamo il valore di ritorno */
     pLua->PushNumber(res);
     
     return 1;
}

Il codice è identico salvo che fortran_factorial_recursive_ questa volta non è una funzione C ma una funzione Fortran definita nel modo che abbiamo visto in precedenza.

E se siete abilissimi programmatori Fortran ma non sapete nulla di C++? Non credo sia un grosso problema, in questo approccio tutta la programmazione è eseguita in Fortran e Lua. Di C++ è necessario sapere proprio le basi e l’uso di LuaGrip per agganciare le funzioni a Lua, nulla più. Una piccola scocciatura e un piccolo sforzo iniziale in più per avere un integrazione Lua/Fortran praticamente perfetta.

 

Qualunque programma che si rispetti, e a maggior ragione un gioco, ha necessità di salvare dati in modo tale da recuperarli negli avvii successivi. Il primo metodo che viene in mente consiste nello scrivere i dati in un file che, in fase di caricamento, verrà passato ad un parser per recuperare le informazioni.

Scrivere un parser però è piuttosto impegnativo e necessita di un bel po’ di lavoro. Ma pensiamoci bene. Noi un parser già pronto ce l’abbiamo: LUA! Esattamente! Sfruttiamo Lua come parser per recuperare le informazioni!

-- Base Lua Script for Load/Save

-- Default Value

nome = "NONE"
visite = 0

-- Check if one file exists or not
function file_exists(name)
   local f=io.open(name,"r")
   if f~=nil then io.close(f) return true else return false end
end

function SaveData()
    myfile = io.open("savegame.lua", "w")
    if myfile ~= nil then
        myfile:write("-- Test Lua SaveGame file")
        myfile:write(string.char(10))
        myfile:write(string.char(10))
        myfile:write(string.format("%s%s", "-- File created on: ", os.date()))
        myfile:write(string.char(10))
        myfile:write(string.char(10))
        myfile:write(string.format("%s%s%s", "nome = \"",nome,"\""))
        myfile:write(string.char(10))
        myfile:write(string.format("%s%d", "visite = ",visite))
        myfile:write(string.char(10))
        io.close(myfile)
    end
end
       
function LoadData()
    if file_exists("savegame.lua") then
        dofile("savegame.lua")
    end
end

function Saluti()
    if visite == 0 then
        print("Ciao! E' la prima volta che avvii il programma!")
        print("Dimmi il tuo nome:")
        nome = io.stdin:read()
        visite = 1
    else
        tmp = string.format("%s%s%s","Ciao ", nome, "! Sei tornato!")
        print(tmp)
        tmp = string.format("%s%d", "Il tuo numero di visite e' ", visite)
        print(tmp)
        visite = visite + 1
    end
end

LoadData()
Saluti()
SaveData()

Diamo un occhiata al listato precedente. Cominciamo dalla funzione Saluti. Questa funzione rappresenta ciò che fa il programma: ti saluta e ti ricorda il numero di volte che lo hai avviato. Ovviamente, se è la prima volta che il programma viene avviato vi verrà richiesto il nome.

Ma tutto questo non ci interessa molto. Passiamo al cuore: la funzione SaveData(). Questa funzione è sicuramente la funzione più interessante. Come potete vedere il codice crea un file e lo riempie con del codice Lua valido che rappresenta i dati da salvare. Nel mio caso, ad esempio, il file savegame.lua contiene

-- Test Lua SaveGame file

-- File created on: Thu Jun 23 21:21:08 2011

nome = "Davide Aversa"
visite = 7

In questo semplice caso, in cui le uniche cose da salvare sono il nome e il numero di visite il file darà di sole 4 righe (commenti compresi) ma questo discorso vale anche in generale e per salvataggi di complessità arbitraria! La funzione SaveGame è solitamente lunga e complessa ma piuttosto facile da gestire anche per salvataggi complicati.

Il vantaggio di questa tecnica appare chiarissimo non appena leggiamo la funzione LoadData. Due righe (in realtà basterebbe solo dofile). Non è stupendo? Abbiamo sostituito righe e righe di parser con un solo comando sfruttando il linguaggio stesso: un chiaro esempio di meta-programmazione applicata ad un operazione fondamentale come il salvataggio e la scrittura dei file.

Quando Lua esegue il file savegame automaticamente ingloba tutte le funzioni contenute nel file e, inoltre, questo formato permette agli sviluppatori di agire facilmente sul salvataggio in modo tale da “creare” manualmente salvataggi di situazioni particolari a scopo di debug!

Questo è uno dei metodi di salvataggio dati più facile, efficiente e veloce che io conosca.

Potete provare il programma in questione semplicemente dando

lua loadsave.lua

Buon divertimento e buon salvataggio.

 

A parte il nome esotico, volevo presentarvi Lua’s Totally Awesome Tutorial Pack xD

Un modo esagerato e divertente per dirvi che ho raccolto tutto il codice degli scorsi tutorial su Lua e C++ in un comodo file tar.gz. All’interno trovate anche l’ultimissima versione di LuaGrip.

DOWNLOAD

Per prima cosa dovete installare LuaGrip. Per fare questo basta dare i comandi di rito:

mkdir build
cd build
cmake ..
make
sudo make install

A questo punto potete compilare ogni file nel tutorial con g++ aggiungendo i dovuti flag. Se non conoscete i flag vi basta aggiungere in coda:

`pkg-config --cflags libluagrip` `pkg-config --cflags lua5.1` `pkg-config --libs libluagrip` `pkg-config --libs lua5.1`

Non proprio una meraviglia di lettura ma dovrebbe fare decentemente il suo lavoro. :D Magari date i comandi singolarmente e vi segnate i flag in modo da non dover recuperare questa stringa infinita di byte ogni volta che dovete compilare un programma con LuaGrip.

I tutorial funzionano anche su Windows ma non so come compilarli dato che non uso Windows da un lustro.

Fatemi sapere se ci sono problemi.

 

Prima cosa. Così la programmazione event-driven? Risposta semplice: un paradigma di programmazione in cui parti di codice vengono avviare dal lancio di eventi. Risposta ancora più semplice (per gli esperti: perdonatemi la banalizzazione). Immaginate tre persone in una casa. Immaginate che queste persone, dotate di genitori nerd molto crudeli, si chiamino Stampa, AggiungiUno e, ovviamente, Main. Main, il leader del gruppo, vuole contare tutte le macchine rosse che passano davanti la finestra.

Main ha due possibilità: andare a chiamare personalmente AggiungiUno per chiedergli di aumentare un contatore e Stampa per avere il risultato, oppure urlare “MACCHINA ROSSA” in modo tale che chi di dovere capisca che è passata una macchina rossa e aggiunga il contatore.

In questo scenario i due metodi sono pressocché equivalenti, ma supponiamo che Main non conosca il nome di chi è nella casa oppure che ad un certo punto AggiungiUno si faccia sostituire da IncrementaUno. In questo caso sarebbe impossibile programmare Main secondo il primo approccio mentre sarebbe banale programmare Main in modo da gridare MACCHINA ROSSA assumendo che ci sia qualcuno nella casa in grado di capire il significato di quel grido.

La programmazione event-driven consiste proprio nel gridare MACCHINA ROSSA. Ovviamente in programmazione non c’è il suono, sta a noi programmare il mezzo in cui gli eventi vengono propagati.

Senza entrare nel dettaglio vediamo come implementare i Lua e C++ un semplice meccanismo a eventi. Gli eventi saranno lanciati da C++ e verranno raccolti da una funzione Lua.

-- Esempio di programmazione Event-Driven
-- in Lua.

MACCHINA_ROSSA = 1000
STAMPA = 2000

numero_macchine_rosse = 0

RegisterEventHandler("EventHandler")

function EventHandler(id)
    if id == MACCHINA_ROSSA then
    print("Macchine +1")
        numero_macchine_rosse = numero_macchine_rosse + 1
    elseif id == STAMPA then
        print(string.format("%s%d", "Num. Macchine: ", numero_macchine_rosse))
    end
end

Cominciamo con lo script Lua.

Le prime due righe associano solo l’indice dell’evento che vogliamo ad una variabile mnemonica. Nulla di particolare, serve solo a rendere il codice più leggibile e chiaro.

La funzione RegisterEventHandler è importante. È una funzione LuaGlue, ovvero una funzione implementata in C++. Questa funzione comunica alla parte C++ quale è il nome della funzione che gestisce gli eventi. La funzione non è in teoria strettamente necessaria, si potrebbe, ad esempio, scrivere direttamente nel codice C++ che la funzione che gestisce gli eventi è EventHandler. Tuttavia in questo modo renderemmo il codice C++ dipendente dal codice Lua: se cambiamo il nome della funzione o se vogliamo cambiarla in run-time dobbiamo riscrivere anche il codice C++. Usando RegisterEventHandler invece le due cose sono completamente disaccoppiate.

Vediamo ora la funzione principe. EventHandler è il mezzo tramite cui gli eventi si propagano ed ha un comportamento molto semplice: riceve in ingresso l’id dell’evento e lo smista verso la funzione idonea. Nel nostro caso, poiché le funzioni sono piuttosto semplici sono scritte direttamente in EventHandler.

Ora vediamo il listato C++.

#include<iostream>
#include<luagrip.hpp>
#include<string>

#define MACCHINA_ROSSA 1000
#define STAMPA 2000

std::string strEventHandler = "";
LuaGrip* pLua;

LuaGlue _RegisterEventHandler(lua_State *L)
{
    strEventHandler = pLua->GetStringArgument(1,"");
   
    return 1;
}

void FireEvent(int id)
{
    if (strEventHandler != "")
    {
        char buf[254];
        sprintf(buf, "%s(%d)", strEventHandler.c_str(), id);
        pLua->RunString(buf);
    }
}

int main()
{
    pLua = new LuaGrip();
    pLua->AddFunction("RegisterEventHandler",_RegisterEventHandler);
    pLua->RunScript("event.lua"); // Carica il modulo Lua.
   
    FireEvent(MACCHINA_ROSSA);
    FireEvent(MACCHINA_ROSSA);
    FireEvent(STAMPA);
    FireEvent(MACCHINA_ROSSA);
    FireEvent(STAMPA);
   
    delete pLua;
}

Il codice è composto da tre funzioni.

La prima è _RegisterEventHandler. Abbiamo già parlato di questa funzione che si limita a ricevere il nome della funzione incaricata di gestire gli eventi e di memorizzarlo in una variabile globale.

La seconda è FireEvent. Come possiamo intuire dal nome questa funzione lancia gli eventi (nel nostro esempio corrisponde all’azione di gridare). In che modo? Ci sono molti modi diversi, il più semplice dei quali consiste nel creare una stringa che corrisponde ad una chiamata di funzione Lua e poi eseguirla nel solito modo.

Infine la funzione main inizializza Lua e lancia gli eventi. La sequenza descritta in main da luogo al seguente output:

Macchine +1
Macchine +1
Num. Macchine: 2
Macchine +1
Num. Macchine: 3

Questa struttura è la base del meccanismo event-driven. Ovviamente la struttura può essere ulteriormente complicata, ad esempio permettendo l’uso di eventi con argomenti (ad esempio un evento IncrementaN).

La prossima volta vorrei farvi vedere invece come sfruttare Lua per il salvataggio e il caricamento dei dati.

 

Aldrin Apollo 11

Abbiamo visto negli articoli precedenti come utilizzare funzioni C++ all’interno dei script Lua (e viceversa). Ci eravamo però dimenticati di dire come fare a passare dati sottoforma di parametri fra Lua e C++. Ora è giunto il momento di colmare questa lacuna.

Il passaggio di parametri fra funzioni Lua e C++ è, come potete immaginare, un aspetto fondamentale della programmazione mista Lua/C++. Tuttavia, nonostante la procedura non sia delle più intuitive, è estremamente semplice. Ma partiamo da un esempio, consideriamo il seguente script Lua.

-- Questo è un esempio di script Lua che
-- utilizza al suo interno una funzione C++.

print("Fattoriale di 5!")
print("Implementato in C++ con amore!")
print(Factorial(5))

Un caso d’uso dei più banali consiste nell’usare all’interno di uno script Lua funzioni C++ per le operazioni CPU-intensive. Possiamo trovare un esempio spicciolo nel listato Lua precedente. La funzione Factorial per il calcolo del fattoriale è notoriamente una funzione che necessita di una buona velocità. Così, invece di implementarla in Lua useremo una funzione C++. Vediamo quindi il codice C++.

LuaGrip* pLua;

int factorial_recursive(int n)
{
    if (n==1) return 1;
    return n * factorial_recursive(n-1);
}

LuaGlue _Factorial(struct lua_State *pLuaState)
{
    /* Ricaviamo i parametri della funzione.
     *
     * La funzione Lua agganciata a questa funzione C++
     * ha la seguente struttura:
     *
     *     int Factorial(int n)
     *
     * Dobbiamo quindi recuperare il primo int.
     */

     double n;
     n = pLua->GetNumberArgument(1);
     
     /* Calcoliamo il fattoriale */
     int res = factorial_recursive(n);
     
     /* Restituiamo il valore di ritorno */
     pLua->PushNumber(res);
     
     return 1;
}

La funzione _Factorial è la classica funzione LuaGlue. La domanda viene spontanea: come fare a passare il valore 5 alla funzione Factorial? E come fare a restituire il valore finale? Andiamo per passi:

La prima cosa che facciamo nella funzione _Factorial è proprio recuperare il parametro 5. Facciamo questo tramite la funzione LuaGrip double GetNumberArgument(int argnum). La funzione prende in ingresso il numero del parametro da recuperare, nel nostro caso abbiamo bisogno del primo parametro e quindi usiamo semplicemente il numero 1. Come valore di ritorno la funzione restituisce un double.

A questo punto passiamo il valore recuperato ad una funzione “tradizionale” factorial_recursive che calcola il valore del fattoriale nel modo “classico”.

Infine, il valore di ritorno viene restituito a Lua grazie alla funzione LuaGrip void PushNumber(res).

Mi raccomando non dimenticate di ritornare un 1 alla fine altrimenti Lua interpreterà il mancato valore di ritorno come un errore nella funzione.

A questo punto scriviamo il main che, in questo caso, non farà altro che agganciare la funzione Factorial a Lua e lanciare lo script.

int main()
{
    pLua = new LuaGrip();
    pLua->AddFunction("Factorial",_Factorial);
    pLua->RunScript("fac.lua");
    delete pLua;
}

A questo punto l’integrazione è completa! Non c’è molto altro da sapere :)

Tuttavia continuerò a presentare esempi. Nel prossimo articolo su Lua/C++ ho intenzione di parlare su come utilizzare Lua per la programmazione event-driven.

 

Full Moon Space Art

Poiché l’argomento Lua e C++ è di notevole interesse per me ed ha suscitato un discreto numero di contatti mi sento in dovere di approfondirlo. La cosa necessiterebbe di un mare di tempo e spazio in quanto si potrebbero scrivere libri e libri sull’argomento (e infatti ce ne sono migliaia) ma cercherò di concentrarmi sugli aspetti pratici e implementativi.

Per ogni esempio che farò ora e per i secoli a venire farò sempre riferimento alla mia libreria LuaGrip (che potete scaricare qui). Questa libreria ha lo scopo di nascondere e avvolgere il collegamento di basso livello con la libreria nativa di Lua. Ho fatto questo per due motivi:

  • Semplificare e abbreviare le operazioni ripetitive del collegamento c++/lua come ad esempio la creazione dell’ambiente lua e la sua distruzione.
  • La libreria di Lua è scritta in C e la sua integrazione in C++ richiede un paio di accortezze che magari risulterebbero di difficile comprensione a chi non è espertissimo. Usando LuaGrip potete fregarvene del tutto e potete utilizzarlo tranquillamente anche se siete apprendisti.

La libreria è direttamente ispirata (dico ispirata per dire che le API sono copiate pari pari) ad una libreria descritta nel libro “Game Developement with Lua” di Paul Schuytema e Mark Manyen. Ho dovuto però reimplementare tutto per rendere l’interfaccia il più generica possibile e per aggiornare il codice alla nuova versione di lua (5.1).

Detto questo, la prima cosa da fare è studiarci per bene il programma di esempio che è incluso nel pacchetto:

#include <stdio.h>
#include <string.h>
#include "luagrip.hpp"

Per prima cosa che vediamo sono i consueti include. Fin qui non c’è nulla di particolare.

LuaGlue _Version(lua_State *L)
{
    puts("This is Version 2.0 of the Console Program");
    return 0;
}

La prima funzione che troviamo è la funzione _Version. Questa funzione è una funzione LuaGlue ovvero una funzione implementata in C++ che sarà richiamabile da uno script Lua. La funzione ha i seguenti particolari:

  • Il valore di ritorno è LuaGlue. Consideratelo semplicemente un valore int.
  • Il nome comincia con un underscore (_). Questa è solo una convenzione stilistica.
  • Come unico parametro in ingresso abbiamo un puntatore a lua_State.

Tutto questo sembra estremamente limitante ma così non è. Vedremo poi come sia possibile sfruttare il puntatore allo stato (o semplicemente la classe LuaGrip) per avere in ingresso qualsiasi parametro e restituire qualunque valore.

char gpCommandBuffer[254];

const char *GetCommand(void)
{
    printf("Ready> ");
    return fgets(gpCommandBuffer,254,stdin);
}

Questa è una semplice funzione ausiliaria, non ha nessun collegamento con lua. Questa funzione non fa altro che leggere una stringa dalla shell e memorizzarla nel buffer gpCommandBuffer.

int main()
{
    LuaGrip *pLua = new LuaGrip;
    pLua->AddFunction("Version",_Version);
   
    pLua->RunScript("hello.lua");
   
    const char *pCommand = GetCommand();
    while(strcmp(pCommand, "QUIT\n") != 0)
    {
        if(!pLua->RunString(pCommand))
        {
            printf("ERROR:%s\n",pLua->GetErrorString());
        }
        pCommand = GetCommand();
    }
   
    delete pLua;
}

Ora veniamo al sodo: la funzione main. La prima cosa che facciamo è creare un istanza della classe LuaGrip che inizializza la macchina virtuale di Lua.

Subito dopo agganciamo la funzione _Version all’interprete Lua. In pratica diciamo alla macchina virtuale di Lua di agganciare la funzione Lua Version alla funzione C _Version. Da questo momento in poi possiamo usare la funzione Version() all’interno di uno script Lua come se fosse un qualsiasi altro comando Lua.

Subito dopo eseguiamo lo script hello.lua, script che non fa altro che stampare a schermo una breve introduzione per la nostra console.

A questo punto inizia il ciclo della shell. Leggiamo i comandi inviati dall’utente fino a quando non viene digitato il comando QUIT. I comandi di nostro interesse sono:

  • pLua->RunString(pCommand) – Analogamente al comando RunScript, il quale esegue uno script contenuto in un file, questo comando esegue comandi Lua contenuti in una stringa.
  • pLua->GetErrorString() – Questo comando restituisce la stringa di errore generata dall’interprete.

Alla fine non ci resta che eliminare l’istanza di LuaGrip per terminare la macchina virtuale.

Ora siamo pronti a vedere come passare valori e parametri fra C++ e Lua. Ma questo ve lo racconto un’altra volta.

Jun 022011
 

Cos’è Lua?

Lua (parola portoghese per luna) è un linguaggio di scripting dinamico, riflessivo e procedurale di origine brasiliana usato massivamente nell’industria dei videogiochi.

Cosa ci fa l’industria dei videogame con Lua? Non usano il c++?

Si. Programmi con altissime richieste prestazionali come i videogiochi non possono fare a meno di utilizzare linguaggi compilati di medio-basso livello come il c++. Tuttavia programmare in c++ è sicuramente più scomodo che programmare in un linguaggio di alto livello e, inoltre, non tutte le parti di un software necessitano della stessa ferrea richiesta di performance. Sarebbe come voler costruire una casa resistente intagliandola in un mastodontico blocco di marmo. :) La prassi consiste quindi nello scrivere grossi mattoni in C++ puro e di legarli insieme con una calce fatta con un linguaggio di scripting.

Nell’industria dei videogiochi da molto tempo si utilizza una calce fatta con Lua.

Lua? Perché usare un linguaggio che non ho mai sentito quando si può usare un linguaggio noto come Python, Ruby o altro?

Nulla vi vieta di usarli. Tuttavia ci sono tre grandi motivi che rendono ancora Lua un leader nel suo campo. Primo, Lua nasce per essere integrato in codice compilato, integrazione che quindi risulta semplice ed efficace. Secondo, a differenza degli altri linguaggi di scripting Lua è decisamente spartano, non possiede nemmeno il paradigma a oggetti (che però può essere replicato in modo personalizzato usando le strutture dati native di Lua); questa essenzialità rende Lua performante. Terzo, e più importante, Lua è estremamente versatile per quanto riguarda il multithreading (permette, ad esempio, di avviare un interprete separato per ogni thread).

Detto questo potete comunque utilizzare nel vostro progetto qualunque linguaggio di scripting, ma almeno saprete cosa ha Lua in più o in meno (e potrete così fare una scelta consapevole).

Figo! Come posso integrare Lua in un mio progetto?

Ho scritto un wrapper in C++ per semplificare ulteriormente l’integrazione di Lua nel vostro progetto.

#ifndef H_LUAGRIP
#define H_LUAGRIP

extern "C" {
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}

struct lua_State;

#define LuaGlue extern "C" int

extern "C" {
    typedef int (*LuaFunctionType)(struct lua_State *pLuaState);
};

class LuaGrip
{
    public:
        /**
         * Constructor.
         */

        LuaGrip();
        virtual ~LuaGrip();
       
        /**
         * Execute Lua script indicated by pFilename.
         *
         * @param pFilename [in] Script's path.
         * @return false on error. true otherwise.
         */

        bool RunScript(const char *pFilename);
       
        /**
         * Execute one string.
         *
         * @param pCommand [in] String representing a Lua command.
         * @return false on error. true otherwise.
         */

        bool RunString(const char *pCommand);
       
        /**
         * Get a string representing the last occurred error.
         *
         * @return ...
         */

        const char *GetErrorString();
       
        /**
         * Link a C/C++ function to Lua interpeter.
         *
         * @param pFunctionName [in] String of function name.
         * @param pfunction [in] Pointer to a function you are going to link.
         * @return  false on error. true otherwise.
         */

        bool AddFunction(const char *pFunctionName, LuaFunctionType pFunction);
       
        /**
         *
         */

        const char *GetStringArgument(int num, const char *pDefault=NULL);
       
        /**
         *
         */

        double GetNumberArgument(int num, double dDefault=0.0);
       
        void PopLast();
       
        void PushString(const char *pString);
        void PushNumber(double value);
   
    private:
        lua_State *m_State;
};

#endif /* H_LUAGRIP */
#include "luagrip.hpp"
#include <string.h>

LuaGrip::LuaGrip()
{
    this->m_State = luaL_newstate();
    luaL_openlibs(this->m_State);
}

LuaGrip::~LuaGrip()
{
    lua_close(this->m_State);
}

bool LuaGrip::AddFunction(const char *pFunctionName, LuaFunctionType pFunction)
{
    lua_register(this->m_State,pFunctionName,pFunction);
    return true;
}

bool LuaGrip::RunScript(const char *pFilename)
{
    if (!luaL_loadfile(this->m_State, pFilename))
    {
        if (!lua_pcall(this->m_State, 0, LUA_MULTRET, 0))
        {
            return false;
        }
    } else return false;
}

bool LuaGrip::RunString(const char *pCommand)
{
    if (luaL_loadbuffer(this->m_State, pCommand, strlen(pCommand), "line") == 0)
    {
        if (lua_pcall(this->m_State, 0, 0, 0) != 0)
        {
            return false;
        }
    } else return false;

    return true;
}

const char *LuaGrip::GetErrorString(void)
{
    return lua_tostring(this->m_State, -1);
}

const char *LuaGrip::GetStringArgument(int num, const char *pDefault)
{
    return luaL_optstring(this->m_State, num, pDefault);

}

double LuaGrip::GetNumberArgument(int num, double dDefault)
{
    return luaL_optnumber(this->m_State, num, dDefault);
}

void LuaGrip::PushString(const char *pString)
{
    lua_pushstring(this->m_State, pString);
}

void LuaGrip::PushNumber(double value)
{
    lua_pushnumber(this->m_State, value);
}

void LuaGrip::PopLast()
{
    lua_pop(this->m_State,1);
}

Sono due file (uno .hpp e l’altro .cpp) che vi basta aggiungere al vostro progetto ed utilizzarli come una classe qualsiasi.

Ottimo! Un esempio?

Ovviamente. Questa è una piccola “shell” per Lua.

#include <stdio.h>
#include <string.h>
#include "luagrip.hpp"

extern "C" int _Version(lua_State *L)
{
    puts("This is Version 2.0 of the Console Program");
    return 0;
}

char gpCommandBuffer[254];

const char *GetCommand(void)
{
    printf("Ready> ");
    return fgets(gpCommandBuffer,254,stdin);
    //return gets(gpCommandBuffer);
}

int main()
{
    LuaGrip *pLua = new LuaGrip;
    pLua->AddFunction("Version",_Version);
   
    pLua->RunScript("hello.lua");
   
    const char *pCommand = GetCommand();
    while(strcmp(pCommand, "QUIT\n") != 0)
    {
        if(!pLua->RunString(pCommand))
        {
            printf("ERROR:%s\n",pLua->GetErrorString());
            pLua->PopLast();
        }
        pCommand = GetCommand();
    }
   
    delete pLua;
}

Hello.lua non è altro che un “Hello World” scritto in Lua: print("Hello World!")

Se volete vi darò qualche ulteriore dettaglio sull’uso di Lua in un prossimo articolo. Fatemi sapere.

© 2008-2012 SlashCode Suffusion theme by Sayontan Sinha