Click here to Skip to main content
Click here to Skip to main content

Integrating Lua into C++

By , 2 Sep 2005
 

Introduction

Ever wanted to tweak a value or two without having to compile the whole code section over again? Then maybe an embedded script is your answer. This is where Lua comes in handy. Lua is an embeddable script interpreter that is lightweight and fast. You can learn more on the Lua site.

Background

Often I have come up against the situation in programming when the need arises to tweak and twiddle in order to arrange items or to set their values. This always requires rebuilding (or edit/continuing) the code over and over again, which wastes a lot of time and hence the idea of incorporating a scripting language in the program. This way I could just change the script, save it and re-run the program over and over again saving a lot of building time.

So why Lua?

Lua is a powerful light-weight, small footprint programming language designed for extending applications. Along with this Lua provides its own memory management through garbage collection (which can be specialized for your application). Lua is also fully re-entrant code with no global variables.

Why not use LuaBind?

For those out there who do not know about this, check out LuaBind. LuaBind is great product but for me it looked too complicated. For one the code is not easy to follow where the classes and objects are. Also seeing that I wanted to integrate Lua into a wxWidgets application, using templates was a bit of a no no (you can read cross-platform issues on the wxWidgets site).

Starting with Lua

First, you can download Lua from the official site Lua.org. At the time of writing, the latest version was 5.02. Lua comes ready to be built with any ANSI C compiler. The whole library is ANSI C compliant. After downloading the code it is quite easy to build a library (I personally stick to a static library for it). Seeing that we wanted to work in the C++ world we needed to convert the headers into C++. I created a new header file:

#ifndef __LUA_INC_H__
#define __LUA_INC_H__

extern "C"
{
   #include "lualib/lua.h"
   #include "lualib/lauxlib.h"
   #include "lualib/lualib.h"
}

#endif // __LUA_INC_H__

This file includes all the Lua components necessary for Lua. Now you can just include the header (luainc.h) as per normal in C++ files. To start with the easiest, create a console project so that you can see the print outs from Lua.

Lua problems with C++

Lua is written in C, the whole Lua API is C based. Hence converting Lua into the C++ world would seem rather difficult, but Lua does provide abilities to do this. I have read many reports saying that Lua cannot be used with classes but through a bit of manipulation this can be achieved.

Embedding Lua

To embed Lua is quite easy. There is a lot of documentation listing the C-API for Lua so I am not going to go through that, but just want to give you the basic points. To control the running of Lua we create a CLuaVirtualMachine class. In my opinion a virtual machine is just there to interact with Lua on loading and running files as well as controlling the state. Many probably disagree, but that is worth my 5 cents. So our VM can load files and outside classes can get hold of the state in order to get things off and onto the Lua stack.

Scripting with Lua

In my application the script is there to provide "answers" to the running program. Hence the script is a collection of functions. Lua provides lots of examples of how to write scripts but a simple "hello world" would be like the following:

-- Lua Hello World (test.lua)
function helloWorld ()
   io.write ("hello World")
end

The io.write is an aux library loaded into Lua. To get this script to run embedded in a program we have the following:

int iErr = 0;
lua_State *lua = lua_open ();  // Open Lua
luaopen_io (lua);              // Load io library
if ((iErr = luaL_loadfile (lua, "test.lua")) == 0)
{
   // Call main...
   if ((iErr = lua_pcall (lua, 0, LUA_MULTRET, 0)) == 0)
   { 
      // Push the function name onto the stack
      lua_pushstring (lua, "helloWorld");
      // Function is located in the Global Table
      lua_gettable (lua, LUA_GLOBALSINDEX);  
      lua_pcall (lua, 0, 0, 0);
   }
}
lua_close (lua);

You should now see "hello World" printed on the console. OK, now we know how to call Lua functions. Now we will make Lua call C functions. What we have to do is register the function with Lua. For simplicity we create another printing function:

static int printMessage (lua_State *lua)
{
   assert (lua_isstring (lua,1));

   const char *msg = lua_tostring (lua, 1);

   // debug output
   cout << "script: " << msg << endl;
   return 0;
}

