Imbedding Lua in your own applications

Posted by Nick Gammon on Fri 24 Mar 2006 03:46 AM — 12 posts, 59,724 views.

Australia Forum Administrator #0
This material is covered in the Lua documentation, but I will do my own version here to help people who are relying on this site for Lua information. :)

Let's start with a simple C program that creates a Lua "script space" and runs a simple literal script:


#include "lua.h"

const char * myscript = 
  " print ('Hello, world. Lua version is', _VERSION)";

int main (void)
  {
  
  lua_State * L = lua_open();   /* opens Lua */
  luaopen_base(L);              /* opens the basic library */
  
  luaL_loadbuffer (L, myscript, strlen (myscript), "example");  /* compile */

  lua_pcall (L, 0, 0, 0);  /* execute */
          
  lua_close (L);  /* close Lua */
  return 0;
}  /* end of main */



This has the absolute basics. It:

  • Creates a script space (in the variable L)
  • Adds to it the basic library functions (such as 'print')
  • Compiles the script (with luaL_loadbuffer)
  • Executes the script (with lua_pcall)
  • Closes the script space
  • Exits


I needed the lua.h file from the Lua distribution, and the lua50.lib file from the Windows binary distribution. The file lua50.dll also needs to be somewhere in your path.

I compiled it like this (under Cygwin):


gcc test.c lua50.lib -o test.exe


This compiled test.c (the above file) and linked in the lua50.lib library. Then running it gives the expected results:


Hello, world. Lua version is Lua 5.0.2

Amended on Fri 24 Mar 2006 03:47 AM by Nick Gammon
Australia Forum Administrator #1
One major problem with the above example is that there is no error handling. If the script has errors you won't know about them, it will silently fail. This more elaborate example will detect and display any compile or runtime error:


#include <stdio.h>
#include "lua.h"


const char * myscript = " print ('Hello, world. Lua version is', _VERSION)";

int main (void)
  {
  int error;
  
  lua_State * L = lua_open();   /* opens Lua */
  luaopen_base(L);              /* opens the basic library */
  
  error = 
     luaL_loadbuffer (L, myscript, strlen (myscript), "example") ||
     lua_pcall (L, 0, 0, 0);
          
  if (error)
    {
    fprintf (stderr, "%s", lua_tostring (L, -1));
    lua_pop (L, 1);
    }  

  lua_close (L);
  return 0;
}  /* end of main */



If either the call to compile, or execute, fails it returns a non-zero value, and the error message is left on top of the Lua stack, which can be obtained by calling lua_tostring.

This example simply displays the message, although obviously you could put it into a dialog box, echo it to your user, or whatever is appropriate.

Let's test that with different scripts. If the script is:


const char * myscript = " print (2 'nick') ";


Then I see:


[string "example"]:1: `)' expected near `'nick''


