Skip to content

Commit 959ce84

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 c2d85ad commit 959ce84

5 files changed

Lines changed: 653 additions & 178 deletions

File tree

luad/conversions/classes.d

Lines changed: 149 additions & 70 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,56 +15,145 @@ import luad.base;
1415
import core.memory;
1516

1617
import std.traits;
17-
import std.string : toStringz;
18+
import std.typetuple;
1819

19-
extern(C) private int classCleaner(lua_State* L)
20+
21+
void pushGetter(T, string member)(lua_State* L)
2022
{
21-
GC.removeRoot(lua_touserdata(L, 1));
22-
return 0;
23+
alias RT = typeof(mixin("T."~member));
24+
final class C
25+
{
26+
static if((!isMemberFunction!(T, member) || returnsRef!(AliasMember!(T, member))) && isUserStruct!RT)
27+
{
28+
ref RT get()
29+
{
30+
T _this = *cast(T*)&this;
31+
return mixin("_this."~member);
32+
}
33+
}
34+
else
35+
{
36+
RT get()
37+
{
38+
T _this = *cast(T*)&this;
39+
return mixin("_this."~member);
40+
}
41+
}
42+
}
43+
44+
lua_pushlightuserdata(L, (&C.init.get).funcptr);
45+
lua_pushcclosure(L, &methodWrapper!(typeof(&C.init.get), T, false), 1);
2346
}
2447

25-
private void pushMeta(T)(lua_State* L, T obj)
48+
private void pushGetters(T)(lua_State* L)
2649
{
27-
if(luaL_newmetatable(L, T.mangleof.ptr) == 0)
28-
return;
50+
lua_newtable(L); // -2 is getters
51+
lua_newtable(L); // -1 is methods
2952

30-
pushValue(L, T.stringof);
31-
lua_setfield(L, -2, "__dclass");
53+
// populate getters
54+
foreach(member; __traits(derivedMembers, T))
55+
{
56+
static if(!skipMember!(T, member) &&
57+
!isStaticMember!(T, member) &&
58+
member != "Monitor")
59+
{
60+
static if(isMemberFunction!(T, member) && !isProperty!(T, member))
61+
{
62+
pushMethod!(T, member)(L);
63+
lua_setfield(L, -2, member.ptr);
64+
}
65+
else
66+
{
67+
pushGetter!(T, member)(L);
68+
lua_setfield(L, -3, member.ptr);
69+
}
70+
}
71+
}
3272

33-
pushValue(L, T.mangleof);
34-
lua_setfield(L, -2, "__dmangle");
73+
lua_pushcclosure(L, &index, 2);
74+
}
75+
76+
void pushSetter(T, string member)(lua_State* L)
77+
{
78+
// TODO: This is broken if setter argument is different from the getter's return type...
79+
alias ArgType = typeof(mixin("T."~member));
80+
81+
final class C
82+
{
83+
static if(isUserStruct!ArgType)
84+
{
85+
final void set(ref ArgType value)
86+
{
87+
T _this = *cast(T*)&this;
88+
mixin("_this."~member) = value;
89+
}
90+
}
91+
else
92+
{
93+
final void set(ArgType value)
94+
{
95+
T _this = *cast(T*)&this;
96+
mixin("_this."~member) = value;
97+
}
98+
}
99+
}
100+
101+
lua_pushlightuserdata(L, (&C.init.set).funcptr);
102+
lua_pushcclosure(L, &methodWrapper!(typeof(&C.init.set), T, false), 1);
103+
}
35104

