With LuaJIT, how could I associate more than one metatable with a C struct?

70 Views Asked by At

I'm currently working on my own game engine, which uses C and LuaJIT, and I'm working on scripting for my entity hierarchy. As part of my hierarchy's design, I have a single node struct here, and I use a void pointer to node-specific data.

typedef struct game_node_t game_node;
typedef struct game_node_vector_t game_node_vec;

struct game_node_t {
    const char *name;
    game_node* parent;
    game_node_vec children;

    enum GAME_NODE_TYPES type;
    void *data;
};

However, in my scripts, I want to use metatables for the different node types in order to resemble object-oriented programming, but my current design prevents me from doing this later on:

game.sound = ffi.metatype("game_node", sound_metatable)
game.sprite = ffi.metatype("game_node", sprite_metatable)

This would give the node types sounds and sprites their specific methods. The resulting error is "cannot change a protected metatable", which isn't surprising after reading LuaJIT's documentation.

So far, I've only tried putting this in my cdef:

typedef struct game_node_t game_sound_node;
typedef struct game_node_t game_sprite_node;

And doing this:

game.sound = ffi.metatype("game_sound_node", sound_metatable)
game.sprite = ffi.metatype("game_sprite_node", sprite_metatable)

This still results in "cannot change a protected metatable". Are there any design changes or potential workarounds that I should consider?

1

There are 1 best solutions below

0
ziggy stardust On

I slept on it and came up with my own solution. I wrote a function to create nodes, which replaces the new method for each node type.

local type_table = {
    sound = sound_metatable.new,
    sprite = sprite_metatable.new
}

function create_node(type)
    return type_table[type]()
end

Then, I wrote a new metatable that looks up the function tables based on the node's type.

local lookup_table = {
    [sound_enum] = sound_metatable,
    [sprite_enum] = sprite_metatable
}

local test_metatable = {
    __index = function (table, key)
        print(tonumber(table.type))
        return lookup_table[tonumber(table.type)][key]
    end
}

Then, I can do this:

game.node = ffi.metatype("game_node", test_metatable)

It adds a small level of overhead, but it enables me to have a single C struct that has a different set of functions depending on the type value.