Click here to Skip to main content
15,881,688 members
Articles / Containers / Virtual Machine

Integrating Lua into C++

Rate me:
Please Sign up or sign in to vote.
4.82/5 (42 votes)
2 Sep 2005CPOL8 min read 332.7K   8.6K   127  
An article about embedding the Lua scripting language with C++ objects.
// ---------------------------------------------------------------------------
// FILE NAME            : LuaScript.cpp
// ---------------------------------------------------------------------------
// DESCRIPTION :
//
// Simple debugging routines
// 
// ---------------------------------------------------------------------------
// VERSION              : 1.00
// DATE                 : 1-Sep-2005
// AUTHOR               : Richard Shephard
// ---------------------------------------------------------------------------
// LIBRARY INCLUDE FILES
#include <assert.h>
#include "luaLib/luainc.h"
#include "luascript.h"
#include "luarestorestack.h"
#include "luathis.h"
// ---------------------------------------------------------------------------

#define BEGIN_LUA_CHECK(vm)   lua_State *state = (lua_State *) vm; \
                              if (vm.Ok ()) { 
#define END_LUA_CHECK         }


//============================================================================
// int LuaCallback
//---------------------------------------------------------------------------
// Lua C-API calling that figures out which object to hand over to
//
// Parameter   Dir      Description
// ---------   ---      -----------
// lua         IN       State variable
//
// Return
// ------
// Number of return varaibles on the stack
//
// Comments
// --------
// This is the function lua calls for each C-Function that is
// registered with lua. At the time of registering the function
// with lua, we make lua record the method "number" so we can
// know what method was actually called. The lua stack is the
// following structure:
// 0: 'this' (table)
// 1 - ...: parameters passed in
//
//============================================================================
static int LuaCallback (lua_State *lua)
{
   // Locate the psudo-index for the function number
   int iNumberIdx = lua_upvalueindex (1);
   int nRetsOnStack = 0;

   bool fSuccess = false;

   // Check for the "this" table
   if (lua_istable (lua, 1))
   {
      // Found the "this" table. The object pointer is at the index 0
      lua_rawgeti (lua, 1, 0);
      
      if (lua_islightuserdata (lua, -1))
      {
         // Found the pointer, need to cast it
         CLuaScript *pThis = (CLuaScript *) lua_touserdata (lua, -1);

         // Get the method index
         int iMethodIdx = (int) lua_tonumber (lua, iNumberIdx);

         // Check that the method is correct index
         assert (!(iMethodIdx > pThis->methods ()));

         // Reformat the stack so our parameters are correct
         // Clean up the "this" table
         lua_remove (lua, 1);
         // Clean up the pThis pointer
         lua_remove (lua, -1);

         // Call the class
         nRetsOnStack = pThis->ScriptCalling (pThis->vm (), iMethodIdx);

         fSuccess = true;
      }
   }

   if (fSuccess == false)
   {
      lua_pushstring (lua, "LuaCallback -> Failed to call the class function");
      lua_error (lua);
   }

   // Number of return variables
   return nRetsOnStack;
}


//============================================================================
// CLuaScript::CLuaScript
//---------------------------------------------------------------------------
// Constructor. Sets up the lua stack and the "this" table
//
// Parameter            Dir      Description
// ---------            ---      -----------
// CLuaVirtualMachine   IN       VM to run on
//
// Return
// ------
// None.
//
//============================================================================
CLuaScript::CLuaScript (CLuaVirtualMachine& vm)
 : m_vm (vm), m_nMethods (0), m_iThisRef (0), m_nArgs (0)
{
   BEGIN_LUA_CHECK (vm)
      // Create a reference to the "this" table. Each reference is unique
      lua_newtable (state);
      m_iThisRef = luaL_ref (state, LUA_REGISTRYINDEX);

      // Save the "this" table to index 0 of the "this" table
      CLuaRestoreStack rs (vm);
      lua_rawgeti (state, LUA_REGISTRYINDEX, m_iThisRef);
      lua_pushlightuserdata (state, (void *) this);
      lua_rawseti (state, -2, 0);
   END_LUA_CHECK
}

//============================================================================
// CLuaScript::~CLuaScript
//---------------------------------------------------------------------------
// Destructor
//
// Parameter   Dir      Description
// ---------   ---      -----------
// None.
//
// Return
// ------
// None.
//
//============================================================================
CLuaScript::~CLuaScript (void)
{
   CLuaRestoreStack rs (m_vm);

   BEGIN_LUA_CHECK (m_vm)
      // Get the reference "this" table
      lua_rawgeti (state, LUA_REGISTRYINDEX, m_iThisRef);

      // Clear index 0
      lua_pushnil (state);
      lua_rawseti (state, -2, 0);
   END_LUA_CHECK
}

//============================================================================
// bool CLuaScript::CompileBuffer
//---------------------------------------------------------------------------
// Compiles a given buffer
//
// Parameter   Dir      Description
// ---------   ---      -----------
// pbBuffer    IN       Data buffer
// szLen       IN       Length of buffer
//
// Return
// ------
// Success
//
//============================================================================
bool CLuaScript::CompileBuffer (unsigned char *pbBuffer, size_t szLen)
{
   assert (pbBuffer != NULL && "CLuaScript::CompileBuffer ->  pbBuffer == NULL");
   assert (szLen != 0 && "CLuaScript::CompileBuffer -> szLen == 0");
   assert (m_vm.Ok () && "VM Not OK");

   // Make sure we have the correct "this" table
   CLuaThis luaThis (m_vm, m_iThisRef);

   return m_vm.RunBuffer (pbBuffer, szLen);
}

//============================================================================
// bool CLuaScript::CompileBuffer
//---------------------------------------------------------------------------
// Compiles a given file
//
// Parameter   Dir      Description
// ---------   ---      -----------
// strFilename IN       File name to compile
//
// Return
// ------
// Success
//
//============================================================================
bool CLuaScript::CompileFile (const char *strFilename)
{
   assert (strFilename != NULL && "CLuaScript::CompileFile -> strFilename == NULL");
   assert (m_vm.Ok () && "VM Not OK");

   // Make sure we have the correct "this" table
   CLuaThis luaThis (m_vm, m_iThisRef);

   return m_vm.RunFile (strFilename);
}

//============================================================================
// int CLuaScript::RegisterFunction
//---------------------------------------------------------------------------
// Registers a function with Lua
//
// Parameter   Dir      Description
// ---------   ---      -----------
// strFuncName IN       Function name
//
// Return
// ------
// Success
//
//============================================================================
int CLuaScript::RegisterFunction (const char *strFuncName)
{
   assert (strFuncName != NULL && "CLuaScript::RegisterFunction -> strFuncName == NULL");
   assert (m_vm.Ok () && "VM Not OK");

   int iMethodIdx = -1;

   CLuaRestoreStack rs (m_vm);

   BEGIN_LUA_CHECK (m_vm)
      iMethodIdx = ++m_nMethods;

      // Register a function with the lua script. Added it to the "this" table
      lua_rawgeti (state, LUA_REGISTRYINDEX, m_iThisRef);

      // Push the function and parameters
      lua_pushstring (state, strFuncName);
      lua_pushnumber (state, (lua_Number) iMethodIdx);
      lua_pushcclosure (state, LuaCallback, 1);
      lua_settable (state, -3);

   END_LUA_CHECK

   return iMethodIdx;
}

//============================================================================
// bool CLuaScript::SelectScriptFunction
//---------------------------------------------------------------------------
// Selects a script function to run
//
// Parameter   Dir      Description
// ---------   ---      -----------
// strFuncName IN       Function name
//
// Return
// ------
// Success
//
//============================================================================
bool CLuaScript::SelectScriptFunction (const char *strFuncName)
{
   assert (strFuncName != NULL && "CLuaScript::SelectScriptFunction -> strFuncName == NULL");
   assert (m_vm.Ok () && "VM Not OK");

   bool fSuccess = true;

   BEGIN_LUA_CHECK (m_vm)
      // Look up function name
      lua_rawgeti (state, LUA_REGISTRYINDEX, m_iThisRef);
      lua_pushstring (state, strFuncName);
      lua_rawget (state, -2);
      lua_remove (state, -2);

      // Put the "this" table back
      lua_rawgeti (state, LUA_REGISTRYINDEX, m_iThisRef);

      // Check that we have a valid function
      if (!lua_isfunction (state, -2))
      {
         fSuccess = false;
         lua_pop (state, 2);
      }
      else
      {
         m_nArgs = 0;
         m_strFunctionName = strFuncName;
      }
   END_LUA_CHECK
   
   return fSuccess;
}

//============================================================================
// bool CLuaScript::ScriptHasFunction
//---------------------------------------------------------------------------
// Checks to see if a function exists
//
// Parameter      Dir      Description
// ---------      ---      -----------
// strScriptName  IN       Function name
//
// Return
// ------
// Success
//
//============================================================================
bool CLuaScript::ScriptHasFunction (const char *strScriptName)
{
   assert (strScriptName != NULL && "CLuaScript::ScriptHasFunction -> strScriptName == NULL");
   assert (m_vm.Ok () && "VM Not OK");

   CLuaRestoreStack rs (m_vm);

   bool fFoundFunc = false;

   BEGIN_LUA_CHECK (m_vm)
      lua_rawgeti (state, LUA_REGISTRYINDEX, m_iThisRef);
      lua_pushstring (state, strScriptName);
      lua_rawget (state, -2);
      lua_remove (state, -2);

      if (lua_isfunction (state, -1))
      {
         fFoundFunc = true;
      }
   END_LUA_CHECK

   return fFoundFunc;
}

//============================================================================
// void CLuaScript::AddParam
//---------------------------------------------------------------------------
// Adds a parameter to the parameter list
//
// Parameter   Dir      Description
// ---------   ---      -----------
// string      IN       string param
//
// Return
// ------
// None.
//
//============================================================================
void CLuaScript::AddParam (char *string)
{
   assert (string != NULL && "CLuaScript::AddParam -> string == NULL");
   assert (m_vm.Ok () && "VM Not OK");

   BEGIN_LUA_CHECK (m_vm)
      lua_pushstring (state, string);
      ++m_nArgs;
   END_LUA_CHECK
}

//============================================================================
// void CLuaScript::AddParam
//---------------------------------------------------------------------------
// Adds a parameter to the parameter list
//
// Parameter   Dir      Description
// ---------   ---      -----------
// iInt        IN       int param
//
// Return
// ------
// None.
//
//============================================================================
void CLuaScript::AddParam (int iInt)
{
   assert (m_vm.Ok () && "VM Not OK");

   BEGIN_LUA_CHECK (m_vm)
      lua_pushnumber (state, (lua_Number) iInt);
      ++m_nArgs;
   END_LUA_CHECK
}

//============================================================================
// void CLuaScript::AddParam
//---------------------------------------------------------------------------
// Adds a parameter to the parameter list
//
// Parameter   Dir      Description
// ---------   ---      -----------
// fFloat      IN       float param
//
// Return
// ------
// None.
//
//============================================================================
void CLuaScript::AddParam (float fFloat)
{
   assert (m_vm.Ok () && "VM Not OK");

   BEGIN_LUA_CHECK (m_vm)
      lua_pushnumber (state, (lua_Number) fFloat);
      ++m_nArgs;
   END_LUA_CHECK
}

//============================================================================
// bool CLuaScript::Go
//---------------------------------------------------------------------------
// Runs the selected script function
//
// Parameter   Dir      Description
// ---------   ---      -----------
// nReturns    IN       Number of expected returns
//
// Return
// ------
// None.
//
//============================================================================
bool CLuaScript::Go (int nReturns /* = 0 */)
{
   assert (m_vm.Ok () && "VM Not OK");

   // At this point there should be a parameters and a function on the
   // Lua stack. Each function get a "this" parameter as default and is
   // pushed onto the stack when the method is selected

   bool fSuccess = m_vm.CallFunction (m_nArgs + 1, nReturns);

   if (fSuccess == true && nReturns > 0)
   {
      // Check for returns
      HandleReturns (m_vm, m_strFunctionName);
      lua_pop ((lua_State *) m_vm, nReturns);
   }

   return fSuccess;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead
South Africa South Africa
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions