Skip to content

Commit 7294e42

Browse files
committed
Added support for pointers.
Pointers are opaque. Pointers support 'nil' assignment. 'deref' property on pointers to access the actual object.
1 parent 3a33813 commit 7294e42

3 files changed

Lines changed: 158 additions & 2 deletions

File tree

luad/conversions/pointers.d

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/**
2+
Internal module for pushing and getting pointers.
3+
Pointers are stored in metadata with metatables to enfirce type-safety.
4+
A 'deref' property is created to access the pointer's value.
5+
*/
6+
module luad.conversions.pointers;
7+
8+
import luad.conversions.helpers;
9+
import luad.conversions.functions;
10+
11+
import luad.c.all;
12+
import luad.stack;
13+
14+
import core.memory;
15+
16+
import std.traits;
17+
import std.conv;
18+
19+
void pushGetter(T)(lua_State* L)
20+
{
21+
static if(isUserStruct!(PointerTarget!T))
22+
{
23+
static ref PointerTarget!T deref(T ptr)
24+
{
25+
return *ptr;
26+
}
27+
}
28+
else
29+
{
30+
static PointerTarget!T deref(T ptr)
31+
{
32+
return *ptr;
33+
}
34+
}
35+
36+
lua_pushlightuserdata(L, &deref);
37+
lua_pushcclosure(L, &functionWrapper!(typeof(&deref)), 1);
38+
}
39+
40+
private void pushGetters(T)(lua_State* L)
41+
{
42+
lua_newtable(L); // -2 is getters
43+
lua_newtable(L); // -1 is methods
44+
45+
pushGetter!T(L);
46+
lua_setfield(L, -3, "deref");
47+
48+
lua_pushcclosure(L, &index, 2);
49+
}
50+
51+
void pushSetter(T)(lua_State* L)
52+
{
53+
static if(isUserStruct!(PointerTarget!T))
54+
{
55+
static void deref(T ptr, ref PointerTarget!T val)
56+
{
57+
*ptr = val;
58+
}
59+
}
60+
else
61+
{
62+
static void deref(T ptr, PointerTarget!T val)
63+
{
64+
*ptr = val;
65+
}
66+
}
67+
68+
lua_pushlightuserdata(L, &deref);
69+
lua_pushcclosure(L, &functionWrapper!(typeof(&deref)), 1);
70+
}
71+
72+
private void pushSetters(T)(lua_State* L)
73+
{
74+
lua_newtable(L);
75+
76+
pushSetter!T(L);
77+
lua_setfield(L, -2, "deref");
78+
79+
lua_pushcclosure(L, &newIndex, 1);
80+
}
81+
82+
private void pushMeta(T)(lua_State* L)
83+
{
84+
if(luaL_newmetatable(L, T.mangleof.ptr) == 0)
85+
return;
86+
87+
pushValue(L, T.stringof);
88+
lua_setfield(L, -2, "__dtype");
89+
90+
// TODO: mangled names can get REALLY long in D, it might be nicer to store a hash instead?
91+
pushValue(L, T.mangleof);
92+
lua_setfield(L, -2, "__dmangle");
93+
94+
lua_pushcfunction(L, &userdataCleaner);
95+
lua_setfield(L, -2, "__gc");
96+
97+
static if(!is(Unqual!(PointerTarget!T) == void))
98+
{
99+
pushGetters!T(L);
100+
lua_setfield(L, -2, "__index");
101+
static if(isMutable!(PointerTarget!T))
102+
{
103+
pushSetters!T(L);
104+
lua_setfield(L, -2, "__newindex");
105+
}
106+
}
107+
108+
lua_pushvalue(L, -1);
109+
lua_setfield(L, -2, "__metatable");
110+
}
111+
112+
void pushPointer(T)(lua_State* L, T value) if (isPointer!T)
113+
{
114+
T* udata = cast(T*)lua_newuserdata(L, T.sizeof);
115+
*udata = value;
116+
117+
GC.addRoot(udata);
118+
119+
pushMeta!T(L);
120+
lua_setmetatable(L, -2);
121+
}
122+
123+
124+
T getPointer(T)(lua_State* L, int idx) if(isPointer!T)
125+
{
126+
verifyType!T(L, idx);
127+
128+
T* udata = cast(T*)lua_touserdata(L, idx);
129+
return *udata;
130+
}
131+
132+
version(unittest)
133+
{
134+
import luad.base;
135+
}
136+
137+
unittest
138+
{
139+
import luad.testing;
140+
141+
lua_State* L = luaL_newstate();
142+
scope(success) lua_close(L);
143+
luaL_openlibs(L);
144+
145+
}

luad/stack.d

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ import luad.conversions.arrays;
7171
import luad.conversions.structs;
7272
import luad.conversions.assocarrays;
7373
import luad.conversions.classes;
74+
import luad.conversions.pointers;
7475
import luad.conversions.variant;
7576
import luad.conversions.helpers;
7677

@@ -132,6 +133,12 @@ void pushValue(T)(lua_State* L, T value) if(!isUserStruct!T)
132133
else static if(isSomeFunction!T)
133134
pushFunction(L, value);
134135

136+
else static if(isPointer!T)
137+
{
138+
// TODO: if value == null -> lua_pushnil(L);
139+
pushPointer(L, value);
140+
}
141+
135142
else static if(is(T == class))
136143
{
137144
if(value is null)
@@ -188,7 +195,7 @@ template luaTypeOf(T)
188195
else static if(isArray!T || isAssociativeArray!T || is(T == LuaTable))
189196
enum luaTypeOf = LUA_TTABLE;
190197

191-
else static if(is(T : const(Object)) || is(T == struct))
198+
else static if(is(T : const(Object)) || is(T == struct) || isPointer!T)
192199
enum luaTypeOf = LUA_TUSERDATA;
193200

194201
else
@@ -236,7 +243,7 @@ T getValue(T, alias typeMismatchHandler = defaultTypeMismatch)(lua_State* L, int
236243
enum expectedType = luaTypeOf!T;
237244

238245
//if a class reference, return null for nil values
239-
static if(is(T : const(Object)))
246+
static if(is(T : const(Object)) || isPointer!T)
240247
{
241248
if(type == LuaType.Nil)
242249
return null;
@@ -305,6 +312,9 @@ T getValue(T, alias typeMismatchHandler = defaultTypeMismatch)(lua_State* L, int
305312
else static if(isSomeFunction!T)
306313
return getFunction!T(L, idx);
307314

315+
else static if(isPointer!T)
316+
return getPointer!T(L, idx);
317+
308318
else static if(is(T : const(Object)))
309319
return getClassInstance!T(L, idx);
310320

visuald/LuaD.visualdproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@
298298
<File path="..\luad\conversions\classes.d" />
299299
<File path="..\luad\conversions\functions.d" />
300300
<File path="..\luad\conversions\helpers.d" />
301+
<File path="..\luad\conversions\pointers.d" />
301302
<File path="..\luad\conversions\structs.d" />
302303
<File path="..\luad\conversions\variant.d" />
303304
</Folder>

0 commit comments

Comments
 (0)