Calling C functions from Lua

Because functions in C and Lua work so differently, exposing a C function to Lua can get a bit tricky. All C functions that Lua can call must follow the signature of lua_CFunction, which is defined in lua.h as the following:

typedef int (*lua_CFunction) (lua_State *L);

This function takes only one argument, the lua_State. The return value of the function is an integer. This integer is the number of elements that the function pushed onto the stack as return values.

Lua has multiple stacks—each C function called from Lua has its own stack and does not share the global stack.

Let's take for example a simple C function that returns the magnitude of a three-dimensional vector. In C, the code for doing so might look something like the following:

double Vec3Magnitude(double x, double y, double z) {
double dot = x * x + y * y + z * z;
if (dot == 0.0) {
return 0.0;
}
return sqrt(dot);
}

The preceding function can't be exposed to Lua directly because it doesn't follow the lua_CFunction signature. There are two ways to expose this function, either to re-write it or to write a wrapper function for it. Both approaches are similar. The following is a rewritten example:

int LuaVec3Magnitude(lua_State* L) {
double x = lua_tonumber(L, 3);
double y = lua_tonumber(L, 2);
double z = lua_tonumber(L, 1);

lua_pop(L, 3);

double dot = x * x + y * y + z * z;
if (dot == 0.0) {
lua_pushnil(L);
}
else {
lua_pushnumber(L, sqrt(dot));
}

return 1;
}

The preceding function can be called from Lua. Before being called, it must be registered. Registering a function means it first needs to be pushed onto the stack with the  lua_pushcfunction function. Next, the function on the stack needs to be assigned to a variable with lua_setglobal. The code that follows registers the LuaVec3Magnitude function to be available in Lua:

lua_pushcfunction(L, LuaVec3Magnitude);
lua_setglobal(L, "Vec3Magnitude");

After the LuaVec3Magnitude function is registered as Vec3Magnitude in Lua, it can be called at any time.

Rewriting a function is not always possible, but you can still write a wrapper function. For example, we could create a function called LuaWrapperVec3Magnitude that interfaces with Lua, but instead of doing the work of Vec3Magnitude, it just calls the Vec3Magnitude function. Then, we can expose LuaWrapperVec3Magnitude as Vec3Magnitude to Lua.

The following code demonstrates this:

int LuaWrapperVec3Magnitude(lua_State* L) {
double x = lua_tonumber(L, 3);
double y = lua_tonumber(L, 2);
double z = lua_tonumber(L, 1);

lua_pop(L, 3);

// Call the original function so it is responsible
// for doing the actual work
double result = Vec3Magnitude(x, y, z);

if (dot == 0.0) {
lua_pushnil(L);
}
else {
lua_pushnumber(L, result);
}

return 1;
}

// Code to expose the wrapper function:
lua_pushcfunction(L, LuaWrapperVec3Magnitude);
lua_setglobal(L, "Vec3Magnitude");
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.14.98