C++ Enum Support in LuaBridge2

2 minute read

Recently I was doing some work to add support for Lua scripting to the Ostinato network traffic generator.

Although there are several solutions for Lua bindings for C++ out there, I decided to go with LuaBridge.

Note that there is a fork of LuaBridge called LuaBridge3, but it requires C++17 and with Ostinato I target a wide variety of platforms some of which are old and still use C++11.

So I decided to stick with the original LuaBridge.

LuaBridge however doesn’t support binding C++ enums. A little bit of googling and reading the documentation I found a solution which I will document here with examples.

First we will add support for passing C++ enums into Lua (as a number). Given the following C++ enums -

enum ProtocolIdType {
    ProtocolIdTypeLlc,
    ProtocolIdTypeEthernet,    
    ProtocolIdTypeIp,
    ProtocolIdTypeTcpUdp
};

enum CksumType {    
    CksumTypeIp,
    CksumTypeIpPseudo,
    CksumTypeTcpUdp
};

We add support for these enums by specializing the LuaBridge Stack template class for each enum type we want to support.

// Helper template for all enums we want to support
template <typename T>
struct EnumWrapper {
    static typename std::enable_if<std::is_enum<T>::value, void>::type
        push(lua_State* L, T value) {
        lua_pushnumber (L, static_cast<std::size_t>(value));
    }

    static typename std::enable_if<std::is_enum<T>::value, T>::type
        get(lua_State* L, int index) {
        return static_cast<T>(lua_tointeger(L, index));
    }
};

// Use EnumWrapper template for each enum type we want to support
namespace luabridge {
    template <>
    struct Stack<ProtocolIdType>
        : EnumWrapper<ProtocolIdType> {};

    template <>
    struct Stack<CksumType>
        : EnumWrapper <AbstractProtocol::CksumType> {};
}

Now you can pass the enums back and forth between C++ and Lua - the C++ enums will be treated as integers in Lua. So, you can call a Lua function pass in a C++ enum value.

LuaRef payloadProtocolId = getGlobal(L, "payloadProtocolId");
uint id = payloadProtocolId(ProtocolIdTypeIp);

LuaRef idType = getGlobal(L, "payloadProtocolIdType");
ProtocolIdType protocolId = idType();

Remember in Lua code these variables are integers. So any code referencing these will need to compare against the integer values.

if payloadProtocolIdType == 1 then
    -- do something
end

What if you wanted to use enums instead of integers in Lua? You can export them as Lua global variables by registering them with LuaBridge as a property under a Lua namespace (table).

// Helper template function for registering enum constants with LuaBridge
template<int T>
int enumValue() {
    return T;
}

getGlobalNamespace(L_)
    .beginNamespace("Protocol")
        // Enum ProtocolIdType
        .addProperty("ProtocolIdLlc", &enumValue<AbstractProtocol::ProtocolIdLlc>)
        .addProperty("ProtocolIdEth", &enumValue<AbstractProtocol::ProtocolIdEth>)
        .addProperty("ProtocolIdIp", &enumValue<AbstractProtocol::ProtocolIdIp>)
        .addProperty("ProtocolIdTcpUdp", &enumValue<AbstractProtocol::ProtocolIdTcpUdp>)
        // Enum CksumType
        .addProperty("CksumIp", &enumValue<AbstractProtocol::CksumIp>)
        .addProperty("CksumIpPseudo", &enumValue<AbstractProtocol::CksumIpPseudo>)
        .addProperty("CksumTcpUdp", &enumValue<AbstractProtocol::CksumTcpUdp>)
    .endNamespace();

After this registration, you can now reference the enums in Lua code as global variables.

local protocolId = Protocol.ProtocolIdIp

That’s it. Hope this has been helpful.

Leave a Comment