Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for user defined attributes / manual overload resolution #71

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion luad/all.d
Original file line number Diff line number Diff line change
Expand Up @@ -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;
49 changes: 46 additions & 3 deletions luad/conversions/classes.d
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Expand Down Expand Up @@ -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)");
}
106 changes: 96 additions & 10 deletions luad/conversions/functions.d
Original file line number Diff line number Diff line change
Expand Up @@ -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!
Expand Down Expand Up @@ -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)));
}
}
}

/**
Expand Down Expand Up @@ -528,6 +613,8 @@ unittest
// Variadic function arguments
unittest
{
import core.vararg;

static string concat(const(char)[][] pieces...)
{
string result;
Expand Down Expand Up @@ -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;
Expand All @@ -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) {
Expand Down