Extending Lua: Making your own libraries.

Requirments:
    1: The Lua Sources.
    2: A C compiler - cc/gcc/g++ for Unix, and Visual C++ for Windows.
Other compilers should work under Windows, basically any C compiler - I will cover compiling under Windows at the end of the tutorial.

    3: make. Unix systems will need it, as this is how Lua is installed by default, and how I do in this tutorial. Luckily practically every Linux user will already have 'make'. Under Windows this is different, I will explain at the end.
This tutorial will work under any OS which Lua has compiled succesfully on, you may have to compile things by hand (rather than 'make') - Unfortunately I can't help you with this, as it is beyond the scope of this tutorial.

    4: And last of all - a good knowledge of programming in C.

So you have got hold of the sources for the "Lua" programming language, you have compiled it, you have learned how to use it - but now you have noticed how small the language is.
Well as Lua is open-source, you can extend it how you want! The easiest way to extend Lua is to write extra functions in Lua, but you are limited to what Lua already supports. Maybe you wish to write a library which adds network support? This is what this tutorial is all about.

I will show you how to write your own extensions for Lua, and when I say extend - I mean actually change the original Lua source code written in C. The simple example I show you here adds a network library ('net'), with one basic function ('uname'). The 'uname' function is the C function 'uname', which when called; puts the results in the structure defined by 'utsname' (defined in the <utsname.h> header file). I have not re-written it, just added access to it (Like most of the other functions in Lua). It doesn't work in the same way as in C, as this is only meant to be a basic tutorial.

How you will use it when it is all done is like so:
-- Lua script: uname.lua
-- Start
print("One at a time: \n")
net.uname("sysname")
print()
net.uname("nodename")
print()
net.uname("release")
print()
net.uname("version")
print()
net.uname("machine")
print("\n");
print("All at once: \n")
net.uname("all")
print()
-- End

As you can see, it will give an easy interface. For those that don't know what the ustname structure in C is:
It is a structure which holds information about the machine the program is running on. For example, this is what is displayed after running that above script on one of my computers (Of course, it won't work on yours yet, as you haven't written the library!):
One at a time:

sysname  : Linux
nodename : desktop1.xxx.xxx
release  : 2.4.22-21mdk
version  : #1 Fri Oct 24 22:43:28 MDT 2003
machine  : i686

All at once:

sysname  : Linux
nodename : desktop1.xxx.xxx
release  : 2.4.22-21mdk
version  : #1 Fri Oct 24 22:43:28 MDT 2003
machine  : i686

I have xxx part of the nodename response for obvious reasons. Anyway, now the moment you have been waiting for: The code itself. Sorry if this page goes on and on, but I will try and make it clear to read.

Create a new text file. Save it as "lnetlib.c" in the "src/lib" directory of the Lua sources. Enter this code into that file:
#include <stdlib.h>
#include <sys/utsname.h>
#include <string.h>

#define lnetlib_c	/* Define the library */

/* Include the Lua API header files */
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

/* The 'uname' function.
** It accepts a Lua State (Don't worry about this),
** and doesn't return anything.
*/
static int net_uname (lua_State *L)
{
	const char *option = luaL_checkstring(L, 1); 	/* Reads the string parameter */
	int z;
	struct utsname u_name;	/* Creates an instance of the utsname structure */

	/* Uses the C function 'uname' and stores the
	** results in the structure we just created.
	** You could add some error checking, by testing the value of z -
	** But I cut this out, though you should add it.
	*/
	z = uname(&u_name);

	/* Detects which option was passed to the function,
	** and prints the correspnding uname response.
	** Again, you should add error checking.
	*/
	if ((strcmp(option, "sysname")) == 0)
	{
		printf("sysname  : %s", u_name.sysname);
	}
	else if ((strcmp(option, "nodename")) == 0)
	{
		printf("nodename : %s", u_name.nodename);
	}
	else if ((strcmp(option, "release")) == 0)
	{
		printf("release  : %s", u_name.release);
	}
	else if ((strcmp(option, "version")) == 0)
	{
		printf("version  : %s", u_name.version);
	}
	else if ((strcmp(option, "machine")) == 0)
	{
		printf("machine  : %s", u_name.machine);
	}
	else if ((strcmp(option, "all")) == 0)
	{
		printf("sysname  : %s\n", u_name.sysname);
		printf("nodename : %s\n", u_name.nodename);
		printf("release  : %s\n", u_name.release);
		printf("version  : %s\n", u_name.version);
		printf("machine  : %s\n", u_name.machine);
	}
	return 0;
}

