Litt's Lua Laboratory:

Calling Lua From a C Program

(With Snippets)

Contents

Introduction

Anatomy of a Lua Call

Create a Lua state variable

Load Lua libraries

Load but don't run the Lua script file

Priming script run to create script's global vars



Pass all arguments to the Lua script on the stack

Run the loaded Lua script

Retrieve the return from the Lua script



Close the Lua state variable

Create a Lua state variable

lua_State *L;

L = luaL_newstate();

Load Lua libraries

luaL_openlibs(L);

Load but don't run the Lua script file

luaL_loadfile(L, "script.lua")

Priming run of the loaded Lua script

to create the script's global variables



lua_pcall (

L,

number_of_args,

number_of_returns,

errfunc_idx

); Pass all arguments to the Lua script on the stack

Various routines, too many to name.

Run the loaded Lua script

lua_pcall (

L,

number_of_args,

number_of_returns,

errfunc_idx

);

Retrieve the return from the Lua script

Various pops and stack calls

Close the Lua state variable

lua_close(L);



Header Files

#include <lua.h>

#include <lauxlib.h>

#include <lualib.h>

Compiling and Linking

cc -Wall -o hello hello.c -I/path/to/Lua/includes -L/path/to/Lua/libraries -llibLuaName -lm



cc

Your systems C compilation command. Could be cc , or gcc , or something else.

-Wall

Show all warnings. You might choose not to insert this until all other errors and warnings have been fixed.

-o hello

The name of the finished executable.

hello.c

The name of the C file to compile.

-I/path/to/Lua/includes

Every C compiler checks for include files in certain places. But often lua.h , liblua.h and lauxlua.h aren't in those places, so you need to tell the compiler where to find them.

-L/path/to/Lua/libraries



Every C compiler checks for libraries to link in certain places. But often the Lua libraries are elsewhere, so you need to tell the compiler where to find them.

-llibLuaName



Link in the Lua library. The way you name this library in the compiler's -l option requires some thought and research, which will be explained later in this subsection.

-lm

Link in the math library (often not necessary)



The Process of Finding the Right Compile Command

Try to compile, no link, no -Wall, no -I. You might get lucky.

Find the location of the Lua include files. Using that location, compile, no link, no -Wall. Compile, no link, use -Wall. Find the location and filenames of the Lua library files. Using the location and filenames, compile and link. Test the Executable.



Step 1: Try to compile, no link, no -Wall, no -I

cc -o hello hello.c -c



slitt@mydesk:~$ cc -o hello hello.c -c

hello.c:8:91: error: lua.h: No such file or directory

hello.c:9:91: error: lauxlib.h: No such file or directory

hello.c:10:85: error: lualib.h: No such file or directory

hello.c:16: error: expected ‘)’ before ‘*’ token

hello.c: In function ‘main’:

hello.c:24: error: ‘lua_State’ undeclared (first use in this function)

hello.c:24: error: (Each undeclared identifier is reported only once

hello.c:24: error: for each function it appears in.)

hello.c:24: error: ‘L’ undeclared (first use in this function)

slitt@mydesk:~$



Step 2: Find the location of the Lua include files

lua.h

lauxlib.h

lualib.h

locate

lua.h

updatedb

locate

lua.h

lauxlib.h

lualib.h

slitt@mydesk:~$ sudo updatedb

[sudo] password for slitt:

slitt@mydesk:~$ locate /lua.h

/usr/include/lua5.1/lua.h

/usr/include/lua5.1/lua.hpp

/usr/share/doc/lua5.1-doc/doc/lua.html

/usr/share/doc/lua5.1-doc/etc/lua.hpp

slitt@mydesk:~$



/usr/include/lua5.1

slitt@mydesk:~$ ls /usr/include/lua5.1/lualib.h

/usr/include/lua5.1/lualib.h

slitt@mydesk:~$ ls /usr/include/lua5.1/lauxlib.h

/usr/include/lua5.1/lauxlib.h

slitt@mydesk:~$



Step 3: Using that location, compile, no link, no -Wall

cc -o hello hello.c -I /usr/include/lua5.1 -c

slitt@mydesk:~$ cc -o hello hello.c -I/usr/include/lua5.1 -c

slitt@mydesk:~$



hello.c

hello.c

Step 4: Compile, no link, use -Wall

