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.
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)
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.
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.
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.
a=3.0
b=4.0
end
Ora come compiliamo? Semplicissimo.
- Prima compiliamo il codice C++ con
g++ esempio.cpp -cottenendo il fileesempio.o - Poi compiliamo il codice fortran con
gfortran ffunction.f -cottenendo il fileffunction.o - Infine uniamo il tutto con
g++ -o esempio esempio.o ffunction.oottenendo l’eseguibileesempio
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:
{
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!
-- 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
-- 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
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.
Per prima cosa dovete installare LuaGrip. Per fare questo basta dare i comandi di rito:
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:
Non proprio una meraviglia di lettura ma dovrebbe fare decentemente il suo lavoro.
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.
-- 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<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
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.
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.
-- 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++.
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.
{
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.
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 <string.h>
#include "luagrip.hpp"
Per prima cosa che vediamo sono i consueti include. Fin qui non c’è nulla di particolare.
{
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.
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.
{
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 comandoRunScript, 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.
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.
#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 <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 <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.








Recent Comments