1 module luad.lfunction; 2 3 import luad.base; 4 import luad.table; 5 import luad.stack; 6 import luad.conversions.functions; 7 8 import luad.c.all; 9 10 /// Represents a Lua function. 11 struct LuaFunction 12 { 13 /// LuaFunction sub-types $(DPREF base, LuaObject) through this reference. 14 LuaObject object; 15 alias object this; 16 17 version(none) package this(lua_State* L, int idx) 18 { 19 LuaObject.checkType(L, idx, LUA_TFUNCTION, "LuaFunction"); 20 object = LuaObject(L, idx); 21 } 22 23 /** 24 * Call this function and collect all return values as 25 * an array of $(DPREF base, LuaObject) references. 26 * Examples: 27 ----------------------- 28 lua.doString(`function f(...) return ... end`); 29 auto f = lua.get!LuaFunction("f"); 30 31 LuaObject[] ret = f(1.2, "hello!", true); 32 33 assert(ret[0].to!double() == 1.2); 34 assert(ret[1].to!string() == "hello!"); 35 assert(ret[2].to!bool()); 36 ----------------------- 37 */ 38 LuaObject[] opCall(U...)(U args) 39 { 40 return call!(LuaVariableReturn!(LuaObject[]))(args).returnValues; 41 } 42 43 /** 44 * Call this function. 45 * Params: 46 * T = expected return type. 47 * args = list of arguments. 48 * Returns: 49 * Return value of type $(D T), or nothing if $(D T) was unspecified. 50 * See $(DPMODULE2 conversions,functions) for how to 51 * catch multiple return values. 52 * Examples: 53 * ------------------ 54 lua.doString(`function ask(question) return 42 end`); 55 auto ask = lua.get!LuaFunction("ask"); 56 57 auto answer = ask.call!int("What's the answer to life, the universe and everything?"); 58 assert(answer == 42); 59 * ------------------ 60 */ 61 T call(T = void, U...)(U args) 62 { 63 this.push(); 64 foreach(arg; args) 65 pushValue(this.state, arg); 66 67 return callWithRet!T(this.state, args.length); 68 } 69 70 /** 71 * Set a new environment for this function. 72 * 73 * The environment of a function is the table used for looking up non-local (global) variables. 74 * Params: 75 * env = new environment 76 * Examples: 77 * ------------------- 78 * lua["foo"] = "bar"; 79 * auto func = lua.loadString(`return foo`); 80 * assert(func.call!string() == "bar"); 81 * 82 * auto env = lua.wrap(["foo": "test"]); 83 * func.setEnvironment(env); 84 * assert(func.call!string() == "test"); 85 * ------------------- 86 */ 87 void setEnvironment(ref LuaTable env) 88 in { assert(this.state == env.state); } 89 body 90 { 91 this.push(); 92 env.push(); 93 lua_setfenv(this.state, -2); 94 lua_pop(this.state, 1); 95 } 96 97 /** 98 * Dump this function as a binary chunk of Lua bytecode to the specified 99 * writer delegate. Multiple chunks may be produced to dump a single 100 * function. 101 * 102 * Params: 103 * writer = delegate to forward writing calls to 104 * 105 * If the delegate returns $(D false) for any of the chunks, 106 * the _dump process ends, and the writer won't be called again. 107 */ 108 bool dump(scope bool delegate(in void[]) writer) 109 { 110 alias typeof(writer) LuaWriter; 111 112 extern(C) static int luaCWriter(lua_State* L, const void* p, size_t sz, void* ud) 113 { 114 auto writer = *cast(LuaWriter*)ud; 115 return writer(p[0..sz]) ? 0 : 1; 116 } 117 118 this.push(); 119 auto ret = lua_dump(this.state, &luaCWriter, &writer); 120 lua_pop(this.state, 1); 121 return ret == 0; 122 } 123 } 124 125 version(unittest) 126 { 127 import luad.testing; 128 import std.variant; 129 import std.typecons; 130 } 131 132 unittest 133 { 134 lua_State* L = luaL_newstate(); 135 scope(success) lua_close(L); 136 luaL_openlibs(L); 137 138 lua_getglobal(L, "tostring"); 139 auto tostring = popValue!LuaFunction(L); 140 141 LuaObject[] ret = tostring(123); 142 assert(ret[0].to!string() == "123"); 143 144 assert(tostring.call!string(123) == "123"); 145 146 tostring.call(321); 147 148 // Multiple return values 149 luaL_dostring(L, "function singleRet() return 42 end"); 150 lua_getglobal(L, "singleRet"); 151 auto singleRet = popValue!LuaFunction(L); 152 153 auto singleRetResult = singleRet.call!(Tuple!int)(); 154 assert(singleRetResult[0] == 42); 155 156 alias Algebraic!(string, double) BasicLuaType; 157 BasicLuaType a = "foo"; 158 BasicLuaType b = 1.5; 159 160 pushValue(L, [a, b]); 161 lua_setglobal(L, "test"); 162 163 luaL_dostring(L, "function multRet() return unpack(test) end"); 164 lua_getglobal(L, "multRet"); 165 auto multRet = popValue!LuaFunction(L); 166 167 auto result = multRet.call!(Tuple!(string, double))(); 168 assert(result[0] == a); 169 assert(result[1] == b); 170 171 unittest_lua(L, `function getName() return "Foo", "Bar" end`); 172 173 lua_getglobal(L, "getName"); 174 auto getName = popValue!LuaFunction(L); 175 176 string[2] arrayRet = getName.call!(string[2])(); 177 assert(arrayRet[0] == "Foo"); 178 assert(arrayRet[1] == "Bar"); 179 180 // setEnvironment 181 pushValue(L, ["test": [42]]); 182 auto env = popValue!LuaTable(L); 183 184 lua_getglobal(L, "unpack"); 185 env["unpack"] = popValue!LuaObject(L); 186 187 multRet.setEnvironment(env); 188 assert(multRet.call!int() == 42); 189 190 }