cc -o hello hello.c -Wall -I/usr/include/lua5.1 -c



slitt@mydesk:~$ cc -o hello hello.c -Wall -I/usr/include/lua5.1 -c

slitt@mydesk:~$

Step 5: Find the location and filenames of the Lua library files

-l

-l

-llua

liblua.a

liblua.o

liblua.so

liblua.*

sudo updatedb

locate liblua | less



/usr/lib/liblua5.1-sql-postgres.a

/usr/lib/liblua5.1-sql-postgres.la

/usr/lib/liblua5.1-sql-postgres.so

/usr/lib/liblua5.1-sql-postgres.so.2

/usr/lib/liblua5.1-sql-postgres.so.2.0.0

/usr/lib/liblua5.1-sql-sqlite3.so.2

/usr/lib/liblua5.1-sql-sqlite3.so.2.0.0

/usr/lib/liblua5.1-unix.so.2

/usr/lib/liblua5.1-unix.so.2.0.0

/usr/lib/liblua5.1.a

/usr/lib/liblua5.1.la

/usr/lib/liblua5.1.so

/usr/lib/liblua5.1.so.0

/usr/lib/liblua5.1.so.0.0.0

/usr/lib/liblua50.so.5

/usr/lib/liblua50.so.5.0

/usr/lib/liblualib50.so.5

/usr/lib/liblualib50.so.5.0

/usr/lib/debug/usr/lib/liblua5.1.so.0.0.0



liblua5.1.*

-l

lua5.1

-L

/usr/lib/lua

-L

Step 6: Using the location and filenames, compile and link

We remove the -c so that it will attempt to link We add -llua5.1



slitt@mydesk:~$ cc -o hello hello.c -Wall -I/usr/include/lua5.1 -llua5.1

slitt@mydesk:~$



Step 7: Test the Executable

slitt@mydesk:~$ ./hello

In C, calling Lua

This is coming from lua.

Back in C again

slitt@mydesk:~$



hello.c

helloscript.lua

Summary

Create a Lua state variable

lua_State *L;

L = luaL_newstate();

Load Lua libraries

luaL_openlibs(L);

Load but don't run the Lua script file

luaL_loadfile(L, "script.lua")

Priming run of the loaded Lua script

to create the script's global variables



lua_pcall (

L,

number_of_args,

number_of_returns,

errfunc_idx

); Pass all arguments to the Lua script on the stack

Various routines, too many to name.

Run the loaded Lua script

lua_pcall (

L,

number_of_args,

number_of_returns,

errfunc_idx

);

Retrieve the return from the Lua script

Various pops and stack calls

Close the Lua state variable

lua_close(L);



lua.h

lauxlib.h

lualib.h

-I

-l

-L

Try to compile, no link, no -Wall, no -I. You might get lucky.

Find the location of the Lua include files. Using that location, compile, no link, no -Wall. Compile, no link, use -Wall. Find the location and filenames of the Lua library files. Using the location and filenames, compile and link. Test the Executable.

Hello World

helloscript.lua

