luasol3

How to write a Lua C module with sol2?


I can implement a Lua C module in pure C by exporting a function luaopen_mymodule that calls luaL_newlib.

How can I do the equivalent in C++, using the sol2 (sol 3) library?


Solution

  • Lua expects the luaopen_mymodule function to follow the lua_CFunction protocol, which means:

    1. the function must have the signature int(lua_State *) noexcept,
    2. the function can get its input arguments from the Lua stack, and
    3. the return value of the function must be the number of Lua values it returns (Lua functions can return multiple values, which can be used in multiple assignments like local foo, bar = baz()), and the actual Lua values to be returned must be on the top of the Lua stack.

    Additionally, the function must be extern "C" so that its name is not mangled.

    Specifically, the luaopen_mymodule function will be called with two arguments that can usually be ignored, and will usually return a single value (typically a table) which will be passed back by the require function. This means:

    1. you can simply ignore the initial contents of the Lua stack, and
    2. luaopen_mymodule should return 1.

    Equipped with the necessary knowledge, we know how to implement luaopen_mymodule:

    1. Wrap the function body inside a try-catch block and handle C++ exceptions properly.
    2. Inside the function body:
      1. Construct a sol::state_view from the lua_State *.
      2. Use the sol::state_view in the same way you'd use a sol::state: call new_usertype(), create_table(), etc.
      3. Immediately before you return from luaopen_mymodule, manually push onto the Lua stack the value you want to return (this means the stack will have one more value on it compared to when the function was called).
      4. Return 1.

    sol allows you to manually push a value onto the Lua stack with the push method on the sol::reference class, which is also available on concrete "value" types such as sol::table since they inherit publicly from sol::reference.

    Here's an example that implements the foo module illustrated in /a/65660774, rewritten in C++ using sol:

    #include <exception>
    #include <sol/sol.hpp>
    
    extern "C" {
    // Define `FOO_LIB` as `__attribute__((visibility("default")))`,
    // `__declspec(dllexport)`, etc.
    FOO_LIB int luaopen_foo(lua_State *L) noexcept try {
      sol::state_view lua(L);
    
      auto result = lua.create_table();
      result["foo"] =
          sol::as_function([](lua_Integer a, lua_Integer b) { return a + b; });
    
      result.push();
      return 1;
    
      // `sol::reference::push` itself returns 1,
      // so the two lines above can be rewritten as:
      // return result.push();
    } catch (const std::exception &e) {
      return luaL_error(L, "%s", e.what());
    }
    }