JakobOvrum/LuaD

Return a LuaObject from a D function

Opened this issue · 11 comments

At the moment, when making a D function for use in Lua, there doesn't seem to be a way of accessing the caller's LuaState, and there doesn't seem to be a way to construct a LuaObject to return to Lua without one. Is there any way of doing this at all elegantly? (I know I can just return a D type, but in this case I actually need Lua's polymorphism on the D end of things)

Provide the LuaState in the function's context, making it a delegate. Methods of doing that include using a nested function, anonymous function or aggregate member function.

I understand; That seems a little less-than-ideal for a program with multiple LuaStates, though (I'm using a couple different ones for sandboxing purposes). Is there any way we could get a function conversion that could check for, e.g. the first parameter of a wrapped function being a LuaState or lua_State* and special case that, a la the raw C api?

I thought about it, but it would interfere with anyone wanting to push/get LuaState to/from Lua as-is. It could cause hard-to-detect bugs.

That's fair enough; Though this might be another situation where a UDA would be appropriate; if, for example, a function's first parameter was a LuaState, and it was marked with @LuaCallerState, that might be clear enough evidence of intent, along the lines of:

void a (LuaState s) {} //WORKS, requires state passed from Lua code;
@LuaCallerState void b (LuaState s, int a) //WORKS, requires int passed from Lua code, gets internal state
@LuaCallerState void c(int a) //ERROR; no LuaState as first parameter
@LuaCallerState void d (int a, LuaState s) //ERROR, LuaState must be first parameter
@LuaCallerState void e (LuaState s, int a, LuaState t)  //WORKS, requires both an int and a state passed from Lua code

Cases C and D could be detected during compile time, so bugs from that angle would be pretty easy to find. Case A could be ambiguous, but it would at least cause an error the first time the function was called from Lua if the intent was to use the caller state.

Attributes are attached to symbols/declarations, not types, so using a UDA won't work in the most basic case:

void myFunc() @luaCallerState;
lua["func"] = &myFunc; // RHS expression loses all UDA information

Worse yet, it will compile but won't do what the user expected.

Oh, darn :/ and it doesn't throw errors on attaching it to a type declaration either.

In that case there's probably not a good solution to this. :/ I really need the functionality in my project, so I'm going to patch my own local source tree, but it doesn't sound like there's a good way to have this upstream.

How come it's not possible for you to use a delegate? It should always be possible.

Because the program has multiple LuaStates, and the code that needs to create a LuaObject can be called from any of them; The only way I can imagine making it possible to communicate the state all the way through D involves global variables on both the D and Lua sides, and that's pretty horrendously messy from my perspective.

Because the program has multiple LuaStates, and the code that needs to create a LuaObject can be called from any of them;

I don't really see how that's a problem. There's a multitude of ways to achieve it with delegates:

LuaObject delegate() func(LuaState lua)
{
    return () => lua.newTable();
}

foreach(lua; states)
    lua["func"] = func(lua);

Or:

class MyState
{
    private:
    LuaState lua;
    LuaObject func() { return lua.newTable(); }

    public:
    this(LuaState lua)
    {
        this.lua = lua;
        lua["func"] = &func;
    }
}

MyState[] myStates = luaStates.map!(lua => new MyState(lua)).array();

Or using class marshalling etc.

Or using class marshalling etc.

I'm curious about this method, care to provide an example for it as well?

I'm curious about this method, care to provide an example for it as well?

Sure:

class MyState
{
    private:
    LuaState lua;

    public:
    this(LuaState lua) { this.lua = lua; }
    LuaObject func() { return lua.newTable(); }
}

lua["myObj"] = new MyState(lua);
lua.doString(`
    local t = myObj:func()
    assert(type(t) == "table" and #t == 0)
`);