That was a compile-time error. However if I make a runtime error (adding a number to a string, then you get a different message:


const char * myscript = " print (2 + 'nick') ";


This gives:


[string "example"]:1: attempt to perform arithmetic on a string value
Australia Forum Administrator #2
Now let's try a more elaborate example. This one will read in a file (an obvious example here is to read a configuration file for a program). The file is loaded and compiled, then executed, effectively putting any assignments it does into the Lua script space.

My example Lua file is this:


a = 22
b = 33
c = "nick"

-- do tests if wanted

if a < b then
  a = 99
end -- if



The advantage of using Lua is that you can do tests or arithmetic inside the configuration file (and put comments).

Here is the code to read it in. We also extract a couple of values from the Lua script space back into C space:


#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include "lua.h"

void error (lua_State *L, const char *fmt, ...) 
  {
  va_list argp;
  va_start (argp, fmt);
  vfprintf (stderr, fmt, argp);
  va_end (argp);
  lua_close (L);
  exit (1);
  }  /* end of error function */
  
int main (void)
  {
    
  int a, b;
  const char * c;
  
  lua_State * L = lua_open();   /* opens Lua */
  luaopen_base(L);              /* opens the basic library */
  
  if (luaL_loadfile(L, "myconfig.lua") ||
      lua_pcall (L, 0, 0, 0))
    error (L, "Error loading file: %s", lua_tostring (L, -1));

  /* get value of "a" */
  
  lua_getglobal (L, "a");
  if (!lua_isnumber (L, -1))
    error (L, "'a' should be a number\n");
  a = (int) lua_tonumber (L, -1);
  printf ("Found value of 'a' to be %i\n", a);
  lua_pop (L, 1);
  
  /* get value of "c" */
  
  lua_getglobal (L, "c");
  if (!lua_isstring (L, -1))
    error (L, "'c' should be a string\n");
  c = lua_tostring (L, -1);
  printf ("Found value of 'c' to be %s\n", c);
  lua_pop (L, 1);

  
  lua_close (L);
  return 0;
}  /* end of main */



This example also uses a more elaborate error function (suggested from the Lua book). This error function reports the error and terminates the application, however you may find it more useful to report the error to the user and continue processing.
Australia Forum Administrator #3
The above example uses the Lua API functions to extract numbers and strings from the global address space. An obvious enhancement for a bigger application would be to make that extraction into a small function (one for numbers, one for strings, and so on). You might also handle defaults by testing for nil, and if so substituting some default value.
Australia Forum Administrator #4
Now let's look at adding in the normal Lua libraries. This simply involves a few more lines of initialization code:


#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include "lua.h"

void error (lua_State *L, const char *fmt, ...) 
  {
  va_list argp;
  va_start (argp, fmt);
  vfprintf (stderr, fmt, argp);
  va_end (argp);
  lua_close (L);
  exit (1);
  }  /* end of error function */
  
int main (void)
  {
  
  lua_State * L = lua_open();   /* opens Lua */
  luaopen_base(L);              /* opens the basic library */
  luaopen_table(L);             /* opens the table library */
  luaopen_io(L);                /* opens the I/O library */
  luaopen_string(L);            /* opens the string lib. */
  luaopen_math(L);              /* opens the math lib. */
  luaopen_loadlib(L);           /* loadlib function */
  luaopen_debug(L);             /* debug library  */
  
  if (luaL_loadfile(L, "myconfig.lua") ||
      lua_pcall (L, 0, 0, 0))
    error (L, "Error loading file: %s", lua_tostring (L, -1));

  lua_close (L);
  return 0;
}  /* end of main */



Running this on this file:


for i = 1, 10 do
  print (i, math.sqrt (i))
end -- if


... gives:


1       1
2       1.4142135623731
3       1.7320508075689
4       2
5       2.2360679774998
6       2.4494897427832
7       2.6457513110646
8       2.8284271247462
9       3
10      3.1622776601684


Note in this example we have used a function in the math library.
Australia Forum Administrator #5
If you decide to use Lua 5.1 (rather than 5.0.2) then opening the libraries is simpler, and indeed required to be done this simpler way or it crashes:


#define _WIN32

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

void error (lua_State *L, const char *fmt, ...) 
  {
  va_list argp;
  va_start (argp, fmt);
  vfprintf (stderr, fmt, argp);
  va_end (argp);
  lua_close (L);
  exit (1);
  }  /* end of error function */
  
int main (void)
  {
 
  lua_State * L = luaL_newstate ();   /* opens Lua */
  luaL_openlibs (L);                  /* new way of opening all libraries */
  
  if (luaL_loadfile(L, "myconfig.lua") ||
      lua_pcall (L, 0, 0, 0))
    error (L, "Error loading file: %s", lua_tostring (L, -1));

  lua_close (L);
  return 0;
}  /* end of main */



The main change is that the multiple lines which opened the libraries are replaced by a single line (luaL_openlibs) which opens all of the standard libraries for you.
USA #6
Great timing Nick. I needed something to use for a guide, and I think this may help get me on my way, plus I know alot of others will love to have the info
USA #7
This is odd.

Nick, in your latest post (the one before Dralnu's) the time of the post is not shown. A bug?

Anyways. I am considering imbedding Lua into Project CR, but I don't see where I would use it. Hm.
Amended on Fri 24 Mar 2006 11:19 AM by Zeno
USA #8
The time shows up for me. It's at 10:00 PM. Maybe if you're in Central Time, or whatever is Pacific+2, it's exactly midnight, which confuses something?

For your project, I'd consider using Lua as a data storage mechanism. And then you can use it as a replacement for mudprog. Lua would excel in both areas.
USA #9
You're right, it's my time zone. It shows up when I'm not logged in. I guess it has issues displaying if it's 0?

Well I won't have any mudprogs. Data storage? As in maybe player files, game options etc?
USA #10
Quote:
As in maybe player files, game options etc?
Precisely. These are really just key-value pairs, i.e. "min_level_avatar" = 51 in SMAUG.

In Lua, you can set up your settings as a table, and then use serialization classes (Nick wrote an example around here somewhere) to output the table to a Lua script. Then the next time you need to load the data, you just load the script, and in doing so you will have loaded up tables to say what exactly has been done. :)
Australia Forum Administrator #11
Yes, Lua would really lend itself to reading in those sorts of things. Examples would be:

  • MUD configuration (eg. name, port number, welcome message)
  • Player files
  • Classes, races, spells
  • Areas


The beauty of Lua is that you basically load in the entire file in one line of code. If you decide to change the files later the loading code doesn't have to be changed. Also the files can be complex (eg. spells might contain multiple effects, which can be in a table, players might have inventory which contain bags which contain potions - these can be tables of tables).