Salve a tutti e ben tornati dalle vacanze!Per riprendere un po’ il ritmo con il blog vi propongo dei mini-post che rispondono velocemente ai più comuni problemi di compilazione ed esecuzione di programmi.

Il primo problema che risolveremo è il seguente

error while loading shared libraries: libXXX.so: cannot open shared object file: No such file or directory

A chi non è capitato almeno una volta nella vita?

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.

 

Molti conosceranno sicuramente le Librerie QT: esse sono infatti il principale tool-kit grafico della suite desktop KDE nonché di molti altre applicazioni e sistemi embedded. In quanto tali, quando si parla di Qt, molti pensano ad esse solamente come mere librerie grafiche, in realtà, tali librerie vanno ben oltre la semplice gestione e visualizzazione di finestre e widget: esse sono infatti una libreria che racchiude un gran numero di layer che semplificano lo sviluppo software in generale. In questo breve articolo infatti mostrerò uno dei tanti modi in cui l’uso delle librerie può Qt semplificare la vita agli sviluppatori: il meccanismo Meta-Object System (moc) e l’accoppiata signals e slots.
Segnali (signals) e slot (slots) sono due elementi fondamentali della programmazione con le Qt. Essi permettono di legare insieme due oggetti senza che nessuno dei due sappia nulla dell’altro. È possibile, ad esempio, costruire due classi A e B e fare in modo che ad un evento di A corrisponda un azione di B e viceversa senza doversi minimamente preoccupare di inserire in A riferimenti di B (e rispettivamente in B riferimenti ad A).
Un segnale è infatti un avviso emesso da un oggetto al verificarsi di determinate condizioni. Ad ogni segnale è collegato uno slot che non è nulla di diverso da una comune funzione C/C++: non appena è emesso il segnale X viene automaticamente eseguita la funzione Y.
Per collegare un segnale ad uno slot si usa il comando

connect(sendre, SIGNAL(signal), receiver, SLOT(slot));

dove sender e receiver sono puntatori a due oggetti (oggetti “qt” per la precisione) e signal e slot non sono altro che dichiarazioni di funzioni senza il nome dei parametri. Possiamo avere varie forme di connessioni:
  • PIÙ SEGNALI COLLEGATI AD UN SOLO SLOT. In questo caso la stessa funzione viene lanciata in corrispondenza di due o più segnali differenti.
  • UN SOLO SEGNALE COLLEGATO A PIÙ SLOT. Viceversa, in questo caso, l’arrivo di un segnale avvia l’esecuzione di più funzioni. Tali funzioni vengono eseguite una dopo l’altra in un ordine indefinito (fate quindi molta attenzione alla sincronizzazione di tali funzioni).
  • UN SEGNALE COLLEGATO AD UN SEGNALE. In questo caso l’arrivo di un segnale causa semplicemente il lancio di un nuovo segnale (che può essere la propagazione del primo o un segnale del tutto diverso).

“Fantastico!” direte voi, ma cosa è necessario per poter usufruire di tale meraviglia? La cosa è ancora più semplice. Il primo requisito è che sia gli oggetti che lanciano e ricevono segnali sia quelli che definiscono gli slot  ereditino dalla classe QObject e definiscano al loro interno la macro Q_OBJECT. Secondo, quando connettiamo un segnale ad uno slot dobbiamo assicurarci che contengano lo stesso numero di parametri, dello stesso tipo e nello stesso ordine. Se le nostre classi rispondono a questi due requisiti siamo pronti ad utilizzare segnali e slot.

Questo meccanismo è usato massicciamente nello sviluppo di applicazioni grafiche in Qt, tuttavia possiamo vedere dall’esempio seguente come esso possa essere applicato in classi che non hanno nulla a che vedere con il lato “grafico” delle Qt.
class Impiegato : public QObject {
    Q_OBJECT
    public:
        Impiegato() { miosalario = 0; }
        int salario() const { return miosalario; }
    public slots:
        void setSalario(int nuovo);
    signals:
        void salarioCambiato(int nuovo);
    private:
        int miosalario;
};

void Impiegato::setSalario(int nuovo) {
    if (nuovo != miosalario) {
        miosalario = nuovo;
        emit salarioCambiato(miosalario);
    }
}
Da questo stralcio di codice possiamo ricavare due osservazioni interessanti. La prima è che le funzioni slot sono del tutto identiche a classiche funzioni C++ e questo significa che possono tranquillamente essere invocate nel modo tradizionale. Il secondo punto è l’introduzione di alcune nuove parole chiave come signals, slots e emit. Queste parole chiave in realtà non sono altro che macro definite nella libreria Qt ma che svolgono un ruolo molto importante nel Meta-Object System.
Un altra cosa che è bene precisare è l’implementazione di setSalario: essa è infatti progettata in modo tale da cambiare solamente se nuovo è diverso da miosalario in modo tale da evitare che si cada in un ciclo infinito in caso di connessione ciclica (ad esempio connettere il segnale salarioCambiato ad un istanza di Impiegato).
L’uso di questo meccanismo crea però un anello in più nella fase di compilazione. Per assicurare che il processo moc vada a buon fine è necessario generare il file di compilazione (makefile) tramita un applicazione ad-hoc: qmake. L’uso di qmake è eccessivamente semplice quindi non approfondirò l’uso di questo tool.
A questo punto avete a disposizione uno strumento meravigliosamente efficiente per la comunicazione fra oggetti della vostra applicazione, sia essa con interfaccia grafica sia senza. Non vi resta che approfondire questa tecnologia con qualche manciata di tutorial.

 

