Skip to content

Commit 409f91e

Browse files
committed
struct changed to use userdata blocks. Structs are handled by-ref in Lua code to match handling of regular tables.
struct and class have uniform support for: methods, properties, member variable accessors. Refactor common code into helpers.d Added support for const function args. pushMethod enhanced to support struct's aswell. New alias syntax updates.
1 parent 25b2bcd commit 409f91e

File tree

7 files changed

+825
-208
lines changed

7 files changed

+825
-208
lines changed

luad/conversions/classes.d

Lines changed: 110 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ See the source code for details.
55
*/
66
module luad.conversions.classes;
77

8+
import luad.conversions.helpers;
89
import luad.conversions.functions;
910

1011
import luad.c.all;
@@ -14,94 +15,119 @@ import luad.base;
1415
import core.memory;
1516

1617
import std.traits;
17-
import std.string : toStringz;
18+
import std.typetuple;
19+
import std.typecons;
1820

19-
extern(C) private int classCleaner(lua_State* L)
20-
{
21-
GC.removeRoot(lua_touserdata(L, 1));
22-
return 0;
23-
}
2421

25-
private void pushMeta(T)(lua_State* L, T obj)
22+
private void pushGetters(T)(lua_State* L)
2623
{
27-
if(luaL_newmetatable(L, T.mangleof.ptr) == 0)
28-
return;
24+
lua_newtable(L); // -2 is getters
25+
lua_newtable(L); // -1 is methods
2926

30-
pushValue(L, T.stringof);
31-
lua_setfield(L, -2, "__dclass");
27+
// populate getters
28+
foreach(member; __traits(derivedMembers, T))
29+
{
30+
static if(!skipMember!(T, member) &&
31+
!isStaticMember!(T, member) &&
32+
member != "Monitor")
33+
{
34+
static if(isMemberFunction!(T, member) && !isProperty!(T, member))
35+
{
36+
static if(canCall!(T, member))
37+
{
38+
pushMethod!(T, member)(L);
39+
lua_setfield(L, -2, member.ptr);
40+
}
41+
}
42+
else static if(canRead!(T, member)) // TODO: move into the getter for inaccessable fields (...and throw a useful error messasge)
43+
{
44+
pushGetter!(T, member)(L);
45+
lua_setfield(L, -3, member.ptr);
46+
}
47+
}
48+
}
3249

33-
pushValue(L, T.mangleof);
34-
lua_setfield(L, -2, "__dmangle");
50+
lua_pushcclosure(L, &index, 2);
51+
}
3552

36-
lua_newtable(L); //__index fallback table
53+
private void pushSetters(T)(lua_State* L)
54+
{
55+
lua_newtable(L);
3756

57+
// populate setters
3858
foreach(member; __traits(derivedMembers, T))
3959
{
40-
static if(__traits(getProtection, __traits(getMember, T, member)) == "public" && //ignore non-public fields
41-
member != "this" && member != "__ctor" && //do not handle
42-
member != "Monitor" && member != "toHash" && //do not handle
43-
member != "toString" && member != "opEquals" && //handle below
44-
member != "opCmp") //handle below
60+
static if(!skipMember!(T, member) &&
61+
!isStaticMember!(T, member) &&
62+
canWrite!(T, member) && // TODO: move into the setter for readonly fields (...and throw a useful error messasge)
63+
member != "Monitor")
4564
{
46-
static if(__traits(getOverloads, T.init, member).length > 0 && !__traits(isStaticFunction, mixin("T." ~ member)))
65+
static if(!isMemberFunction!(T, member) || isProperty!(T, member))
4766
{
48-
pushMethod!(T, member)(L);
49-
lua_setfield(L, -2, toStringz(member));
67+
pushSetter!(T, member)(L);
68+
lua_setfield(L, -2, member.ptr);
5069
}
5170
}
5271
}
5372

54-
lua_setfield(L, -2, "__index");
55-
56-
pushMethod!(T, "toString")(L);
57-
lua_setfield(L, -2, "__tostring");
73+
lua_pushcclosure(L, &newIndex, 1);
74+
}
5875

59-
pushMethod!(T, "opEquals")(L);
60-
lua_setfield(L, -2, "__eq");
76+
private void pushMeta(T)(lua_State* L)
77+
{
78+
if(luaL_newmetatable(L, T.mangleof.ptr) == 0)
79+
return;
6180

62-
//TODO: handle opCmp here
81+
pushValue(L, T.stringof);
82+
lua_setfield(L, -2, "__dtype");
6383

84+
// TODO: mangled names can get REALLY long in D, it might be nicer to store a hash instead?
85+
pushValue(L, T.mangleof);
86+
lua_setfield(L, -2, "__dmangle");
6487

65-
lua_pushcfunction(L, &classCleaner);
88+
lua_pushcfunction(L, &userdataCleaner);
6689
lua_setfield(L, -2, "__gc");
6790

91+
pushGetters!T(L);
92+
lua_setfield(L, -2, "__index");
93+
pushSetters!T(L);
94+
lua_setfield(L, -2, "__newindex");
95+
96+
// TODO: look into why we can't call these on const objects... that's insane, right?
97+
static if(canCall!(T, "toString"))
98+
{
99+
pushMethod!(T, "toString")(L);
100+
lua_setfield(L, -2, "__tostring");
101+
}
102+
static if(canCall!(T, "opEquals"))
103+
{
104+
pushMethod!(T, "opEquals")(L);
105+
lua_setfield(L, -2, "__eq");
106+
}
107+
108+
// TODO: handle opCmp here
109+
110+
// TODO: operators, etc...
111+
68112
lua_pushvalue(L, -1);
69113
lua_setfield(L, -2, "__metatable");
70114
}
71115