/* Now, this defines the functions you have created, into the net library group.
** The right hand name ('net_uname'), is the name of the function in the C code.
** The left hand name, is the name you want it to be called from Lua.
** Take note of the names of variables, they correspond to your 'net' library.
** If you changed the name of the library, you would have to change these
** variables and names.
*/

static const luaL_reg netlib[] = {
  {"uname", net_uname},
  {NULL, NULL}
};

/* This defines a function that opens up your library. */

LUALIB_API int luaopen_net (lua_State *L) {
  luaL_openlib(L, LUA_NETLIBNAME, netlib, 0);
  return 1;
}

Make you sure you save that file. We are nearly done! We just have to 'register' the library in Lua, and make it accesable. Open up the "lualib.h" file in the "/include" directory of the Lua sources. Locate the lines of code that look like:
#define LUA_MATHLIBNAME	"math"
LUALIB_API int luaopen_math (lua_State *L);
Then add the following beneath those lines:
#define LUA_NETLIBNAME	"net"
LUALIB_API int luaopen_net (lua_State *L);

Save that file, now open up "lua.c" in the "/src/lua/" directory. Find a section which looks like this:
static const luaL_reg lualibs[] = {
  {"base", luaopen_base},
  {"table", luaopen_table},
  {"io", luaopen_io},
  {"string", luaopen_string},
  {"math", luaopen_math},
  {"debug", luaopen_debug},
  {"loadlib", luaopen_loadlib},
  /* add your libraries here */
  LUA_EXTRALIBS
  {NULL, NULL}
};
Add a line for your library, so now it looks like this:
static const luaL_reg lualibs[] = {
  {"base", luaopen_base},
  {"table", luaopen_table},
  {"io", luaopen_io},
  {"string", luaopen_string},
  {"math", luaopen_math},
  {"debug", luaopen_debug},
  {"loadlib", luaopen_loadlib},
  {"net", luaopen_net},
  /* add your libraries here */
  LUA_EXTRALIBS
  {NULL, NULL}
};

Now we are done! We just have to re-compile Lua. So make sure you have saved all of the above files. If you install Lua by using the 'make' method, you need to do one thing first:
Open up the Makefile in the "/src/lib/" directory of the Lua sources, and add the the location of your library source code to the SRCS and OBJS variables. So that section should now look like this:
OBJS= lauxlib.o lbaselib.o ldblib.o liolib.o lmathlib.o ltablib.o lstrlib.o loadlib.o lnetlib.o
SRCS= lauxlib.c lbaselib.c ldblib.c liolib.c lmathlib.c ltablib.c lstrlib.c loadlib.c lnetlib.c

Now save that, and install Lua like you usually do: (You may have to be root to do this)
make
make install

Now you can try the Lua script I used at the very top of this tutorial. And run it like any other Lua script:
lua uname.lua

And it should work! You have now added to the Lua programming language. Of course the above example is not really very useful, you are welcome to add more and more functions, and more and more libraries. If you want to do it so your function return values, rather than print out values from C - A good place to start reading is the Official Lua API documentation.

Windows users:

I expect a lot of Windows users install Lua by using third-party binary installations. So to use the above example, you are going to need to get the official source code. Go to Lua website and download it now. Compile Lua by following the instructions in the Install file, Visual C++ users will find it very easy, as all you have to do is create 4 projects (1 for each part of Lua: standard library, core library, lua and luac), add the files needed for each one and build - that's it.
So to re-compile Lua to use your new library, just do the same thing over again.
For those of you that have probably noticed, yes... we have added to the 'standard library'.

Well that's it... I hope you have enjoyed reading this tutorial, and are making good use of the knowledge gained from it. I wrote this tutorial becuase I could not find much information about extending Lua, so I just read the whole Lua source code - to see what was happening, and hacked my own code until it worked. So I am passing my knowledge to other people.
If I happen to find any other good tutorials on the Internet about extending Lua, and other parts to do with the Lua API - I will add them here.

Feel free to email any comments to comments@heavycoder.com

Recommended reading: Programming in Lua (Amazon)