A complex library API can often be wrapped quickly and (nearly) completely using SWIG. An advantage of using SWIG in this case is that it is easy to build SWIG-based wrappers that enable use of the library in 18 major languages including Lua, Perl, Python, Ruby, and Java, among others.
If Lua is your preferred (and possibly only) concern, then I'd recommend learning to use luaL_register()
at the core of a strategy to build Lua modules in C. An advantage of building a module this way is that you keep all of your functions in a single name space without any overhead. You will need to craft a wrapper function that matches the Lua C function calling convention (just as you do with lua_register()
) and that gathers the Lua arguments from the stack, calls the wrapped function, and pushes any return value and out parameters back to the Lua stack. A good overview of how to go about this can be found in the book Programming in Lua. The online copy of the first edition discusses library creation in Chapter 26, but was written for Lua 5.0. I strongly urge anyone seriously using Lua to own a copy of the current edition of PiL.
Unfortunately, one area where Lua 5.1 differs the most from 5.0 is in the dynamic loading of modules (both C and Lua) with require
.
Here's a complete (if small) example for a C library that works in Lua 5.1. We start with the implementation of the wrapper in a C file:
#include <lua.h>
#include <luaxlib.h>
#include <math.h>
#undef PI
#define PI (3.14159265358979323846)
static int l_sin (lua_State *L) {
double r = luaL_checknumber(L,1);
lua_pushnumber(L, sin(r));
return 1;
}
static int l_cos (lua_State *L) {
double r = luaL_checknumber(L,1);
lua_pushnumber(L, cos(r));
return 1;
}
static const struct luaL_reg smlib [] = {
{"sin", l_sin},
{"cos", l_cos},
{NULL, NULL} /* sentinel */
};
int luaopen_sm (lua_State *L) {
luaL_openlib(L, "sm", smlib, 0);
lua_pushnumber(L,PI);
lua_rawset(L,-2,"pi");
return 1;
}
Note in particular that the only function that need be exported is luaopen_sm()
, whose name must correspond to the name of the module that will be used with require
, and with the name of the DLL file. With that file compiled as a DLL named sm.dll
(probably named libsm.so
on Unix-like systems), then it can be loaded and used in a Lua script like this:
require "sm"
print(sm.sin(sm.pi/3), sm.cos(sm.pi/3));
This example, although untested, should compile and run. For a complete example wrapping most functions from math.h
, see the source to the math
module that is distributed with Lua. Because these thin wrappers contain a lot of repetitive code, tools like SWIG are often able to create them given only the declaration of each function.
Wrapping methods of a C++ class is similar in principle. Each Lua-callable wrapper function is going to need an argument that can be mapped into this
on the C++ side, and it must be implemented as either a module-static function or static member function that also locates the target object instance as well as converts the other arguments. SWIG is particularly good at constructing this kind of wrapper and hides a lot of gory details along the way.