36-
lua_newtable(L); //__index fallback table
105+
private void pushSetters(T)(lua_State* L)
106+
{
107+
lua_newtable(L);
37108

109+
// populate setters
38110
foreach(member; __traits(derivedMembers, T))
39111
{
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
112+
static if(!skipMember!(T, member) &&
113+
!isStaticMember!(T, member) &&
114+
canWrite!(T, member) && // TODO: move into the setter for readonly fields
115+
member != "Monitor")
45116
{
46-
static if(__traits(getOverloads, T.init, member).length > 0 && !__traits(isStaticFunction, mixin("T." ~ member)))
117+
static if(!isMemberFunction!(T, member) || isProperty!(T, member))
47118
{
48-
pushMethod!(T, member)(L);
49-
lua_setfield(L, -2, toStringz(member));
119+
pushSetter!(T, member)(L);
120+
lua_setfield(L, -2, member.ptr);
50121
}
51122
}
52123
}
53124

125+
lua_pushcclosure(L, &newIndex, 1);
126+
}
127+
128+
private void pushMeta(T)(lua_State* L)
129+
{
130+
if(luaL_newmetatable(L, T.mangleof.ptr) == 0)
131+
return;
132+
133+
pushValue(L, T.stringof);
134+
lua_setfield(L, -2, "__dtype");
135+
136+
// TODO: mangled names can get REALLY long in D, it might be nicer to store a hash instead?
137+
pushValue(L, T.mangleof);
138+
lua_setfield(L, -2, "__dmangle");
139+
140+
lua_pushcfunction(L, &userdataCleaner);
141+
lua_setfield(L, -2, "__gc");
142+
143+
pushGetters!T(L);
54144
lua_setfield(L, -2, "__index");
145+
pushSetters!T(L);
146+
lua_setfield(L, -2, "__newindex");
55147

56148
pushMethod!(T, "toString")(L);
57149
lua_setfield(L, -2, "__tostring");
58150

59151
pushMethod!(T, "opEquals")(L);
60152
lua_setfield(L, -2, "__eq");
61153

62-
//TODO: handle opCmp here
63-
154+
// TODO: handle opCmp here
64155

65-
lua_pushcfunction(L, &classCleaner);
66-
lua_setfield(L, -2, "__gc");
156+
// TODO: operators, etc...
67157

68158
lua_pushvalue(L, -1);
69159
lua_setfield(L, -2, "__metatable");
@@ -74,34 +164,16 @@ void pushClassInstance(T)(lua_State* L, T obj) if (is(T == class))
74164
T* ud = cast(T*)lua_newuserdata(L, obj.sizeof);
75165
*ud = obj;
76166

77-
pushMeta(L, obj);
78-
lua_setmetatable(L, -2);
79-
80167
GC.addRoot(ud);
168+
169+
pushMeta!T(L);
170+
lua_setmetatable(L, -2);
81171
}
82172

83-
//TODO: handle foreign userdata properly (i.e. raise errors)
84173
T getClassInstance(T)(lua_State* L, int idx) if (is(T == class))
85174
{
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
175+
//TODO: handle foreign userdata properly (i.e. raise errors)
176+
verifyType!T(L, idx);
105177

106178
Object obj = *cast(Object*)lua_touserdata(L, idx);
107179
return cast(T)obj;
@@ -112,36 +184,42 @@ template hasCtor(T)
112184
enum hasCtor = __traits(compiles, __traits(getOverloads, T.init, "__ctor"));
113185
}
114186

115-
// TODO: exclude private members (I smell DMD bugs...)
116-
template isStaticMember(T, string member)
187+
// For use as __call
188+
void pushCallMetaConstructor(T)(lua_State* L)
117189
{
118-
static if(__traits(compiles, mixin("&T." ~ member)))
190+
static if(!hasCtor!T)
119191
{
120-
static if(is(typeof(mixin("&T.init." ~ member)) == delegate))
121-
enum isStaticMember = __traits(isStaticFunction, mixin("T." ~ member));
122-
else
123-
enum isStaticMember = true;
192+
static T ctor(LuaObject self)
193+
{
194+
static if(is(T == class))
195+
return new T;
196+
else
197+
return T.init;
198+
}
124199
}
125200
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)
135201
{
136-
return new T(args);
202+
// TODO: handle each constructor overload in a loop.
203+
// TODO: handle each combination of default args
204+
alias Ctors = typeof(__traits(getOverloads, T.init, "__ctor"));
205+
alias Args = ParameterTypeTuple!(Ctors[0]);
206+
207+
static T ctor(LuaObject self, Args args)
208+
{
209+
static if(is(T == class))
210+
return new T(args);
211+
else
212+
return T(args);
213+
}
137214
}
138215

139216
pushFunction(L, &ctor);
217+
lua_setfield(L, -2, "__call");
140218
}
141219

142220
// TODO: Private static fields are mysteriously pushed without error...
143221
// TODO: __index should be a function querying the static fields directly
144-
void pushStaticTypeInterface(T)(lua_State* L)
222+
void pushStaticTypeInterface(T)(lua_State* L) if(is(T == class) || is(T == struct))
145223
{
146224
lua_newtable(L);
147225

@@ -152,11 +230,7 @@ void pushStaticTypeInterface(T)(lua_State* L)
152230
return;
153231
}
154232

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

161235
lua_newtable(L);
162236

@@ -165,7 +239,12 @@ void pushStaticTypeInterface(T)(lua_State* L)
165239
static if(isStaticMember!(T, member))
166240
{
167241
enum isFunction = is(typeof(mixin("T." ~ member)) == function);
242+
static if(isFunction)
243+
enum isProperty = (functionAttributes!(mixin("T." ~ member)) & FunctionAttribute.property);
244+
else
245+
enum isProperty = false;
168246

247+
// TODO: support static properties
169248
static if(isFunction)
170249
pushValue(L, mixin("&T." ~ member));
171250
else

0 commit comments

Comments
 (0)