Azinum
Azinum

Reputation: 31

Link C variable to Lua

I'm looking for a solution to bind a C variable (integer, double, normal C string or similar) to Lua. Lua should be able to modify this variable (in C context). I'm not looking for a solution to call a C function at all (from Lua to get the reference to that variable). I also do not want to use any external library for this (Lua API will do fine). I have seen that LuaBridge solve this (existing project using this: https://github.com/Malaxiz/Third/blob/network/Fifth/CGame.cpp#L240), but as I said, no external libraries and definitely not C++.

Example of use:

C code

typedef struct Instance {
    int test;    /* The variable I want to link to Lua (and be able to modify it)*/
} Instance;

int main() {
    lua_State* L = lua_open();
    Instance instance;
    instance.test = 5;
    /* ... */
}

And in Lua:

instance.test = instance.test + 5
print(instance.test)   -- Should be 10

Back to C:

int main() {
    ...
    printf("%i\n", instance.test);    /* Should be 10 here too */
}

Is there a workaround for this?

Upvotes: 1

Views: 1685

Answers (2)

user3125367
user3125367

Reputation: 3000

It is the "homework" for Lua/C programmer, but for google sake:

#include <stdio.h>
#include <stdlib.h>
#include <lauxlib.h>
#include <lualib.h>

typedef struct Instance Instance;

struct Instance {
    int test1;
    double test2;
};

// linstance.c {

    static const char *const tname = "Instance";

    enum { f_test1, f_test2 };
    static const char *const fmap[] = { "test1", "test2" };

    static int
    l_get(lua_State *L) // (t, k) -> v
    {
        Instance *inst = *(Instance **)luaL_checkudata(L, 1, tname);

        switch (luaL_checkoption(L, 2, NULL, fmap)) {
            case f_test1: lua_pushinteger(L, inst->test1); break;
            case f_test2: lua_pushnumber(L, inst->test2); break;
        }
        return 1;
    }

    static int
    l_set(lua_State *L) // (t, k, v)
    {
        Instance *inst = *(Instance **)luaL_checkudata(L, 1, tname);

        switch (luaL_checkoption(L, 2, NULL, fmap)) {
            case f_test1: inst->test1 = luaL_checkinteger(L, 3); break;
            case f_test2: inst->test2 = luaL_checknumber(L, 3); break;
        }
        return 0;
    }

    void
    pushInstance(lua_State *L, Instance *instance)
    {
        Instance **ud = lua_newuserdata(L, sizeof(*ud));
        *ud = instance;

        if (luaL_newmetatable(L, tname)) {
            lua_pushcfunction(L, l_set);
            lua_setfield(L, -2, "__newindex");
            lua_pushcfunction(L, l_get);
            lua_setfield(L, -2, "__index");
            lua_pushstring(L, tname);
            lua_setfield(L, -2, "__metatable");
        }
        lua_setmetatable(L, -2);
    }

// }

int
main(int argc, char *argv[])
{
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    Instance instance = { -1, -1.0 };

    pushInstance(L, &instance);
    lua_setglobal(L, "instance");

    luaL_dostring(L, "print(instance.test1, instance.test2)");
    luaL_dostring(L, "instance.test1 = 3.14; instance.test2 = 3.14");
    luaL_dostring(L, "print(instance.test1, instance.test2)");
    printf("%d\t%g\n", instance.test1, instance.test2);

    if (luaL_dostring(L, "print(instance.UNKNOWN)")) {
        printf("%s\n", lua_tostring(L, -1));
        lua_pop(L, 1);
    }

    lua_close(L);
    return 0;
}

-1  -1
3   3.14
3   3.14
[string "print(instance.UNKNOWN)"]:1: bad argument #2 to '__index' (invalid option 'UNKNOWN')

Upvotes: 1

Vlad
Vlad

Reputation: 5847

I'm not looking for a solution to call a C function at all.

You'll have to. There will be a call to C function, always. Even if it might be not explicit on Lua side, it will be there.
Typically you will set metatable on instance object that lives in Lua. It will catch reads and writes to that object, and redirect those actions to C side by calling C functions.

Upvotes: 1

Related Questions