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 }