In e C/C++ si ha la convenzione di suddividere il codice all’interno di due file: i file .h per le dichiarazioni e .cpp o .c. Le motivazioni di questa scelta, in realtà, vanno oltre il semplice vezzo stilistico. Tale suddivisione permette infatti una profonda modularità del software e una rudimentale forma di information-hiding. I vari moduli (cpp) quando richiamano un altro modulo (tramite include) vengono a conoscenza solamente delle dichiarazioni delle funzioni e mai della loro implementazione. Implementazione che, inoltre, non gli servirebbe a nulla.

Capita così che il novello programmatore C++ (nostro ormai noto compagno di avventure), conscio di questa convenzione, inizia a scrivere le sue classi con disinvoltura. Suddivide tutte le sue classi con cura ma pecca di solerzia: divide anche le classi template.

Le classi template sono delle classi che hanno uno (o più) tipi parametrizzati. Supponiamo infatti di voler scrivere una struttura dati (ad esempio un array)per organizzare Pere. Poi decidiamo di scrivere una struttura dati analoga solo per gestire Autoradio. Ci rendiamo conto che la struttura fondamentale delle due classi è la stessa. Senza template dovremmo scrivere due classi differenti mentre con le classi template ci basta farne una parametrizzata. Vediamo un esempio:

template
class MyArray
{
         std::vector data;
      public:
         void Add(T  *d);
         void Remove();
         void Print();
};

Come potete notare all’interno di questo scheletro di classe il tipo gestito non è esplicito ma indicato da un segnaposto T. Quando inizializziamo una nuova coda non ci resta che inizializzarla specificando il tipo parametrizzato, ad esempio in questo modo:

MyArray<Pere> lista_pere = new MyArray<Pere>

Ma come funziona questo artificio stregonesco? Molto semplice: in pratica è il compilatore che si incarica di scrivere le classi differenti basandosi sullo scheletro che voi avete dato.

Ed è proprio qui che cade la magagna! A causa di ciò, infatti, le classi template non possono essere divise in file .h e .cpp. se lo fate noterete che il vostro linker inizierà a lamentarsi vistosamente dicendovi che non esiste nessun MyArray<Pere>.

Il motivo di questo comportamento è logico se ci pensate. Supponiamo di avere diviso il nostro template in due file: MyArray.h e MyArray.cpp. Supponiamo poi di utilizzare il nostro template nel file Fruttivendolo.cpp. A questo punto compiliamo:

  1. Compiliamo tranquillamente il nostro template generando il file compilato MyArray.o.
  2. A questo punto andiamo a compilare Fruttivendolo.cpp.
  3. Come abbiamo detto prima il compilatore nota la riga
    MyArray<Pere>

    e quindi “costruisce” la classe corrispodente.

  4. Ma il file Fruttivendolo.cpp può vedere solo MyArray.h perché è il solo file incluso!

A questo punto il compilatore si porge giustamente la domanda: come faccio a compilare la classe MyArray<Pere> se non conosco come sono implementati i metodi del template MyArray.h?

Infatti non lo fa. Ignora il tutto lasciando il compito al linker e sperando che esso trovi da qualche parte un aggancio ad una classe MyArray<pere> che, come è giusto che sia, non trova.

Come risolvere il problema? La soluzione più semplice è di scrivere tutto (dichiarazione più implementazione) tutto nel file header. Tuttavia noi vogliamo mantenere una certa omogeneità nel codice e vogliamo quindi mantenere la divisione!

Per questo ci sono tre soluzioni accettabili. Ovviamente ognuna hai suoi vantaggi e svantaggi e sta a voi scegliere quale sia la migliore per la vostra esigenza:

  1. Nel file .h aggiungere alla fine la riga #include MyArray.cpp. In questo modo viene simulata la scrittura “tutto in uno” pur mantenendo la suddivisione del file. Ovviamente in questo caso nel file .cpp NON VA INCLUSO IL FILE .h per evitare un inclusione ciclica. Questa soluzione ha lo svantaggio di “gonfiare” il codice compilato nel caso lo stesso template venga utilizzato in parecchi file.
  2. Una soluzione alternativa è di definire tutte le funzioni come inline. Anche qui abbiamo gli stessi svantaggi del caso precedente (forse sono anche peggio).
  3. La terza soluzione è l’ideale se sapete da subito quali sono i tipi di dato con cui utilizzerete il template. In questo caso vi basta aggiungere in fonfo al file .cpp la riga
    template class MyArray<Pere>;

    per forzare la compilazione del template per quei tipi particolari. A questo punto il linker troverà sicuramente una versione già compilate della classe che è stata appena chiamata.

Spero che questa piccola note, spesso trascurata quando si parla di template, vi faccia risparmiare tutto il tempo che ha fatto perdere a me a suo tempo.

Buon coding! :D

© 2008-2012 SlashCode Suffusion theme by Sayontan Sinha