C++ Enums in Lua

On September 13, 2010, in Code Monkey, by Tom

LuaI’ve been working lately on embedding Lua 5.1.4 into a C++ application. I’m really impressed with Lua, and most things are pretty straight-forward. However, when it comes to mapping typed enumerations into Lua, I just haven’t found an easy way to do it. For example, let’s say you want to expose a C++ function into Lua called create(), which takes an enumerated type as a parameter. In C++ this would look like:

typedef enum _TYPE {
    TYPE_FOO = 0,
    TYPE_BAR,
    TYPE_BAZ,
    TYPE_MAX
} TYPE;

bool create(const TYPE type);

To me, it makes the most sense to call create() from Lua with syntax like:

> create(type.foo)

Ideally, we’d also like the following:

  1. The enumeration is const, and can’t be altered from Lua code.
  2. The C++ implementation of create() could validate the type passed to it is the expected type.

Unfortunately, this isn’t as straight-forward as everything else I’ve done with Lua.

Solution (i.e. cut-to-the-chase and explain later)

Okay, I’m a practical guy… download lua_typed_enums.h and include it in your code. Then you can add the enums shown above to Lua by calling add_enum_to_lua():

add_enum_to_lua( L, "type",
    "foo", TYPE_FOO,
    "bar", TYPE_BAR,
    "baz", TYPE_BAZ,
    0 );

Your enums are now available in Lua as:

type.foo
type.bar
type.baz

Now, your C++ code that takes TYPE as an input and returns a boolean would be written as follows:

static int lua_create( lua_State *L )
{
    if( !check_enum_type( L, "type", -1 ) ) {
        /* error */
    }

    int value = get_enum_value( L, -1 );

    /* do something cool */

    lua_pushboolean( L, 1 );
    return (1);
}

You can now call your create() method from Lua how we wanted:

> create(type.bar)

Look at the comments in the header file if you want more info about using add_enum_to_lua(), check_enum_type(), and get_enum_value().

How it Works

There’s no direct way to map the expected behavior of C-enums into Lua (const-ness with typing). What I did was to create a read-only Lua table representing the enumerated type and then make each value in that table have a defined type.

Let’s deal with the first task: making a read-only table. This is pretty well documented on the web, but I’ve included a drawing below that would have helped me a lot in the beginning. Let’s assume we want to represent our enum as a Lua table. We could visualize it as follows:

We could still use the type.foo syntax with this table, but a user could assign a new value to the enum like:

> type.foo = 9

To prevent this, we need to deny write access to the table. The trick to doing this is to use a metatable and set the __index and __newindex values.

So how does this work? When the user reads a value from the table (i.e. by using type.foo), Lua fails to find “foo” in the table. So it looks in the metatable for the __index key. If that is set to point to a table, then it will try to lookup the value in that table. In the case above, it finds the “foo” value and returns it like normal. However, if the user attempts to assign to type.foo, Lua follows the __newindex path, which causes a function to get called that does nothing but print an error… effectively, the write is denied. Here’s what happens:

> type.foo = 9
[string "?"]:1: Attempt to modify read-only table

Okay, cool… we’ve made read-only enumerations, but what about typing. Glad you asked! We add one more layer of abstraction, of course <groaning>:

Basically, we make each value a table that includes the actual value and a type (“type” in our example above). The check_enum_type() function looks at the “type” field in the table to make sure the correct type was passed to your function. The get_enum_value() function gets the “value” field… which is equal to the actual C-enum type you’d expect. From the Lua side, you can inspect the enums like:

> print(type.foo.value)
0
> print(type.foo.type)
type

Explicitly, “type” is a read-only table with value “foo”. The value of “foo” is the table { value=0, type="type" }. I hope this explanation helps. Obviously, I’ve been geeking out on Lua a little too much lately :)

Tagged with:  

One Response to C++ Enums in Lua

  1. David Leistiko says:

    In the function “get_enum_value” by not popping the string off of the stack before returning the value will result in an extra string parameter showing up in the c-code that handles lua functions who make use of the enum type and also are of the dot calling convention i.e.
    “dataType.doSomething(enum.value, 0, false)”… there will be a hidden 4th parameter that is a string.

    Reply

Leave a Reply

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

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>