...
luaopen_io (lua);       // Load io library
// setup global printing (trace)
lua_pushcclosure (lua, printMessage, 0);
lua_setglobal (lua, "trace");
...

And our script becomes:

-- Lua Hello World (test.lua)
function helloWorld ()
   io.write ("hello World")
   trace ("trace working now :)")
end

You will now see "hello Worldscript: trace working now :)" printed out. So how does this help us?

Getting into C++

We have now seen how to get Lua call C functions but C is a long way from C++. The major thing about calling a C++ function is that you have to know the object you are referring to (i.e. this pointer). In the above code, you must have noticed that Lua stores all its functions in the LUA_GLOBALSINDEX table. In fact if you read the documentation you will find that most things in Lua are table based. This points us to the idea of storing our object in a Lua table. For a synergy with C++, we will call the table "this", thus indicating C++.

Firstly our script changes to show the existence of the "this" table. The script does become slightly more complicated but is still easy enough to code. The major difference is that the functions now live in the "this" table rather than in the global table. The C++ code takes care of which "this" table is in operation. Also each script function has to pass the "this" table around if they want to access non-global functions. Our Hello World now becomes:

function this.helloWorld (this)
   io.write ("hello World")
   trace ("trace working now :)")
end

For this you will see that the script can still call the global C functions.

To start the C++ part, we need a few helpers to get us on our way. The first is a CLuaScript class. This class is for user classes to inherit from when they want to run the scripting functions. This class allows users to register C++ functions (methods) to Lua. The CLuaScript contains the reference to the "this" table as well. The reference is used to get hold of the "this" table later on when needed. The next helper class is CLuaThis. This class helps to control, which "this" table is currently being used by Lua.

Every time a new CLuaScript is created, it creates a new table in Lua. This table is then stored in a global table called the registry. The registry is a persistent table across function calls. The "this" table is kept by means of a reference. With the reference we can push the table onto the stack when we need it. When the script is compiled, Lua places the helloWorld function in the "this" table. Lua knows about the "this" table because CLuaThis has told Lua before the compile where the "this" table was. The job of the "this" table is to keep all the functions together for the CLuaScript object. Each CLuaScript now has its own "this" table and bang we have object orientated Lua.

Now what?

Now that we have the theory, the trouble is how to put that into practice. Seeing that Lua can only call C functions (or static members of classes) we need a way of converting that to C++. Lua provides us with an ability to register a variable with the calling function. This allows us to be very cunning while registering our functions with Lua. In fact, we do not actually register the functions but register the index look-ups for them.

Getting our hands dirty

When we create a CLuaScript object we need to create a table and store the current this pointer in the table at index 0. This way when the script calls our C function we will be able to get the object pointer relating to the call. Hence the constructor:

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);

You will notice a CLuaRestoreStack class there. This is just a class to ensure that the Lua stack is balanced on the exit. It is useful because then you can be lazy and not worry about making sure you leave the stack as you found it :)

Now comes the compiling of files. We need to load the correct "this" table to the stack before we compile the script. In the constructor of CLuaThis class we have:

// Save the old "this" table
lua_getglobal (state, "this");
m_iOldRef = luaL_ref (state, LUA_REGISTRYINDEX);

// replace it with our new one
lua_rawgeti(state, LUA_REGISTRYINDEX, iRef);
lua_setglobal (state, "this");

This saves the previous reference to a "this" table and loads the reference of our new one. Then in the destructor we return the stack. This is done so that the functions are all stored in the right table. Our compiling code then becomes:

CLuaThis luaThis (m_vm, m_iThisRef); // Make available to 
                                     // correct "this" table
                                     
m_vm.RunFile (strFilename);          // Compile the file

After all this we come to registering those C++ functions. We always register the same C function with Lua for each C++ function. The difference is that each time we register with a unique index. This index is sent to the CLuaScript callback function. So hence the registering with Lua looks like this:

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);

You will notice that the function is stored in the "this" table and not globally. So now every time you want to call a function from a script you need to refer to the table. Now say we register a function called "hello" in Lua, our script would access it by:

function this.helloWorld (this)
   io.write ("hello World")
   trace ("trace working now :)")
   this:hello ()
end

Using the code

CLuaScript

