1

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?

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

5 Comments

What I meant was that I don't want to call a C function from Lua to bind the variable (get reference e.t.c).
Lua values and native C values are separate entities. You'll have to either store it on single side - in Lua VM or in C, means calling func to store/read values across VM/native border. Or make other calls to synchronize values of two entities, if you want to store those values on both sides. Function calls to C either way.
Can you show me a workaround? And also, can you explain how LuaBridge does it? In LuaBridge you can successfully store a reference to, say, C integer and change it in a Lua script without calling a wrapper function.
Wrappers called implicitly by Lua. Read about Lua metatables and __index / __newindex metamethods. That's the building blocks for any bridging lib.
I'll look into it, thanks. It would be good if you improved your answer and maybe show an example.
1

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')

1 Comment

This was very helpful. It's a little hard to read with your coding-style. So get function is called when doing index set/get with help of metatable. Didn't know the checkudata existed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.