diff --git a/luad/all.d b/luad/all.d index a92c9ae..b1d5ee2 100644 --- a/luad/all.d +++ b/luad/all.d @@ -7,4 +7,4 @@ module luad.all; public import luad.base, luad.table, luad.lfunction, luad.dynamic, luad.state, luad.lmodule; -public import luad.conversions.functions : LuaVariableReturn, variableReturn; +public import luad.conversions.functions : LuaVariableReturn, variableReturn, noscript, rename, prefix, suffix; diff --git a/luad/conversions/classes.d b/luad/conversions/classes.d index cc9d78a..25ca335 100644 --- a/luad/conversions/classes.d +++ b/luad/conversions/classes.d @@ -43,10 +43,19 @@ private void pushMeta(T)(lua_State* L, T obj) member != "toString" && member != "opEquals" && //handle below member != "opCmp") //handle below { - static if(__traits(getOverloads, T.init, member).length > 0 && !__traits(isStaticFunction, mixin("T." ~ member))) + enum overloadCount = __traits(getOverloads, T.init, member).length; + + static if(!__traits(isStaticFunction, mixin("T." ~ member))) { - pushMethod!(T, member)(L); - lua_setfield(L, -2, toStringz(member)); + static if(overloadCount == 1) + { + string methodName = pushMethod!(T, member)(L); + + if(methodName != null) + lua_setfield(L, -2, toStringz(methodName)); + } + else static if(overloadCount > 1) + pushOverloadedMethod!(T, member)(L); } } } @@ -286,3 +295,37 @@ unittest lua_setglobal(L, "checkNull"); unittest_lua(L, `checkNull(nil)`); } + +// User defined attribute test +unittest +{ + static class A + { + int n = 5; + + @prefix("set") @property void foo(int x) + { + n = x; + } + + @rename("givefoo") @property int foo() { return n; } + + @noscript() @rename("shouldntExist") void bar() { } + @rename("shouldExist") void newbar() { } + } + + L = luaL_newstate(); + luaL_openlibs(L); + + A a = new A(); + pushValue(L, a); + lua_setglobal(L, "A"); + + unittest_lua(L, "assert(A ~= nil)"); + unittest_lua(L, "assert(A.shouldntExist == nil)"); + unittest_lua(L, "assert(A.bar == nil)"); + unittest_lua(L, "assert(A.shouldExist ~= nil)"); + unittest_lua(L, "assert(A.newbar == nil)"); + unittest_lua(L, "assert(A:givefoo() == 5)"); + unittest_lua(L, "A:setfoo(10); assert(A:givefoo() == 10)"); +} \ No newline at end of file diff --git a/luad/conversions/functions.d b/luad/conversions/functions.d index 1ff32f2..9b528ad 100644 --- a/luad/conversions/functions.d +++ b/luad/conversions/functions.d @@ -131,6 +131,54 @@ int callFunction(T)(lua_State* L, T func, ParameterTypeTuple!T args) return pushReturnValues(L, call()); } +// User defined attributes that can be placed on functions in order to change the binding behavior +// Prevents this function from being registered +struct noscript {} + +// Changes the name used to register this function +struct rename +{ + string Name; +} + +struct prefix +{ + string Affix; +} + +struct suffix +{ + string Affix; +} + +private: + +bool isNoScript(Attributes...)() +{ + foreach (attribute; Attributes) + { + static if(is(typeof(attribute) == noscript)) + return true; + } + + return false; +} + +string getProperName(string member, Attributes...)() +{ + foreach (attribute; Attributes) + { + static if(is(typeof(attribute) == rename)) + return attribute.Name; + else static if(is(typeof(attribute) == prefix)) + return attribute.Affix ~ member; + else static if(is(typeof(attribute) == suffix)) + return member ~ attribute.Affix; + } + + return member; +} + private: // TODO: right now, virtual functions on specialized classes can be called with base classes as 'self', not safe! @@ -248,19 +296,56 @@ void pushFunction(T)(lua_State* L, T func) if (isSomeFunction!T) lua_pushcclosure(L, &functionWrapper!T, 1); } -// TODO: optimize for non-virtual functions -void pushMethod(Class, string member)(lua_State* L) if (isSomeFunction!(__traits(getMember, Class, member))) +string pushMethod(Class, string member)(lua_State* L) if (isSomeFunction!(__traits(getMember, Class, member))) { alias typeof(mixin("&Class.init." ~ member)) T; + alias attributes = TypeTuple!(__traits(getAttributes, __traits(getMember, Class, member))); + + static if ((attributes.length == 0) || !isNoScript!attributes) + { + // Delay vtable lookup until the right time + static ReturnType!T virtualWrapper(Class self, ParameterTypeTuple!T args) + { + return mixin("self." ~ member)(args); + } + + lua_pushlightuserdata(L, &virtualWrapper); + lua_pushcclosure(L, &methodWrapper!(T, Class, true), 1); + + static if(attributes.length > 0) + return getProperName!(member, attributes); + else + return member; + } + else + return null; +} - // Delay vtable lookup until the right time - static ReturnType!T virtualWrapper(Class self, ParameterTypeTuple!T args) +// Called when binding a method that has multiple overloads, also used for properties for obvious reasons +void pushOverloadedMethod(Class, string member)(lua_State* L) if (isSomeFunction!(__traits(getMember, Class, member))) +{ + alias Overloads = TypeTuple!(__traits(getOverloads, Class, member)); + + template virtualWrapper(alias Args, FuncT) { - return mixin("self." ~ member)(args); + static ReturnType!FuncT vwFunc(Class self, ParameterTypeTuple!Args args) + { + return mixin("self." ~ member)(args); + } } - lua_pushlightuserdata(L, &virtualWrapper); - lua_pushcclosure(L, &methodWrapper!(T, Class, true), 1); + foreach(overload; Overloads) + { + alias FnType = FunctionTypeOf!overload; + alias attributes = TypeTuple!(__traits(getAttributes, overload)); + + static if ((attributes.length == 0) || !isNoScript!attributes) + { + lua_pushlightuserdata(L, &virtualWrapper!(overload, FnType).vwFunc); + lua_pushcclosure(L, &methodWrapper!(FnType, Class, true), 1); + lua_setfield(L, -2, toStringz(getProperName!(member, attributes))); + } + } } /** @@ -528,6 +613,8 @@ unittest // Variadic function arguments unittest { + import core.vararg; + static string concat(const(char)[][] pieces...) { string result; @@ -576,7 +663,6 @@ unittest //C varargs require at least one fixed argument. static string concat_cvar (int count, ...) { - import core.stdc.stdarg; string result; va_list args; @@ -596,8 +682,8 @@ unittest //D-style variadics have an _arguments array that specifies //the type of each passed argument. - static string concat_dvar (...) { - import core.vararg; + static string concat_dvar (...) + { string result; foreach (argtype; _arguments) {