Calling C++ functions from Lua 5.2

Welcome to the third part of these Lua 5.2 personal notes. If you have not yet, you may want to read my previous notes on running Lua 5.2 scripts from C++ or passing variables from Lua 5.2 to C++.

This new tutorial explains how to call C++ functions from Lua 5.2, including passing and receiving arguments from them. These notes are based on the Lua-User Wiki Sample Code and the Lua 5.2 Reference Manual, as usual, and on the tutorial by Christian Stigen Larsen.

First, I will show you some code (in Lua and C++) and the execution output. Afterwards comes the details of the most difficult parts.

Lua script

The Lua script will just call a C++ function with some input arguments, and then will receive and print two output arguments. The code looks like this:

-- call the registered C-function
io.write('[Lua] Calling the C functionn')
a,b = displayLuaFunction(12, 3.141592, 'hola')

-- print the return values
io.write('[Lua] The C function returned <' .. a .. '> and <' .. b .. '>n')

C++ program

The C++ code is almost exactly the same as in the previous tutorial. The only differences are the declaration and pushing of the function that Lua will call. The whole code is:

#include <iostream>
#include <sstream>

#include <lua.hpp>

int displayLuaFunction(lua_State*);

int main(int argc, char* argv[])
{
    // new Lua state
    std::cout << "[C++] Starting Lua state" << std::endl;
    lua_State *lua_state = luaL_newstate();

    // load Lua libraries
    std::cout << "[C++] Loading Lua libraries" << std::endl;
    static const luaL_Reg lualibs[] = 
    {
        {"base", luaopen_base},
        {"io", luaopen_io},
        {NULL, NULL}
    };
    const luaL_Reg *lib = lualibs;
    for(; lib->func != NULL; lib++)
    {
        std::cout << " loading '" << lib->name << "'" << std::endl;
        luaL_requiref(lua_state, lib->name, lib->func, 1);
        lua_settop(lua_state, 0);
    }

    // push the C++ function to be called from Lua
    std::cout << "[C++] Pushing the C++ function" << std::endl;
    lua_pushcfunction(lua_state, displayLuaFunction);
    lua_setglobal(lua_state, "displayLuaFunction");

    // load the script
    std::cout << "[C++] Loading the Lua script" << std::endl;
    int status = luaL_loadfile(lua_state, "parrotscript.lua");
    std::cout << " return: " << status << std::endl;

    // run the script with the given arguments
    std::cout << "[C++] Running script" << std::endl;
    int result = 0;
    if(status == LUA_OK)
    {
        result = lua_pcall(lua_state, 0, LUA_MULTRET, 0);
    }
    else
    {
        std::cout << " Could not load the script." << std::endl;
    }

    // close the Lua state
    std::cout << "[C++] Closing the Lua state" << std::endl;
    return 0;
}

int displayLuaFunction(lua_State *l)
{
    // number of input arguments
    int argc = lua_gettop(l);

    // print input arguments
    std::cout << "[C++] Function called from Lua with " << argc 
              << " input arguments" << std::endl;
    for(int i=0; i<argc; i++)
    {
        std::cout << " input argument #" << argc-i << ": "
                  << lua_tostring(l, lua_gettop(l)) << std::endl;
        lua_pop(l, 1);
    }

    // push to the stack the multiple return values
    std::cout << "[C++] Returning some values" << std::endl;
    lua_pushnumber(l, 12);
    lua_pushstring(l, "See you space cowboy");

    // number of return values
    return 2;
}

The output is:

The details

Lua functions are first-class values. This means that they can be treated as conventional values, in particular, they can be stored in variables. Thus, one can pass a function to Lua by simply pushing the value to the Lua stack and making its name global:

    // push the C++ function to be called from Lua
    lua_pushcfunction(lua_state, displayLuaFunction);
    lua_setglobal(lua_state, "displayLuaFunction");

Lua will be able to access displayLuaFunction() from its global name “displayLuaFunction”. Also, there is a macro, lua_register(), that allows writing these two steps in one sentence. Instead of the previous code, one might have coded it as:

    // push the C++ function to be called from Lua
    lua_register(lua_state, "displayLuaFunction", displayLuaFunction);

(my thanks to kpityu for pointing this out in the comments).

In order C++ functions to communicate properly with Lua, they must follow some rules. The first one is its signature, which has to be consistent with the type defined by lua_CFcuntion:

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

i. e., the function must return an integer and only accept one input argument of type pointer-to lua_state.

The second rule is just protocol to follow for input/output function argument interchange between Lua and C++. As you may have guessed, all this passing of variables is done via the Lua stack. When a C++ function is called from Lua, for example

displayLuaFunction(a, b, c)

a new Lua stack, independent of the rest of stacks, is generated for the function. This stack contains the input arguments of the function. Thus, from inside of the C++ function one knows the amount of input arguments by getting the position of the top of this stack:

    // number of input arguments
    int argc = lua_gettop(l);

and the input arguments can be accessed directly from the stack:

    lua_tostring(l, lua_gettop(l));

Notice that the order in the stack is the same as in the function call, hence, the first element popped is actually the last input argument. Since initially the only elements of the stack are the input arguments, you may access a given index by directly pointing to it. For instance, for the second input argument can be converted to a string — having checked the stack has at least two elements — by this statement:

    lua_tostring(l, 2);

Once the function has finished, the return values must be placed in the stack so Lua can reach them:

    // push to the stack the multiple return values
    lua_pushnumber(l, 12);
    lua_pushstring(l, "See you space cowboy");

The final part is to return the number of variables pushed to the stack that we want Lua to see as the C++ function return value:

    // number of return values
    return 2;

And that is it!

With this we have covered the basic protocol for creating new C++ functions that are Lua compatible. In case you want more examples you can check the Lua libraries, which are made of C++ functions following these rules.

Stay tuned for the next episode of these personal notes. I am not sure if I will be talking about linking C++ dynamic libraries to Lua or about encapsulating and passing objects from and to Lua.

Read the next tutorial!

About

View all posts by

11 thoughts on “Calling C++ functions from Lua 5.2

    1. Ooops, typo. Thanks for pointing it out, I meant lua_pushcfunction(). About lua_register() I usually prefer to talk about the functions rather than about the macros. Though many people can benefit from knowing the macro, so I will change the text. Thanks! 🙂

  1. Thanks for the tutorials! These were to be the only tutorials targeted at Lua *5.2* that I could find, and they helped me a lot!

Leave a Reply

Your email address will not be published. Required fields are marked *