Embedding a scripting language inside your C/C++ code

Posted by Steve on Tue 4 Oct 2005 at 11:07

There are many scripting languages which may be embedded into applications. Some languages have evolved this ability over time, and others have been designed from the ground up as embedded languages. One of the languages specifically designed for embedded use is Lua.

Here we're going to examine using the Lua Scripting Language and embedding it inside a C application.

One of my recent hobbies has been the creation of simple games, reminiscent of those I used to play upon my first computer.

Whilst writing games isn't something that will appeal to all Debian users, or sysadmins, there are a lot of uses to which a program can put an embedded scripting language - so that is the motivation for posting this here.

To start with you'll need to install the libraries. That can easily be done with :

apt-get install liblua50-dev

This will give you a set of development libraries, header files, and other things required to build lua scripted applications.

Once installed you can compile and link your applications using the lua-config helper. This will allow you to generate the compiler flags necessary:

skx@vilya:~$ lua-config --include --libs
-I/usr/include/lua50 -L/usr/include -llualib50 -llua50

Once installed you will want your C, or C++, code to do three things:

  • Initialise Lua
  • Call from C into a Lua script.
  • Call from Lua into your C application.

Each of these jobs will be demonstrated with a small example.

Initialising Lua

The most simple example we'll look at involves initialising the lua scripting engine, after it has been linked into your application.

The code would look like this:

#include <stdio.h>

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

/* the Lua interpreter */
lua_State* L;

int main ( int argc, char *argv[] )
{
	/* initialize Lua */
	L = lua_open();

	/* load various Lua libraries */
	lua_baselibopen(L);
        luaopen_table(L);
        luaopen_io(L);
        luaopen_string(L);
        luaopen_math(L);
    
	/* cleanup Lua */
	lua_close(L);

	return 0;
}

(You may download this file.)

Once you've saved this as init.c you can compile it and build it like so:

skx@vilya:~$ gcc -o init -Wall `lua-config --include --libs` init.c
skx@vilya:~$ 

Executing the code, predictably, does nothing:

skx@vilya:~$ ./init 
skx@vilya:~$ 

This is because we've written no real code. We merely initialise an interpreter instance and then immediately clean it up. The various luaopen_ functions are worthy of note, they perform a job equivilent to loading some standard libraries. If you don't include them you will be unable to use some of the standard functions - by ommitting them you can "sandbox" your scripts a little.

To do something useful we must move on.

Calling from C into a Lua script.

Loading and calling into Lua scripts from your C code is the whole point of the lua language. So there are several ways you can do this.

The most basic is to load a single source file and execute it in its entirety - much like running system( "perl someScript.pl" );.

The more general purpose way of calling Lua code from C is to push arguments to the function upon the stack and then make a call.

We will show an example of both kinds of code. The first is the simplest.

Imagine we have a lua script, called do-me.lua with the following contents:

print "Start"
for i=1,10 do
   print(i) 
end
print "End"

(This script merely executes a loop - printing the numbers 1 through 10.)

We can load and execute this script by the following code:

#include <stdio.h>

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

/* the Lua interpreter */
lua_State* L;

int main ( int argc, char *argv[] )
{
	/* initialize Lua */
	L = lua_open();

	/* load Lua base libraries */
	lua_baselibopen(L);

	/* run the script */
	lua_dofile(L, "do-me.lua");

	/* cleanup Lua */
	lua_close(L);

	return 0;
}

(This file is available for download.)

Compiling this code is a simple process, similar to the last one:

skx@vilya:~$ gcc -o do-me -Wall `lua-config --include --libs` do-me.c

Once we have the compiled C program we can execute it to see the Lua script do-me.lua executed:

skx@vilya:~$ ./do-me 
Start
1
2
3
4
5
6
7
8
9
10
End

That was a useful enough example. But it isn't great. Instead if you're building your extensible application you'll want to call specific user defined functions - allowing users to override basic operations, etc.

For the next example we'll write a lua function which will calculate the sum of two numbers. Then call it from C.

The process will look like this:

  • Find the function.
  • Push the arguments onto the stack.
  • Call the function, specifying the arguments we've pushed.
  • Get the return value.

This code, saved in the file add.c, looks like this:

#include <stdio.h>

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"


/* the Lua interpreter */
lua_State* L;

int main ( int argc, char *argv[] )
{
	int sum;
	
	/* initialize Lua */
	L = lua_open();

	/* load Lua base libraries */
	lua_baselibopen(L);

	/* load the script */
	lua_dofile(L, "add.lua");
	
	/* the function name */
	lua_getglobal(L, "add");

	/* the first argument */
	lua_pushnumber(L, 41 );

	/* the second argument */
	lua_pushnumber(L, 22 );

	/* call the function with 2
	   arguments, return 1 result */
	lua_call(L, 2, 1);

	/* get the result */
	sum = (int)lua_tonumber(L, -1);
	lua_pop(L, 1);

	/* print the result */
	printf( "The result is %d\n", sum );

	/* cleanup Lua */
	lua_close(L);

	return 0;
}

