Lua e C++

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.

3 comments on “Lua e C++

  1. Figo… sì, mi piacerebbe leggere un altro articolo con qualche dettaglio in più.
    Un po’ mi mancavi… 🙂

    • Ricevuto 🙂 Comincio a scrivere qualche altra cosa allora. ^^