The first class to consider is the CLuaScript. You need to inherit your scripting class from it and overload the ScriptCalling and HandleReturns methods. The base code calls ScriptCalling each time the script calls one of the classes' methods. An index to the method is passed in as well. The HandleReturns method is called if there are any returns from the script that needs to be handled. The function name that is returning the values is passed to this. The CLuaScript base has the ability of sending parameters (int, float and string) to script functions.

CLuaVirtualMachine

The next class is CLuaVirtualMachine. You will only require one of these (unless you have other requirements). A test program would have the following structure:

class CMyScript : public CLuaScript
{
public:
   CMyScript (CLuaVirtualMachine& vm) : CLuaScript (vm)
   {
      m_iMethodBase = RegisterFunction ("hello1");
      RegisterFunction ("hello2");
      RegisterFunction ("hello3");
   }

   ...
   int ScriptCalling (CLuaVirtualMachine& vm, 
                                  int iFunctionNumber)
   {
      switch (iFunctionNumber - m_iMethodBase)
      {
      case 0:
         return Hello1 (vm);
      case 1:
         return Hello2 (vm);
      case 2:
         return Hello3 (vm);
      }

      return 0;
   }
   ...
   int Hello2 (CLuaVirtualMachine& vm)
   {
      lua_State *state = (lua_State *) vm;

      int iNumber = (int) lua_tonumber (state, -1);
      printf ("Hellow (2) -> %d\n", iNumber);
      return 0;
   }
   ...
   void HandleReturns (CLuaVirtualMachine& vm, 
                                const char *strFunc);
   ...
};

void main (void)
{
   CLuaVirtualMachine vm;
   vm.InitialiseVM ();
   ...
   CMyScript ms (vm);
   ms.CompileFile ("test.lua");
   ...
   ms.SelectScriptFunction ("CountAndCall");
   ms.AddParam (2);
   ms.Go ();
   ...
}

The others

There are other classes as mentioned in the discussion. There is a debugging class as well, but I do not really use it much but when things go wrong it comes in handy to see where the script is going. The sample shows how it is used.

History

  • v1.0 - Ah..., the first version. Never buy any software that is version 1. Bugs will be there :)
  • v1.01 - Corrected CLuaThis class to get the "this" table before setting up the new one.

License

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

About the Author

RichardS
Software Developer (Senior)
South Africa South Africa
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionLua 5.2 questionmemberMember 44938444 Mar '13 - 23:32 
How would you rewrite the following code to work in Lua 5.2 (LUA_GLOBALSINDEX is removed):
 
// Push the function name onto the stack
     lua_pushstring (lua, "helloWorld");
     // Function is located in the Global Table
     lua_gettable (lua, LUA_GLOBALSINDEX);
     lua_pcall (lua, 0, 0, 0);
 
I've tried the following without any success:
 
lua_pushglobaltable(lua);;
lua_pushstring(lua, "helloWorld");
lua_gettable(lua, -2);
lua_pcall(lua, 0, 0, 0);

AnswerRe: Lua 5.2 questionmemberRichardS5 Mar '13 - 1:05 
Hi,
 
Have you tried:
http://stackoverflow.com/questions/10087226/lua-5-2-lua-globalsindex-alternative[^]
 

Regards,
Richard
Question[My vote of 1] Globals?memberMember 944989130 Jan '13 - 22:15 
Would have been nice to have a simple example for accessing lua vars with C++. Why not starting the easy way?
AnswerRe: [My vote of 1] Globals?memberRichardS5 Mar '13 - 0:56 
Hi,
 
The idea of this was to be able to access functions not global variables.
 
Thanks,
Richard
QuestionErrorsmemberexilepilot7 Jan '13 - 14:25 
Hey, this article may be a little old but I have several errors with the first piece of code that prints "Hello, world" using io.write() function in Lua.
 
There's exactly 8 errors, I've put them on pastebin
Errors
 
Also, here's the source if you think I've got something wrong.
 
Source
 
EDIT:
 
I copied the code and made a new project using code::blocks which uses mingw compiler.
The errors say I've got undefined references to Lua functions:
  • luaopen
  • luaopen_io
  • luaL_loadfile