72116
void pushClassInstance(T)(lua_State* L, T obj) if (is(T == class))
73117
{
74-
T* ud = cast(T*)lua_newuserdata(L, obj.sizeof);
118+
Rebindable!T* ud = cast(Rebindable!T*)lua_newuserdata(L, obj.sizeof);
75119
*ud = obj;
76120

77-
pushMeta(L, obj);
78-
lua_setmetatable(L, -2);
79-
80121
GC.addRoot(ud);
122+
123+
pushMeta!T(L);
124+
lua_setmetatable(L, -2);
81125
}
82126

83-
//TODO: handle foreign userdata properly (i.e. raise errors)
84127
T getClassInstance(T)(lua_State* L, int idx) if (is(T == class))
85128
{
86-
if(lua_getmetatable(L, idx) == 0)
87-
{
88-
luaL_error(L, "attempt to get 'userdata: %p' as a D object", lua_topointer(L, idx));
89-
}
90-
91-
lua_getfield(L, -1, "__dmangle"); //must be a D object
92-
93-
static if(!is(T == Object)) //must be the right object
94-
{
95-
size_t manglelen;
96-
auto cmangle = lua_tolstring(L, -1, &manglelen);
97-
if(cmangle[0 .. manglelen] != T.mangleof)
98-
{
99-
lua_getfield(L, -2, "__dclass");
100-
auto cname = lua_tostring(L, -1);
101-
luaL_error(L, `attempt to get instance %s as type "%s"`, cname, toStringz(T.stringof));
102-
}
103-
}
104-
lua_pop(L, 2); //metatable and metatable.__dmangle
129+
//TODO: handle foreign userdata properly (i.e. raise errors)
130+
verifyType!T(L, idx);
105131

106132
Object obj = *cast(Object*)lua_touserdata(L, idx);
107133
return cast(T)obj;
@@ -112,36 +138,42 @@ template hasCtor(T)
112138
enum hasCtor = __traits(compiles, __traits(getOverloads, T.init, "__ctor"));
113139
}
114140

115-
// TODO: exclude private members (I smell DMD bugs...)
116-
template isStaticMember(T, string member)
141+
// For use as __call
142+
void pushCallMetaConstructor(T)(lua_State* L)
117143
{
118-
static if(__traits(compiles, mixin("&T." ~ member)))
144+
static if(!hasCtor!T)
119145
{
120-
static if(is(typeof(mixin("&T.init." ~ member)) == delegate))
121-
enum isStaticMember = __traits(isStaticFunction, mixin("T." ~ member));
122-
else
123-
enum isStaticMember = true;
146+
static T ctor(LuaObject self)
147+
{
148+
static if(is(T == class))
149+
return new T;
150+
else
151+
return T.init;
152+
}
124153
}
125154
else
126-
enum isStaticMember = false;
127-
}
128-
129-
// For use as __call
130-
void pushCallMetaConstructor(T)(lua_State* L)
131-
{
132-
alias typeof(__traits(getOverloads, T.init, "__ctor")) Ctor;
133-
134-
static T ctor(LuaObject self, ParameterTypeTuple!Ctor args)
135155
{
136-
return new T(args);
156+
// TODO: handle each constructor overload in a loop.
157+
// TODO: handle each combination of default args
158+
alias Ctors = typeof(__traits(getOverloads, T.init, "__ctor"));
159+
alias Args = ParameterTypeTuple!(Ctors[0]);
160+
161+
static T ctor(LuaObject self, Args args)
162+
{
163+
static if(is(T == class))
164+
return new T(args);
165+
else
166+
return T(args);
167+
}
137168
}
138169

139170
pushFunction(L, &ctor);
171+
lua_setfield(L, -2, "__call");
140172
}
141173

142174
// TODO: Private static fields are mysteriously pushed without error...
143175
// TODO: __index should be a function querying the static fields directly
144-
void pushStaticTypeInterface(T)(lua_State* L)
176+
void pushStaticTypeInterface(T)(lua_State* L) if(is(T == class) || is(T == struct))
145177
{
146178
lua_newtable(L);
147179

@@ -152,11 +184,7 @@ void pushStaticTypeInterface(T)(lua_State* L)
152184
return;
153185
}
154186

155-
static if(hasCtor!T)
156-
{
157-
pushCallMetaConstructor!T(L);
158-
lua_setfield(L, -2, "__call");
159-
}
187+
pushCallMetaConstructor!T(L);
160188

161189
lua_newtable(L);
162190

@@ -165,7 +193,12 @@ void pushStaticTypeInterface(T)(lua_State* L)
165193
static if(isStaticMember!(T, member))
166194
{
167195
enum isFunction = is(typeof(mixin("T." ~ member)) == function);
196+
static if(isFunction)
197+
enum isProperty = (functionAttributes!(mixin("T." ~ member)) & FunctionAttribute.property);
198+
else
199+
enum isProperty = false;
168200

201+
// TODO: support static properties
169202
static if(isFunction)
170203
pushValue(L, mixin("&T." ~ member));
171204
else

0 commit comments

Comments
 (0)