(This file is available for download.)

Compiling is as before:

skx@vilya:~$ gcc -o add -Wall `lua-config --include --libs` add.c
skx@vilya:~$ 

With the proper lua script file, add.lua:

function add ( x, y )
	return x + y
end

We can now run the program:

skx@vilya:~$ ./add 
The result is 63
skx@vilya:~$ 
Call from Lua into your C application.

The most interesting aspect of writing scriptable applications is to allow the script to contain your logic, calling into your application for the "hard" work.

In my terms that involves writing a simple piece of C to display the sprites upon the screen - then having the lua application make calls such as "moveLeft", "moveRight", "isDead" etc.

To call from Lua to your C functions you must first register them. Then when they are called you have to do a couple of extra things from your C code:

  • Fetch the arguments.
  • Do whatever you wish with them.
  • Push the results upon the stack.
  • Return the number of results.

In this next example we'll write a C function "average" which will average a list of numbers. We will call this from the lua script "average.lua".

The calling script will look like this:

avg, sum = average(10, 20, 30, 40,  50)

print("The average is ", avg)
print("The sum is ", sum)

The corresponding C code looks like this:

#include <stdio.h>

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

/* the Lua interpreter */
lua_State* L;

/* The function we'll call from the lua script */
static int average(lua_State *L)
{
	/* get number of arguments */
	int n = lua_gettop(L);
	double sum = 0;
	int i;

	/* loop through each argument */
	for (i = 1; i <= n; i++)
	{
	        if (!lua_isnumber(L, i)) 
		{
		      lua_pushstring(L, "Incorrect argument to 'average'");
		      lua_error(L);
		}

		/* total the arguments */
		sum += lua_tonumber(L, i);
	}

	/* push the average */
	lua_pushnumber(L, sum / n);

	/* push the sum */
	lua_pushnumber(L, sum);

	/* return the number of results */
	return 2;
}


int main ( int argc, char *argv[] )
{
	/* initialize Lua */
	L = lua_open();

	/* load Lua base libraries */
	lua_baselibopen(L);

	/* register our function */
	lua_register(L, "average", average);

	/* run the script */
	lua_dofile(L, "average.lua");

	/* cleanup Lua */
	lua_close(L);

	return 0;
}

(You may download these files too.)

Compiling this and running it shows that we've:

  • Loaded the script average.lua.
  • Called from that script back into our C application.

Compilation and execution look like this:

skx@vilya:~$ gcc -o average -Wall `lua-config --include --libs` average.c
skx@vilya:~$ ./average 
The average is  30
The sum is      150
Other Tricks

It is possible that you will want to setup some global variables from your C code which you wish the script to be able to use. A good example of this is a version number.

You can do that with the following code:

#define VERSION "0.7"

lua_pushstring(L, "VERSION");
lua_pushstring(L, VERSION);
lua_settable(L, LUA_GLOBALSINDEX);

This allows your Lua code to do something like this:

print( "Version " .. VERSION );

If you've followed along you should be capable of embedding simple functions into external script. Or writing main loops entirely in Lua.

The online Programming Lua book contains a wealth of information about using the language, including:

This section in particular, (and the following ones building upon it), show in detail how you can manage calling from C to Lua, and back again.

 

 


Posted by Anonymous (200.102.xx.xx) on Tue 4 Oct 2005 at 15:31
tolua++ is also a very nice tool. It enables you to take a function in C and have it readily available in Lua. It's a swig for lua.

Unfortunately, the version of tolua available in Debian is kind of old - it only works with Lua 4.

[ Parent | Reply to this comment ]

