Approfondiamo Lua

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.

Comments are closed.