Using Lua Scripts as Config Files from C++

October 16, 2013

tl;dr: LuaTables++ is a library that allows you to access values from Lua scripts in an easy manner. Custom types (i.e. structs or classes) can be easily added so that storing and retrieval to and from Lua is super simple. Serialization to strings is included. Code available here (MIT license). Handling of custom classes / structs is explained here.

Introduction

For my visualization tool MeshUp I am using jsoncpp to store the configurations files. However it dawned to me that it should be easy to implement a library that allows to store and retrieve values from Lua scripts similarly easy as jsoncpp. As Lua has a nicer syntax than JSON this would allow me to store the configuration as Lua scripts and thus getting all the scripting capabilities there as well.

Overview

The last few days I spent some time to pimp the my LuaTables (which I mentioned here) library to make it more intuitive to use from C++ and I ended up something very nice. It even deserves a renaming to LuaTables++. First, an example:

-- agentsettings.lua
person = {
    name = "James Bond",
    age = 42.,
    drunken = true,
    address = {
        country = "United Kingdom"
    }
}

return person

Can be accessed using LuaTables++ using:

LuaTable ltable = LuaTable::fromFile("myfile.lua")

std::string name = ltable["name"];
double age = ltable["age"];
bool drunken = ltable["drunken"];
std::string country = ltable["address"]["country"];

Similarly you can set values:

ltable["drunken"] = false;

And also convert the table back to a string, e.g. to save the configuration back to a file:

std::string config_string = ltable.serialize();

Custom Types

Update: I have found a much better solution on how to handle custom types with LuaTables++.

This was very nice, but it gets better: LuaTables++ uses some templating magic inside which can be used to add reading and writing for structured data. E.g. to add the following custom type:

struct CustomType {
    CustomType() :
        name ("unnamed"),
        age (-1.),
        drunken (false) {}

    string name;
    double age;
    bool drunken;
};

one has to implement two functions:

Here the function for converting from LuaTables++ to the C++ CustomType:

template<> CustomType LuaTableNode::getDefault<CustomType>(const CustomType &default_value) {
    CustomType result = default_value;

    if (stackQueryValue()) {
        lua_getfield (luaTable->L, -1, "name");
        result.name = lua_tostring (luaTable->L, -1);
        lua_pop (luaTable->L, 1);

        lua_getfield (luaTable->L, -1, "age");
        result.age = lua_tonumber (luaTable->L, -1);
        lua_pop (luaTable->L, 1);

        lua_getfield (luaTable->L, -1, "drunken");
        result.drunken = lua_toboolean (luaTable->L, -1);
        lua_pop (luaTable->L, 1);
    }

    stackRestore();

    return result;
}

And here the function to store a C++ CustomType to LuaTables++:

template<> void LuaTableNode::set<CustomType>(const CustomType &value) {
    stackCreateValue();

    // create new table for the CustomType
    lua_newtable(luaTable->L);  // parent, CustomTable

    // set the fields of the custom type
    lua_pushstring (luaTable->L, "name");
    lua_pushstring (luaTable->L, value.name.c_str());
    lua_settable (luaTable->L, -3);

    lua_pushstring (luaTable->L, "age");
    lua_pushnumber (luaTable->L, value.age);
    lua_settable (luaTable->L, -3);

    lua_pushstring (luaTable->L, "drunken");
    lua_pushboolean (luaTable->L, value.drunken);
    lua_settable (luaTable->L, -3);

    // add table of CustomType to the parent
    stackPushKey(); // parent, CustomTable, key
    lua_pushvalue(luaTable->L, -2); // parent, CustomTable, key, CustomTable
    lua_settable(luaTable->L, -4);

    // restore the stack
    stackRestore();
}

Once this is done, one can directly store and retrieve any CustomType to Lua:

// Create a new Custom Type
CustomType agent007;
agent007.name = "James Bond";
agent007.age = 42.;
agent007.drunken = true;

// Store it in LuaTables++
ltable["agents"][123] = agent007;

// Retrieve it from LuaTables++
CustomType other_agent = ltable["agents"][123];

One nice thing about this is, that once you have implemented the set() and getDefault() functions you automatically can serialize and de-serialize CustomTypes. Yeeeehaaaa!

Code

LuaTables++ is available from https://bitbucket.org/MartinFelis/luatables only depends on the STL and Lua, which should be easy to fulfill. It is licensed under the MIT license (same as Lua).

Fun Fact: Around the same time Elias Daler implemented something similar to retrieve values from a Lua script http://eliasdaler.wordpress.com/2013/10/11/lua_cpp_binder/. His article also contains a brief introduction how the internals work.