And others that are used in the headers.
 

-Exile
AnswerRe: ErrorsmemberRichardS27 Jan '13 - 23:45 
Hi,
 
Sorry, haven't compiled this in a long time (moved onto using Python rather than Lua).
 
It looks like some linker error with not being able to find the Lua libraries in the Link path.
 
Regards,
Richard
QuestionLua 5.2memberSendel11 Dec '12 - 21:15 
Hi,
 
What to do to get it work with Lua 5.2 ?
When two men in business always
agree, one of them is unnecessary.
William Wrigley

AnswerRe: Lua 5.2memberSendel11 Dec '12 - 21:42 
in
LuaVirtualMachine.cpp 
change
m_pState = lua_open ();
to
m_pState = luaL_newstate (); 
and
lua_open_loadlib (m_pState);
to
luaL_openlibs (m_pState); 
 
and in
LuaDebugger.cpp
remove
case LUA_HOOKTAILRET:
 
Cheers,
Sendel Java | [Coffee]
 

if you are not part of a solution you might be a part of the problem...
When two men in business always
agree, one of them is unnecessary.
William Wrigley

GeneralRe: Lua 5.2memberRichardS27 Jan '13 - 23:49 
Thanks Sendel - looks good.
QuestionTwo classes in the same .lua scriptmemberAkkernight29 Jun '11 - 14:24 
Is it possible to have two classes, children of CLuaScript, sharing the same .lua file at the same time? For example your CMyScript and CMyScript2, so that CMyScript::Hello1() is called in CMyScript2::addme() ?
I hope my question is clear.
AnswerRe: Two classes in the same .lua scriptmemberRichardS29 Jun '11 - 23:06 
Hi,
 
Have not really tried but it was designed to be a single file interface.
 
I think as long was you are accessing the script one call at a time it should be fine.
 
Sorry.
GeneralNot working with coroutines [modified]memberteknoman11721 Jul '10 - 14:18 
I am having some issues using this with coroutines. When I call the functions from a coroutine all of the parameters seem to be NULL or 0, depending on the type.
the console_Print function is supposed to print a numerical value to the in game console. Except the function is having issues. The following function runs but when it prints temp which is non zero, it always is zero.
 
temp = 0
counter = 0
 
this.Update = coroutine.wrap( function(thisI, timeSinceI)
	local this = thisI
	local timeSince = timeSinceI
	while true do
		while counter < 1 do
			this, timeSince = coroutine.yield()
			counter = counter + timeSince
		end
		counter = 0
		temp = temp + 1
		print ("Frame counter: " .. temp)
		this:console_Print(temp)
	end
end)
 
I need the coroutines to be able do level scripting. This following code does work however
 
function this.Update (this, timeSince)
	counter = counter + timeSince
	temp = temp + 1
	if counter > 1 then
		counter = 0
		print ("Frame counter: " .. temp)
		this:console_Print(temp)
	end
end
Nathaniel

modified on Wednesday, July 21, 2010 8:40 PM

GeneralRe: Not working with coroutinesmemberRichardS21 Jul '10 - 21:21 
Hi,
 
Threading is a problem. I suspect it is something to do with the stack and where items are placed when coroutines execute. The null parameters would probably be caused by pulling 0's off the stack instead of the real data. Unfortunately this was not thought when implementing the code. When I have some time I will look into it.
 

Thanks.
QuestionHello, May I Have your email?memberHefe Hoo2 May '10 - 23:07 
Nice Job.I'm trying to fix it as what I wanted. May I Have your email? thanks

By Hefe

GeneralMy vote of 2memberChris Grimes26 Oct '09 - 10:05 
Magical assertions in the article like, 'We need to load the correct "this" table to the stack before we compile the script.' Why use a table named 'this'??? The resulting lua function signatures don't resemble any OOP language I've seen!
QuestionCan I use this code in multithreaded envmemberborisusun27 Jan '09 - 20:52 
Can I use this code in multithreaded env? Should I create virtual machine per thread.
 
