Adding Lua 5.2 to your application
Note: The code in here is adapted from an actual project, however I've not yet had time to verify it doesnt have typos. Google search results are just overflowing with info on old Lua versions that I wanted to dump this to the web now in the hopes of being at least vaguely helpful.
Lua is a really cool, clean little programming language that is easy to embed in your applications. Not only is it under a permissive license, it's ANSI C.
However, recent updates have made most of the documentation about it on the web a bit outdated, so I thought I'd drop this quick tutorial on how to add Lua to your application and do some of the typical inter-operation things with it that you'd want to do when hosting scripts in your application.
Building Lua
Building Lua is very easy. After getting the source code (Im using the unoffical Git repository from LuaDist on Github), you duplicate the file lua/src/luaconf.h.orig under the name lua/src/luaconf.h. Then you point Terminal at Luas folder and do
make macosx
(Or if you're not on a Mac, use the appropriate platform name here, you can see available ones by just calling make without parameters in that folder)
This will churn a short moment, and then you'll have a liblua.a file. Add that to your Xcode project (or equivalent) so it gets linked in, and make sure the header search paths include the lua/src/ folder. Thats it, now you can use Lua in your application.
cd ${PROJECT_DIR}/lua/src/ if [ ! -f luaconf.h ]; then cp luaconf.h.orig luaconf.h fi make macosx
By specifying ${PROJECT_DIRECTORY}/lua/src/lua.h as the input file and ${PROJECT_DIRECTORY}/lua/src/liblua.a, Xcode will take care to not unnecessarily rebuild Lua if you make your application depend on this target.
Running a Lua script
To use Lua, you include the following headers:
#include "lua.h" #include "lauxlib.h" #include "lualib.h"
(If you're using C++, be sure to wrap them in extern "C" or you'll get link errors) Then you can simply compile the following code to initialize a Lua context and run a script from a text file:
lua_State *L = luaL_newstate(); // Create a context. luaL_openlibs(L); // Load Lua standard library. // Load the file: int s = luaL_loadfile( L, "/path/to/file.lua" ); if( s == 0 ) { // Run it, with 0 params, accepting an arbitrary number of return values. // Last 0 is error handler Lua function's stack index, or 0 to ignore. s = lua_pcall(L, 0, LUA_MULTRET, 0); } // Was an error? Get error message off the stack and print it out: if( s != 0 ) { printf("Error: %s\n", lua_tostring(L, -1) ); lua_pop(L, 1); // Remove error message from stack. } lua_close(L); // Dispose of the script context.
The script file would contain something like:
-- this is a comment io.write("Hello world, from ",_VERSION,"!\n")
Calling from Lua into C
Now you can run a file full of commands. But how do you have it call back into your application? Theres a special call for that, lua_register, which creates a new function that actually wraps a special C function. You call it like this:
// Create a C-backed Lua function, myavg(): lua_register( L, "myavg", foo ); // Create a global named "myavg" and stash an unnamed function with C function "foo" as its implementation in it.
to register a C function named foo as a Lua function named myavg. The actual function would look like this:
// An example C function that we call from Lua: static int foo (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ lua_Number sum = 0; int i; for (i = 1; i <= n; i++) { if (!lua_isnumber(L, i)) { lua_pushstring(L, "incorrect argument"); lua_error(L); } sum += lua_tonumber(L, i); } lua_pushnumber(L, sum/n); /* first result */ lua_pushnumber(L, sum); /* second result */ return 2; /* number of results */ }
This example function loops over all parameters that have been passed (using lua_isnumber to check they're numbers, and lua_tonumber to actually retrieve them as ints), which may be a variable number, adds and averages them, and then pushes two return values on the stack (the average and the sum), and returns the number of return values it gave.
Functions in Lua and other oddities
You could now call it like:
io.write( "Average is: ", myavg(1,2,3,4,5) )
from Lua. The funny thing here is, in Lua, there are no functions in the traditional sense. Its a prototype-based programming language, so all functions are closures/blocks/lambdas, and can be treated just like any value, like an integer or a string. To declare a function, lua_register simply creates a global variable named myavg and sticks such a function object in it.
When you declare a function in Lua, it's also really just a shorthand for an assignment statement. So to run a function declared in a Lua file, like:
function main( magicNumber ) io.write("Main was called with magicNumber ", magicNumber, "!") end
you first have to execute it, which will create the global named main and stick a function in it. Only now do you look up the function object from that global and call it, again using lua_pcall like here:
lua_getglobal(L,"main"); if( lua_type(L, -1) == LUA_TNIL ) return; // Function doesn't exist in script. lua_pushinteger(L,5); s = lua_pcall(L, 1, LUA_MULTRET, 0); // Tell Lua to expect 1 param & run it.
The 2nd parameter to lua_pcall tells it how many parameters to expect.
Creating Lua objects from C
Objects are likewise just tables (i.e. key-value dictionaries) where ivars are just values, and methods are functions stored as values. So, to create a new object with methods implemented in C, you do:
// Create a C-backed Lua object: lua_newtable( L ); // Create a new object & push it on the stack. // Define mymath.durchschnitt() for averaging numbers: lua_pushcfunction( L, foo ); // Create an (unnamed) function with C function "foo" as the implementation. lua_setfield( L, -2, "durchschnitt" ); // Pop the function off the back of the stack and into the object (-2 == penultimate object on stack) using the key "durchschnitt" (i.e. method name). lua_setglobal( L, "mymath" ); // Pop the object off the stack into a global named "mymath".
To call this, function, you do it analogous to before, just that you first use lua_getglobal( L, "mymath" ) to push the object on the stack, then lua_getfield to actually push the `durchschnitt` function stored under that key in the object.
Since functions are closures/blocks/lambdas, they can also capture variables (upvalues). To set those, you use lua_pushcclosure instead of lua_pushcfunction and pass the number of values you pushed on the stack to capture as the last parameter. E.g. if you wanted to pass along a pointer to an object in your program that the session object wraps, instead of stashing it in an ivar, you could capture it like:
// Define session.write() for sending a reply back to the client: lua_pushlightuserdata( L, sessionPtr ); // Create a value wrapping a pointer to a C++ object (this would be dangerous if we let the script run longer than the object was around). lua_pushcclosure( L, session_write, 1 );// Create an (unnamed) function with C function "session_write" as the implementation and one associated value (think "captured variable", our userdata on the back of the stack). lua_setfield( L, -2, "write" ); // Pop the function value off the back of the stack and into the object (-2 == penultimate object on stack) using the key "write" (i.e. method name). lua_setglobal( L, "session" ); // Pop the object off the stack into a global named "session".
and inside the session_write function, you'd retrieve it again like:
session* sessionPtr = (session*) lua_touserdata( L, lua_upvalueindex(1) );
Overriding Luas getters and setters with C
And finally, what if you wanted to have properties on this object that, when set, actually call into your C code? You install a metatable on your object, which contains a __newindex (setter) and __index (getter) function:
// Set up our 'session' table: lua_newtable( luaState ); // Create object to hold session. lua_newtable( luaState ); // Create metatable of object to hold session. lua_pushlightuserdata( luaState, myUserData ); lua_pushcclosure( luaState, get_variable_c_func, 1 ); // Wrap our C function in Lua. lua_setfield( luaState, -2, "__index" ); // Put the Lua-wrapped C function in the metatable as "__index". lua_pushlightuserdata( luaState, myUserData ); lua_pushcclosure( luaState, set_variable_c_func, 1 ); // Wrap our C function in Lua. lua_setfield( luaState, -2, "__newindex" ); // Put the Lua-wrapped C function in the metatable as "__newindex". lua_setmetatable( luaState, -2 ); // Associate metatable with object holding me. lua_setglobal( luaState, "session" ); // Put the object holding session into a Lua global named "session".
Where, like before, myUserData is some pointer to whatever data you need to access to do your work in the getter/setter (like the actual C struct this Lua object stands for) and get_variable_c_func and set_variable_c_func are the C functions that you provide that get called to retrieve, and add/change instance variables of the session object.
Note that get_variable_c_func will receive 2 Lua parameters on the stack: The session table itself, and the name of the instance variable you're supposed to get. You return this value as the only return value. set_variable_c_func gets a third parameter, the value to assign to the variable, but obviously doesnt return anything.
Dynamically providing your own globals
Sometimes you want to dynamically expose objects in your application to a script. One easy way to do that is to make them global variables. In our examples above, we did so by manually registering a new global with a table. But if you have lots of objects or they might change often, you don't want to do that.
Luckily, Lua keeps all its globals in an invisible table named _G, which you can put on the stack using lua_pushglobaltable(L). Now, you can create a meta-table with an __index fallback function for that as well. The key your callback gets will be the name of the global, which you can use to look up the object and dynamically generate and push a table for this object.
Note: In the code above, we called lua_setglobal() in the end, which pushed our table off the stack and stuffed it in the _G table. Since were not doing that here, be sure to do a lua_pop( L, 1 ) to remove the globals table from the stack again. Otherwise, your call to lua_pcall() will try to call that table and give the error `attempt to call a table value.`
And now you know all you need to call Lua from C, and have Lua call your C functions back.