Posted by Steve (82.41.xx.xx) on Tue 4 Oct 2005 at 17:21
[ View Steve's Scratchpad | View Weblogs ]

Thanks for the link. In the past I've used the C++ wrapper for Lua (lua++), but I figured I'd not distract anybody by mentioning it.

Swig rocks. I've used it several times to write Perl bindings for shared libraries, etc. Highly recommended.

Steve
--

[ Parent | Reply to this comment ]

Posted by Anonymous (69.129.xx.xx) on Thu 13 Oct 2005 at 01:27
SWIG 1.3.26, which was released a few days ago, just added a lua module. I would suggest using SWIG, because it makes it a lot easier.

[ Parent | Reply to this comment ]

Posted by Anonymous (216.18.xx.xx) on Tue 4 Oct 2005 at 18:40
Interesting article - thanks!

-d

[ Parent | Reply to this comment ]

Posted by Steve (82.41.xx.xx) on Wed 5 Oct 2005 at 02:39
[ View Steve's Scratchpad | View Weblogs ]

Since I realise I didn't use C++ in the article, despite mentioning it in the title here's a link to a quite demo I knocked up in C++:

Sample usage:

  /**
   * Gain access to the single LUA intepretter object.
   */
  CLUAInstance *lua = CLUAInstance::getInstance();

  /**
   * Run a script with it!
   */
  lua->runScript( "lua.lua" );

  /*
   * Cleanup.
   */
  delete( lua );

Steve
--

[ Parent | Reply to this comment ]

Posted by divan (83.170.xx.xx) on Wed 5 Oct 2005 at 05:14
Very nice!
The same could be done with TCL, which is more powerful and widely spreaded, but more heavy and slowly.
I've made some tests, comparing LUA and TCL as embeddable languages, and LUA is definitely better choice for most cases.

[ Parent | Reply to this comment ]

Posted by lpenz (200.102.xx.xx) on Wed 5 Oct 2005 at 15:35
Yeah.
Not wanting to get into a flamewar, but if you're providing a scripting language in your application to non-TCL programmers, Lua is much better. It's easier, and more "familiar".
There's an excelent book on Lua at http://www.lua.org/pil/.

[ Parent | Reply to this comment ]

Posted by Anonymous (201.20.xx.xx) on Thu 6 Oct 2005 at 15:19
"The same could be done with TCL, which is more powerful and widely spreaded, but more heavy and slowly."

More powerful? Why would you say so?

[ Parent | Reply to this comment ]

Posted by divan (83.170.xx.xx) on Thu 6 Oct 2005 at 23:37
It's my subjective opinion. At least, there are more TCL bindings for various APIs then LUA.

[ Parent | Reply to this comment ]

Posted by Anonymous (65.5.xx.xx) on Mon 17 Oct 2005 at 15:48
I don't know much about TCL, but writing a Lua library binding is pretty easy even without tolua++/swig/luabind.

It's what the language was meant for, i think.

[ Parent | Reply to this comment ]

Posted by Anonymous (193.54.xx.xx) on Wed 19 Oct 2005 at 12:02
I have to say that scripting has looked like an Eldorado for me for a long time. Thanks for a nice and clear example of what scripting involves as steps in both parts (the C part and the script language part).

I'll certainly capitalize on that work while doing my script-embedding preparations for my program.

Is Lua good at numbers, I mean at dealing with the equivalent of double number in C with a wealth of decimal digits ?

I'm still trying to find a scripting language that looks most like C, as I feel comfortable with C. Any ideas ?

Cheers,

Filippo Rusconi
polyxmass.org

[ Parent | Reply to this comment ]

Posted by Steve (82.41.xx.xx) on Wed 19 Oct 2005 at 13:47
[ View Steve's Scratchpad | View Weblogs ]

Lua is good at dealing with numbers, so you shouldn't have any worries there.

For example:

third = ( 1 / 3 )
print ( third * 3 )
1

Other languages used for scripting with a C syntax? I'm not too sure to be honest. I know that there are embeddable versions of C, such as CInt, but I've only ever used Perl, Python, or LUA for embedding. Of those Lua is the most C-like, and actually the simplest to embed and use..

My recent project for writing security scanning scripts embeds LUA, and provides features such as port-scanning, and service detection. Using a C++ core and a set of LUA scripts it's very simple to develop with - somethign I'd not have otherwise attempted ..

Steve
--

[ Parent | Reply to this comment ]

Posted by debian88 (68.122.xx.xx) on Tue 12 Dec 2006 at 07:44

for pure c syntax scripting language,
embeddable C/C++ interpreter Ch might be a solution for you:

http://www.softintegration.com/products/sdk/embedch/

[ Parent | Reply to this comment ]

Posted by Anonymous (213.54.xx.xx) on Thu 29 Mar 2007 at 11:17
At least in debian 4.0 you'll also need:

apt-get install liblualib50-dev

[ Parent | Reply to this comment ]

Posted by Anonymous (69.181.xx.xx) on Thu 10 Jun 2010 at 00:45
I know this is about 5 years late, but, thank you. This article helped me so much.

[ Parent | Reply to this comment ]

Posted by Anonymous (71.97.xx.xx) on Mon 12 Mar 2012 at 22:00
Thank you for writing this excellent introduction! It's still relevant & applicable today besides some minor API changes that have occurred over the years :)

[ Parent | Reply to this comment ]

Posted by Anonymous (173.176.xx.xx) on Fri 4 May 2012 at 17:48
very helpful article.

[ Parent | Reply to this comment ]

Posted by Anonymous (142.231.xx.xx) on Thu 5 Jul 2012 at 19:39
Yes. Despite the deprecation of some functions in the article, it is by far the clearest step-by-step I've found for embedding Lua in C/C++.

Directly working with the stack is much better than using luabind, et al. If for nothing else, working with the stack allows one to supply a variable number of arguments to Lua functions. This is apparently impossible with luabind.

Thanks for writing this detailed article!

[ Parent | Reply to this comment ]

Sign In

Username:

Password:

[Register|Advanced]

 

Flattr

 

Current Poll

What do you use for configuration management?








( 673 votes ~ 10 comments )

 

 

Related Links