Actually we are using fibers? Is there any implications for using fibers this this library?
AnswerRe: Can I use this code in multithreaded envmemberRichardS6 Feb '09 - 5:09 
We have not run this library with in a multiple threaded application. In essence I think that it may not run so well. I think that Lua is single threaded when you initialise it, and the virtual machine does not take multiple instances into account (i.e. no instance protection).
 
If you built in instance protection, made sure that only one thread accesses Lua at a time, then it would be possible.
 
"Programming today is a race between software engineers striving to build bigger and
better idiot-proof programs, and the Universe trying to produce bigger and better idiots.
So far the Universe is winning." -- Rich Cook

GeneralFunctorsmemberMax Elliott23 Apr '08 - 6:27 
Richard,
 
Good work.
 
I didn't find any licensing comments in the code or here in the article, so I am going to assume it's ok for me to go ahead and use it as a starting point in my project... Let me know if otherwise.
 
One idea (I'm going to make the changes here and I will let you know how it goes) is to use Functors instead of inheriting from CLuaScript and implementing ScriptCalling() with a switch statement on function index. It would massively simplify the code and make it a little faster. Basically you just register a pointer to the Functor as a void* instead of the 'this' pointer (in the userdata). The Functor holds the this pointer and the method address. With some template magic, I hope to allow functions with arg lists as well.
 

Cheers!
 
Max Elliott
Sony Computer Entertainment
"Live in the present"

GeneralRe: FunctorsmemberRichardS23 Apr '08 - 21:35 
Thanks Max.
 
In later work I have used Functors quite often, so I agree it should have an added benefit.
 
"Programming today is a race between software engineers striving to build bigger and
better idiot-proof programs, and the Universe trying to produce bigger and better idiots.
So far the Universe is winning." -- Rich Cook

GeneralInheritance and CLuaThismemberGwareth5 Feb '07 - 6:53 
Just a question (and I'll probably test this myself here in a bit), but if you created a class (say CTest) and inherited it from CLuaThis. Then, in CLuaThis instead of registering "this" in the table, you registered "typeid(this).name()" wouldn't that register the proper class name?
 
Just curious. Or maybe you'd have to redefine that function in the derived class. Hmm. Something to think about.
NewsLua 5.1memberJ.Kaminski4 Nov '06 - 7:43 

Hello Richard,
 
thank you for this good work! Smile | :)
To make it run with Lua 5.1 you have to replace
 
luaopen_base (m_pState);
luaopen_table (m_pState);
luaopen_string (m_pState);
luaopen_math (m_pState);
luaopen_debug (m_pState);
luaopen_io (m_pState);
luaopen_package (m_pState);

 
in CLuaVirtualMachine::InitializeVM with
 
luaL_openlibs (m_pState);
 
Best regards,
Juergen
(you got my 5!)
GeneralRe: Lua 5.1memberRichardS5 Nov '06 - 20:06 
Thanks Juergen. Out of curiosity, why move to an older release of Lua?
 

 
"Programming today is a race between software engineers striving to build bigger and
better idiot-proof programs, and the Universe trying to produce bigger and better idiots.
So far the Universe is winning." -- Rich Cook

GeneralRe: Lua 5.1memberEnrico Detoma23 Feb '07 - 1:26 
Actually Lua 5.1 is newer than the 5.02 version which is mentioned in the article.
 
Regards
Enrico

GeneralCaveat: protected: const char *m_strFunctionName;membersenneth12 Jan '06 - 2:42 
This design will lead into a function never returning it's value when you call other script functions from inside registered C functions, becuase it's getting overwritten with another function name, and virtual void HandleReturns() will get the wrong function name. So better replace it with something like a stack that pushes the functions names and pops them when they return.

GeneralRe: Caveat: protected: const char *m_strFunctionName;memberRichardS12 Jan '06 - 2:55 
Hi senneth,
 
Could you give me an example of this happenning?
 
From your explination above I assume you mean calling Lua functions inside the HandleReturns function, or inside the function C++ class method that the script calls?
 
regards,
Rich
 
"Programming today is a race between software engineers striving to build bigger and
better idiot-proof programs, and the Universe trying to produce bigger and better idiots.
So far the Universe is winning." -- Rich Cook

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 2 Sep 2005
Article Copyright 2005 by RichardS
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid