3.16 – Defining C Functions

Lua can be extended with functions written in C. These functions must be of type lua_CFunction, which is defined as

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

A C function receives a Lua state and returns an integer, the number of values it wants to return to Lua.

In order to communicate properly with Lua, a C function must follow the following protocol, which defines the way parameters and results are passed: A C function receives its arguments from Lua in its stack in direct order (the first argument is pushed first). So, when the function starts, its first argument (if any) is at index 1. To return values to Lua, a C function just pushes them onto the stack, in direct order (the first result is pushed first), and returns the number of results. Any other value in the stack below the results will be properly discharged by Lua. Like a Lua function, a C function called by Lua can also return many results.

As an example, the following function receives a variable number of numerical arguments and returns their average and sum:

  1. static int foo (lua_State *L) {
  2. int n = lua_gettop(L); /* number of arguments */
  3. lua_Number sum = 0;
  4. int i;
  5. for (i = 1; i <= n; i++) {
  6. if (!lua_isnumber(L, i)) {
  7. lua_pushstring(L, "incorrect argument to function `average'");
  8. lua_error(L);
  9. }
  10. sum += lua_tonumber(L, i);
  11. }
  12. lua_pushnumber(L, sum/n); /* first result */
  13. lua_pushnumber(L, sum); /* second result */
  14. return 2; /* number of results */
  15. }

To register a C function to Lua, there is the following convenience macro:

  1. #define lua_register(L,n,f) \
  2. (lua_pushstring(L, n), \
  3. lua_pushcfunction(L, f), \
  4. lua_settable(L, LUA_GLOBALSINDEX))
  5. /* lua_State *L; */
  6. /* const char *n; */
  7. /* lua_CFunction f; */

which receives the name the function will have in Lua and a pointer to the function. Thus, the C function foo above may be registered in Lua as average by calling

  1. lua_register(L, "average", foo);