1 /**
2 Internal module for pushing and getting _structs.
3
4 A struct is treated as a table layout schema.
5 Pushing a struct to Lua will create a table and fill it with key-value pairs - corresponding to struct fields - from the struct; the field name becomes the table key as a string.
6 Struct methods are treated as if they were delegate fields pointing to the method.
7 For an example, see the "Configuration File" example on the $(LINK2 $(REFERENCETOP),front page).
8 */
9 module luad.conversions.structs;
10
11 import luad.c.all;
12
13 import luad.stack;
14
15 private template isInternal(string field)
16 {
17 enum isInternal = field.length >= 2 && field[0..2] == "__";
18 }
19
20 //TODO: ignore static fields, post-blits, destructors, etc?
21 void pushStruct(T)(lua_State* L, ref T value) if (is(T == struct))
22 {
23 lua_createtable(L, 0, value.tupleof.length);
24
25 foreach(field; __traits(allMembers, T))
26 {
27 static if(!isInternal!field &&
28 field != "this" &&
29 field != "opAssign")
30 {
31 pushValue(L, field);
32
33 enum isMemberFunction = mixin("is(typeof(&value." ~ field ~ ") == delegate)");
34
35 static if(isMemberFunction)
36 pushValue(L, mixin("&value." ~ field));
37 else
38 pushValue(L, mixin("value." ~ field));
39
40 lua_settable(L, -3);
41 }
42 }
43 }
44
45 T getStruct(T)(lua_State* L, int idx) if(is(T == struct))
46 {
47 T s;
48 fillStruct(L, idx, s);
49 return s;
50 }
51
52 void fillStruct(T)(lua_State* L, int idx, ref T s) if(is(T == struct))
53 {
54 foreach(field; __traits(allMembers, T))
55 {
56 static if(field != "this" && !isInternal!(field))
57 {
58 static if(__traits(getOverloads, T, field).length == 0)
59 {
60 lua_getfield(L, idx, field.ptr);
61 if(lua_isnil(L, -1) == 0) {
62 mixin("s." ~ field ~
63 " = popValue!(typeof(s." ~ field ~ "))(L);");
64 } else
65 lua_pop(L, 1);
66 }
67 }
68 }
69 }
70
71 version(unittest)
72 {
73 import luad.base;
74 struct S
75 {
76 LuaObject o;
77 int i;
78 double n;
79 string s;
80
81 string f(){ return "foobar"; }
82 }
83 }
84
85 unittest
86 {
87 import luad.testing;
88
89 lua_State* L = luaL_newstate();
90 scope(success) lua_close(L);
91 luaL_openlibs(L);
92
93 pushValue(L, "test");
94 auto obj = popValue!LuaObject(L);
95
96 pushValue(L, S(obj, 1, 2.3, "hello"));
97 assert(lua_istable(L, -1));
98 lua_setglobal(L, "struct");
99
100 unittest_lua(L, `
101 for key, expected in pairs{i = 1, n = 2.3, s = "hello"} do
102 local value = struct[key]
103 assert(value == expected,
104 ("bad table pair: '%s' = '%s' (expected '%s')"):format(key, value, expected)
105 )
106 end
107
108 assert(struct.f() == "foobar")
109 `);
110
111 lua_getglobal(L, "struct");
112 S s = getStruct!S(L, -1);
113
114 assert(s.o == obj);
115 assert(s.i == 1);
116 assert(s.n == 2.3);
117 assert(s.s == "hello");
118
119 lua_pop(L, 1);
120
121 struct S2
122 {
123 string a, b;
124 }
125
126 unittest_lua(L, `
127 incompleteStruct = {a = "foo"}
128 `);
129
130 lua_getglobal(L, "incompleteStruct");
131 S2 s2 = getStruct!S2(L, -1);
132
133 assert(s2.a == "foo");
134 assert(s2.b == null);
135
136 lua_pop(L, 1);
137 }