io.write("This is coming from lua.

")



#!/usr/bin/lua

lua_pcall

()

Create a Lua state variable

lua_State *L;

L = luaL_newstate();

Load Lua libraries

luaL_openlibs(L);

Load but don't run the Lua script file

luaL_loadfile(L, "script.lua")

Priming run of the loaded Lua script

to create the script's global variables



lua_pcall (

L,

number_of_args,

number_of_returns,

errfunc_idx

); Pass all arguments to the Lua script on the stack

Various routines, too many to name.

Run the loaded Lua script

lua_pcall (

L,

number_of_args,

number_of_returns,

errfunc_idx

);

Retrieve the return from the Lua script

Various pops and stack calls

Close the Lua state variable

lua_close(L);



Priming run of the loaded Lua script

Passing arguments to the Lua script

Retrieving the return from the Lua script



bail()

hello.c

#include <lua.h> /* Always include this when calling Lua */

#include <lauxlib.h> /* Always include this when calling Lua */

#include <lualib.h> /* Always include this when calling Lua */



#include <stdlib.h> /* For function exit() */

#include <stdio.h> /* For input/output */



void bail(lua_State *L, char *msg){

fprintf(stderr, "

FATAL ERROR:

%s: %s



",

msg, lua_tostring(L, -1));

exit(1);

}



int main(void)

{

lua_State *L;



L = luaL_newstate(); /* Create Lua state variable */

luaL_openlibs(L); /* Load Lua libraries */



if (luaL_loadfile(L, "helloscript.lua")) /* Load but don't run the Lua script */

bail(L, "luaL_loadfile() failed"); /* Error out if file can't be read */



printf("In C, calling Lua

");



if (lua_pcall(L, 0, 0, 0)) /* Run the loaded Lua script */

bail(L, "lua_pcall() failed"); /* Error out if Lua file has an error */



printf("Back in C again

");



lua_close(L); /* Clean up, free the Lua state var */



return 0;

}



slitt@mydesk:~$ cc -o hello hello.c -Wall -I/usr/include/lua5.1 -llua5.1

slitt@mydesk:~$



-Wall

slitt@mydesk:~$ ./hello

In C, calling Lua

This is coming from lua.

Back in C again

slitt@mydesk:~$



lua_pcall()

lua_pcall()

Calling Subroutines, Sending Arguments and Receiving Returns

except

Priming run of the loaded Lua script

Passing arguments to the Lua script

Retrieving the return from the Lua script

Tourist Trap Alert



All over the Internet, including on some of the Lua project's own documentation, you'll see hints basically telling you that to call a C subroutine you do:



lua_getglobals(L, "subroutine_name")

push_args_to_subroutine

if(lua_pcall(L, num_of_args, num_of_returns, 0)

error_routine();



These Internet instructions say nothing about doing a priming lua_pcall() , and if you do not do a priming lua_pcall() , you'll get an error message something like this:



attempt to call a nil value



If you get the preceding error message and don't know it's caused by lack of a priming lua_pcall() (or priming lua_call() or priming dofile() ), you're about to have several hours or maybe even days of frustration. Mailing list threads old and new about this error basically tell you RTFM or RTFW, but of course we locals know it was reading the web that got you into this problem in the first place.



I saw one guy who took the opportunity to tell the asker "I'm trying to help you help yourself but you won't take the help, so now I'm putting you on my list of people I won't help." All for what we locals know was probably a simple lack of a priming run THAT SEEMS NOT TO BE DOCUMENTED ANYWHERE!



Before calling a function in a Lua script, do that priming run first!!!!!!



callfuncscript.lua

function tellme()

io.write("This is coming from lua.tellme.

")

end



#include <lua.h> /* Always include this when calling Lua */

#include <lauxlib.h> /* Always include this when calling Lua */

#include <lualib.h> /* Prototype for luaL_openlibs(), */

/* always include this when calling Lua */



#include <stdlib.h> /* For function exit() */

#include <stdio.h> /* For input/output */



void bail(lua_State *L, char *msg){

fprintf(stderr, "

FATAL ERROR:

%s: %s



",

msg, lua_tostring(L, -1));

exit(1);

}



int main(void)

{

lua_State *L;



L = luaL_newstate(); /* Create Lua state variable */

luaL_openlibs(L); /* Load Lua libraries */



if (luaL_loadfile(L, "callfuncscript.lua")) /* Load but don't run the Lua script */

bail(L, "luaL_loadfile() failed"); /* Error out if file can't be read */



/* ABOVE HERE IS HELLO WORLD CODE */



if (lua_pcall(L, 0, 0, 0)) /* PRIMING RUN. FORGET THIS AND YOU'RE TOAST */

bail(L, "lua_pcall() failed"); /* Error out if Lua file has an error */





lua_getglobal(L, "tellme"); /* Tell what function to run */



/* BELOW HERE IS THE HELLO WORLD CODE */

printf("In C, calling Lua

");

if (lua_pcall(L, 0, 0, 0)) /* Run the function */

bail(L, "lua_pcall() failed"); /* Error out if Lua file has an error */

printf("Back in C again

");



lua_close(L); /* Clean up, free the Lua state var */



return 0;

}



tellme()

lua_getglobal()

lua_pcall()

tellme()

Arguments and Return Value

make sure

lua_getglobal()

make sure

Litt Opinion



There are several pieces of example code on the net showing the passing of arguments to entire Lua scripts rather than functions, and those whole Lua scripts returning arguments. They do this by, within C, setting a global variable to be used in Lua. I've chosen not to reproduce these techniques here because:

In my opinion the internals of such code is a little hard to understand I never liked global variables in the first place. Multiple args to the main Lua script require multiple global variables such code becomes quite muddled Functions are built from the ground up to accept a series of arguments.



callfuncscript.lua

function tellme()

io.write("This is coming from lua.tellme.

")

end



function square(n)

io.write("Within callfuncscript.lua fcn square, arg=")

io.write(tostring(n))

n = n * n

io.write(", square=")

io.write(tostring(n))

print(".")

return(n)

end



print("Priming run")



callfunc.c

#include <lua.h> /* Always include this when calling Lua */

#include <lauxlib.h> /* Always include this when calling Lua */

#include <lualib.h> /* Prototype for luaL_openlibs(), */

/* always include this when calling Lua */



#include <stdlib.h> /* For function exit() */

#include <stdio.h> /* For input/output */



void bail(lua_State *L, char *msg){

fprintf(stderr, "

FATAL ERROR:

%s: %s



",

msg, lua_tostring(L, -1));

exit(1);

}



int main(void)

{

lua_State *L;



L = luaL_newstate(); /* Create Lua state variable */

luaL_openlibs(L); /* Load Lua libraries */



if (luaL_loadfile(L, "callfuncscript.lua")) /* Load but don't run the Lua script */

bail(L, "luaL_loadfile() failed"); /* Error out if file can't be read */



if (lua_pcall(L, 0, 0, 0)) /* PRIMING RUN. FORGET THIS AND YOU'RE TOAST */

bail(L, "lua_pcall() failed"); /* Error out if Lua file has an error */



printf("In C, calling Lua->tellme()

");



lua_getglobal(L, "tellme"); /* Tell it to run callfuncscript.lua->tellme() */

if (lua_pcall(L, 0, 0, 0)) /* Run the function */

bail(L, "lua_pcall() failed"); /* Error out if Lua file has an error */



printf("Back in C again

");

printf("In C, calling Lua->square(6)

");



lua_getglobal(L, "square"); /* Tell it to run callfuncscript.lua->square() */

lua_pushnumber(L, 6); /* Submit 6 as the argument to square() */

if (lua_pcall(L, 1, 1, 0)) /* Run function, !!! NRETURN=1 !!! */

bail(L, "lua_pcall() failed");



printf("Back in C again

");

int mynumber = lua_tonumber(L, -1);

printf("Returned number=%d

", mynumber);



lua_close(L); /* Clean up, free the Lua state var */

return 0;

}



Even though two functions are called, only one priming run is needed. Each Lua script requires only one priming run in order to create its globals so its functions are visible to the C program.

In order to fully pass an integer into callfuncscript.lua's square() function and receive the function's return, the lua_pcall()'s nargs argument must be 1 (one argument passed in) and its nreturns argument must be 1 (1 argument passed back). If nargs is 0 you'll get the dreaded " attempt to call a number value" error, while if nreturns is 0 it will always return 0. So remember, this lua_pcall() is lua_pcall(L, 1, 1, 0) .

Passing Tables to Lua Functions

The Lua Program

function tellme()

io.write("This is coming from lua.tellme.

")

end



function square(n)

io.write("Within callfuncscript.lua fcn square, arg=")

io.write(tostring(n))

n = n * n

io.write(", square=")

io.write(tostring(n))

print(".")

return(n)

end



function tweaktable(tab_in)

local tab_out = {numfields=1}

for k,v in pairs(tab_in) do

tab_out.numfields = tab_out.numfields + 1

tab_out[tostring(k)] = string.upper(tostring(v))

end

tab_out.numfields = tostring(tab_out.numfields)

io.write("At bottom of callfuncscript.lua tweaktable(), numfields=")

io.write(tab_out.numfields)

print()

return tab_out

end



print("Priming run")



The C Program

#include <lua.h> /* Always include this when calling Lua */

#include <lauxlib.h> /* Always include this when calling Lua */

#include <lualib.h> /* Prototype for luaL_openlibs(), */

/* always include this when calling Lua */



#include <stdlib.h> /* For function exit() */

#include <stdio.h> /* For input/output */



void bail(lua_State *L, char *msg){

fprintf(stderr, "

FATAL ERROR:

%s: %s



",

msg, lua_tostring(L, -1));

exit(1);

}



int main(void)

{

lua_State *L;



L = luaL_newstate(); /* Create Lua state variable */

luaL_openlibs(L); /* Load Lua libraries */



if (luaL_loadfile(L, "callfuncscript.lua")) /* Load but don't run the Lua script */

bail(L, "luaL_loadfile() failed"); /* Error out if file can't be read */



if (lua_pcall(L, 0, 0, 0)) /* PRIMING RUN. FORGET THIS AND YOU'RE TOAST */

bail(L, "lua_pcall() failed"); /* Error out if Lua file has an error */



printf("In C, calling Lua->tweaktable()

");

lua_getglobal(L, "tweaktable"); /* Tell it to run callfuncscript.lua->tweaktable() */

lua_newtable(L); /* Push empty table onto stack table now at -1 */

lua_pushliteral(L, "fname"); /* Push a key onto the stack, table now at -2 */

lua_pushliteral(L, "Margie"); /* Push a value onto the stack, table now at -3 */

lua_settable(L, -3); /* Take key and value, put into table at -3, */

/* then pop key and value so table again at -1 */



lua_pushliteral(L, "lname"); /* Push a key onto the stack, table now at -2 */

lua_pushliteral(L, "Martinez"); /* Push a value onto the stack, table now at -3 */

lua_settable(L, -3); /* Take key and value, put into table at -3, */

/* then pop key and value so table again at -1 */



if (lua_pcall(L, 1, 1, 0)) /* Run function, !!! NRETURN=1 !!! */

bail(L, "lua_pcall() failed");



printf("============ Back in C again, Iterating thru returned table ============

");



/* table is in the stack at index 't' */

lua_pushnil(L); /* Make sure lua_next starts at beginning */

const char *k, *v;

while (lua_next(L, -2)) { /* TABLE LOCATED AT -2 IN STACK */

v = lua_tostring(L, -1); /* Value at stacktop */

lua_pop(L,1); /* Remove value */

k = lua_tostring(L, -1); /* Read key at stacktop, */

/* leave in place to guide next lua_next() */

printf("Fromc k=>%s<, v=>%s<

", k, v);

}



lua_close(L); /* Clean up, free the Lua state var */

return 0;

}



slitt@mydesk:~$ ./callfunc

Priming run

In C, calling Lua->tweaktable()

At bottom of callfuncscript.lua tweaktable(), numfields=3

============ Back in C again, Iterating thru returned table ============

Fromc k=>fname<, v=>MARGIE<

Fromc k=>lname<, v=>MARTINEZ<

Fromc k=>numfields<, v=>3<

slitt@mydesk:~$



Table Argument Passing Idioms

lua_newtable(L);

/* Table at stack top, index -1 right now. */

lua_pushliteral(L, "fname"); /* Push a key onto the stack, table ends up at -2 */

lua_pushliteral(L, "Margie"); /* Push a value onto the stack, table ends up at -3 */

lua_settable(L, -3); /* Incorporate key and value, into the table */

/* then pop twice so table again at -1 */

/* repeat as necessary */



lua_settable()

lua_settable

lua_pushboolean

lua_pushcclosure

lua_pushcfunction

lua_pushfstring

lua_pushinteger lua_pushlightuserdata

lua_pushliteral

lua_pushlstring

lua_pushnil

lua_pushnumber lua_pushstring

lua_pushthread

lua_pushvalue

lua_pushvfstring

lua_getglobal(L, fcnname) In spite of what it sounds like, this function actually puts the function corresponding to the fcnname argument on the top of the stack.

lua_newtable(L) This function puts an empty table at the top of the stack.

lua_next(L, -level) This function replaces the previous key with the new one, and then pushes the new value on top of that. You need to do a pop to stay even.



lua_pop(L,number) This pops off the stack, number levels.

lua_settable(L, -level) This pops twice after assigning the key/value pair in the top two stack positions to the table at level -level .



Table Return Passing Idioms

lua_pushnil(L); /* Make sure lua_next starts at beginning */

const char *k, *v;

while (lua_next(L, -2)) { /* TABLE LOCATED AT -2 IN STACK */

v = lua_tostring(L, -1); /* Value at stacktop */

lua_pop(L,1); /* Remove value */

k = lua_tostring(L, -1); /* Read key at stacktop, */

/* leave in place to guide next lua_next() */

/* Do what you need to with k and v */

}

lua_next()