dynamic_struct.lua.hpp (12143B)
1 #ifndef UUID_8BEC2FCA_66B2_48E5_A689_E29590320829 2 #define UUID_8BEC2FCA_66B2_48E5_A689_E29590320829 3 #pragma once 4 5 #if !LIBSHIT_WITH_LUA 6 #define NEPTOOLS_DYNAMIC_STRUCT_LUAGEN(name, ...) 7 #define NEPTOOLS_DYNAMIC_STRUCT_TABLECTOR(...) 8 #else 9 10 #include "dynamic_struct.hpp" 11 12 #include <libshit/lua/auto_table.hpp> 13 #include <libshit/lua/user_type.hpp> 14 #include <libshit/utils.hpp> 15 16 #include <type_traits> 17 #include <boost/integer.hpp> 18 19 namespace Neptools 20 { 21 22 namespace Detail 23 { 24 25 template <typename T, size_t N, typename... Args> struct IndexOfImpl; 26 27 template <typename T, size_t N, typename... Args> 28 struct IndexOfImpl<T, N, T, Args...> 29 { static constexpr size_t VALUE = N; }; 30 31 template <typename T, size_t N, typename U, typename... Args> 32 struct IndexOfImpl<T, N, U, Args...> 33 { static constexpr size_t VALUE = IndexOfImpl<T,N+1,Args...>::VALUE; }; 34 35 template <typename T, typename... Args> 36 constexpr size_t IndexOf = IndexOfImpl<T, 0, Args...>::VALUE; 37 38 template <typename T> 39 struct DynamicStructTypeTraitsName 40 { 41 static constexpr const char* NAME = Libshit::Lua::TYPE_NAME<T>; 42 }; 43 44 } 45 46 template <typename T, typename Enable = void> 47 struct DynamicStructTypeTraits : Detail::DynamicStructTypeTraitsName<T> 48 { 49 static void Push(Libshit::Lua::StateRef vm, const void* ptr, size_t size) 50 { 51 LIBSHIT_ASSERT(size == sizeof(T)); (void) size; 52 vm.Push(*static_cast<const T*>(ptr)); 53 } 54 55 static void Get(Libshit::Lua::StateRef vm, int idx, void* ptr, size_t size) 56 { 57 LIBSHIT_ASSERT(size == sizeof(T)); (void) size; 58 *static_cast<T*>(ptr) = vm.Check<T>(idx); 59 } 60 61 static constexpr bool SIZABLE = false; 62 }; 63 64 // do not use standard lua names for them since they're only "integer"/"number" 65 // as far as lua is concerned 66 #define CNAME(type, name) \ 67 template<> struct Detail::DynamicStructTypeTraitsName<type> \ 68 { static constexpr const char* NAME = name; } 69 CNAME(int8_t, "int8"); CNAME(int16_t, "int16"); 70 CNAME(int32_t, "int32"); CNAME(int64_t, "int64"); 71 CNAME(uint8_t, "uint8"); CNAME(uint16_t, "uint16"); 72 CNAME(uint32_t, "uint32"); CNAME(uint64_t, "uint64"); 73 CNAME(float, "float"); CNAME(double, "double"); 74 #undef CNAME 75 76 template <typename... Args> 77 struct DynamicStructTypeInfo 78 { 79 void (*push)(Libshit::Lua::StateRef vm, const void* ptr, size_t size); 80 void (*get)(Libshit::Lua::StateRef vm, int idx, void* ptr, size_t size); 81 const char* name; 82 83 typename boost::uint_value_t<sizeof...(Args)-1>::least index; 84 typename boost::uint_value_t<std::max({sizeof(Args)...})>::least size; 85 bool sizable; 86 }; 87 88 template <typename... Args> 89 inline constexpr const DynamicStructTypeInfo<Args...> infos[sizeof...(Args)] = { 90 { 91 &DynamicStructTypeTraits<Args>::Push, 92 &DynamicStructTypeTraits<Args>::Get, 93 DynamicStructTypeTraits<Args>::NAME, 94 Detail::IndexOf<Args, Args...>, 95 sizeof(Args), 96 DynamicStructTypeTraits<Args>::SIZABLE, 97 }..., 98 }; 99 100 template <typename... Args> 101 struct DynamicStructBuilderLua 102 { 103 using FakeClass = typename DynamicStruct<Args...>::TypeBuilder; 104 105 LIBSHIT_NOLUA 106 static const DynamicStructTypeInfo<Args...>& 107 GetInfo(Libshit::Lua::StateRef vm, Libshit::Lua::Raw<LUA_TSTRING> name) 108 { 109 LIBSHIT_LUA_GETTOP(vm, top); 110 111 int r = lua_rawgetp(vm, LUA_REGISTRYINDEX, &infos<Args...>); //+1 112 LIBSHIT_ASSERT(r); 113 lua_pushvalue(vm, name); //+2 114 r = lua_rawget(vm, -2); //+2 115 if (Libshit::Lua::IsNoneOrNil(r)) 116 luaL_error(vm, "Invalid type %s", vm.Get<const char*, true>(name)); 117 118 LIBSHIT_ASSERT(r == LUA_TLIGHTUSERDATA); 119 auto ret = lua_touserdata(vm, -1); 120 LIBSHIT_ASSERT(ret); 121 lua_pop(vm, 2); //+0 122 123 LIBSHIT_LUA_CHECKTOP(vm, top); 124 return *static_cast<DynamicStructTypeInfo<Args...>*>(ret); 125 } 126 127 // bld:add(type, size) -> bld 128 static Libshit::Lua::RetNum Add( 129 Libshit::Lua::StateRef vm, 130 typename DynamicStruct<Args...>::TypeBuilder& bld, 131 Libshit::Lua::Raw<LUA_TSTRING> name, size_t size) 132 { 133 LIBSHIT_LUA_GETTOP(vm, top); 134 auto& t = GetInfo(vm, name); 135 if (!t.sizable && t.size != size) 136 luaL_error(vm, "Type %s is not sizable", t.name); 137 bld.Add(t.index, size); 138 139 lua_pushvalue(vm, 1); 140 LIBSHIT_LUA_CHECKTOP(vm, top+1); 141 return 1; 142 } 143 144 // bld:add(type) -> bld 145 static Libshit::Lua::RetNum Add( 146 Libshit::Lua::StateRef vm, 147 typename DynamicStruct<Args...>::TypeBuilder& bld, 148 Libshit::Lua::Raw<LUA_TSTRING> name) 149 { 150 LIBSHIT_LUA_GETTOP(vm, top); 151 auto& t = GetInfo(vm, name); 152 if (t.sizable) 153 luaL_error(vm, "Type %s requires size", t.name); 154 bld.Add(t.index, t.size); 155 156 lua_pushvalue(vm, 1); 157 LIBSHIT_LUA_CHECKTOP(vm, top+1); 158 return 1; 159 } 160 }; 161 162 template <typename... Args> 163 struct DynamicStructTypeLua 164 { 165 using FakeClass = typename DynamicStruct<Args...>::Type; 166 using Builder = typename DynamicStruct<Args...>::TypeBuilder; 167 using BuilderLua = DynamicStructBuilderLua<Args...>; 168 169 // type[i] -> {type=string,size=int}|nil 170 static Libshit::Lua::RetNum Get( 171 Libshit::Lua::StateRef vm, 172 const typename DynamicStruct<Args...>::Type& t, 173 size_t i) noexcept 174 { 175 LIBSHIT_LUA_GETTOP(vm, top); 176 if (i >= t.item_count) 177 { 178 lua_pushnil(vm); // +1 179 LIBSHIT_LUA_CHECKTOP(vm, top+1); 180 return 1; 181 } 182 LIBSHIT_ASSERT(t.items[i].idx < sizeof...(Args)); 183 184 const auto& info = infos<Args...>[t.items[i].idx]; 185 lua_createtable(vm, 0, 2); // +1 186 lua_pushstring(vm, info.name); // +2 187 lua_setfield(vm, -2, "type"); // +1 188 189 lua_pushinteger(vm, info.size); // +2 190 lua_setfield(vm, -2, "size"); // +1 191 192 LIBSHIT_LUA_CHECKTOP(vm, top+1); 193 return 1; 194 } 195 196 static void Get( 197 const typename DynamicStruct<Args...>::Type&, 198 Libshit::Lua::VarArg) noexcept {} 199 200 LIBSHIT_NOLUA 201 // {"name",size} or {name="name",size=size} 202 // size optional 203 static void AddTableType(Libshit::Lua::StateRef vm, Builder& bld) 204 { 205 LIBSHIT_LUA_GETTOP(vm, top); 206 int size_type = LUA_TSTRING; // whatever, just be invalid 207 if (lua_rawgeti(vm, -1, 1) == LUA_TSTRING) // +1 208 size_type = lua_rawgeti(vm, -2, 2); // +2 209 else if (lua_pop(vm, 1); lua_getfield(vm, -1, "name") == LUA_TSTRING) // +1 210 size_type = lua_getfield(vm, -2, "size"); // +2 211 212 if (Libshit::Lua::IsNoneOrNil(size_type)) 213 BuilderLua::Add(vm, bld, {lua_absindex(vm, -2)}); // +3 214 else if (size_type == LUA_TNUMBER) 215 BuilderLua::Add(vm, bld, {lua_absindex(vm, -2)}, 216 vm.Get<int, true>(-1)); // +3 217 else 218 luaL_error(vm, "invalid type table, expected {string,integer} or " 219 "{name=string, size=integer}"); 220 221 lua_pop(vm, 2); 222 LIBSHIT_LUA_CHECKTOP(vm, top+1); 223 } 224 225 // create from table 226 static boost::intrusive_ptr<const FakeClass> 227 New(Libshit::Lua::StateRef vm, Libshit::Lua::RawTable tbl) 228 { 229 Builder bld; 230 auto [len, one] = vm.RawLen01(tbl); 231 bld.Reserve(len); 232 vm.Fori(tbl, one, len, [&](size_t, int type) 233 { 234 if (type == LUA_TSTRING) 235 BuilderLua::Add(vm, bld, {lua_absindex(vm, -1)}); // +1 236 else if (type == LUA_TTABLE) 237 AddTableType(vm, bld); // +1 238 else 239 vm.TypeError(false, "string or table", -1); 240 241 lua_pop(vm, 1); // 0 242 }); 243 244 return bld.Build(); 245 } 246 }; 247 248 template <typename... Args> 249 struct DynamicStructLua 250 { 251 using FakeClass = DynamicStruct<Args...>; // must be first 252 static_assert(sizeof...(Args) > 0); 253 254 static Libshit::Lua::RetNum Get( 255 Libshit::Lua::StateRef vm, const DynamicStruct<Args...>& s, 256 size_t i) noexcept 257 { 258 if (i >= s.GetSize()) 259 { 260 lua_pushnil(vm); 261 return 1; 262 } 263 auto idx = s.GetTypeIndex(i); 264 LIBSHIT_ASSERT(idx < sizeof...(Args)); 265 infos<Args...>[idx].push(vm, s.GetData(i), s.GetSize(i)); 266 return 1; 267 } 268 static void Get( 269 const DynamicStruct<Args...>&, Libshit::Lua::VarArg) noexcept {} 270 271 static void Set( 272 Libshit::Lua::StateRef vm, DynamicStruct<Args...>& s, size_t i, 273 Libshit::Lua::Any val) 274 { 275 if (i >= s.GetSize()) 276 LIBSHIT_THROW(std::out_of_range, "DynamicStruct"); 277 auto idx = s.GetTypeIndex(i); 278 LIBSHIT_ASSERT(idx < sizeof...(Args)); 279 infos<Args...>[idx].get(vm, val, s.GetData(i), s.GetSize(i)); 280 } 281 282 static Libshit::Lua::RetNum ToTable( 283 Libshit::Lua::StateRef vm, DynamicStruct<Args...>& s) 284 { 285 auto size = s.GetSize(); 286 lua_createtable(vm, size ? size-1 : size, 0); // +1 287 for (size_t i = 0; i < size; ++i) 288 { 289 auto idx = s.GetTypeIndex(i); 290 LIBSHIT_ASSERT(idx < sizeof...(Args)); 291 infos<Args...>[idx].push(vm, s.GetData(i), s.GetSize(i)); // +2 292 lua_rawseti(vm, -2, i); // +1 293 } 294 return 1; 295 } 296 297 static boost::intrusive_ptr<FakeClass> New( 298 Libshit::Lua::StateRef vm, const typename FakeClass::TypePtr type, 299 Libshit::Lua::RawTable vals) 300 { 301 auto s = FakeClass::New(type); 302 size_t i = 0; 303 vm.Ipairs01(vals, [&](size_t, int) 304 { Set(vm, *s, i++, {lua_absindex(vm, -1)}); }); 305 return s; 306 } 307 308 LIBSHIT_NOLUA static void Register(Libshit::Lua::TypeBuilder& bld) 309 { 310 // create type table 311 lua_createtable(bld, sizeof...(Args)-1, 0); //+1 312 313 for (size_t i = 0; i < sizeof...(Args); ++i) 314 { 315 lua_pushlightuserdata( 316 bld, Libshit::implicit_const_cast<void*>(&infos<Args...>[i])); //+2 317 lua_setfield(bld, -2, infos<Args...>[i].name); //+1 318 } 319 lua_rawsetp(bld, LUA_REGISTRYINDEX, infos<Args...>); //+0 320 321 luaL_getmetatable(bld, "libshit_ipairs"); 322 bld.SetField("__ipairs"); 323 } 324 }; 325 326 } 327 328 #define NEPTOOLS_DYNAMIC_STRUCT_TABLECTOR(...) \ 329 /* workaround can't specialize for nested classes in template class, because \ 330 well, including a wrapper around cairo in the standard is more important \ 331 than fixing problems like this */ \ 332 template<> struct Libshit::Lua::GetTableCtor< \ 333 boost::intrusive_ptr<const ::Neptools::DynamicStruct<__VA_ARGS__>::Type>> \ 334 : std::integral_constant<::Libshit::Lua::TableCtorPtr< \ 335 boost::intrusive_ptr< \ 336 const ::Neptools::DynamicStruct<__VA_ARGS__>::Type>>, \ 337 ::Neptools::DynamicStructTypeLua<__VA_ARGS__>::New> {} 338 #define NEPTOOLS_DYNAMIC_STRUCT_LUAGEN(nam, ...) \ 339 template class ::Neptools::DynamicStruct<__VA_ARGS__>::TypeBuilder; \ 340 template struct ::Neptools::DynamicStructLua<__VA_ARGS__>; \ 341 template struct ::Neptools::DynamicStructBuilderLua<__VA_ARGS__>; \ 342 template struct ::Neptools::DynamicStructTypeLua<__VA_ARGS__>; \ 343 template<> struct Libshit::Lua::GetTableCtor< \ 344 ::Libshit::NotNull<boost::intrusive_ptr<::Neptools::DynamicStruct<__VA_ARGS__>>>> \ 345 : std::integral_constant<::Libshit::Lua::TableCtorPtr< \ 346 ::Libshit::NotNull<boost::intrusive_ptr<::Neptools::DynamicStruct<__VA_ARGS__>>>>, \ 347 nullptr> {}; \ 348 LIBSHIT_LUA_TEMPLATE(DynStructBind##nam, (name: #nam), \ 349 ::Neptools::DynamicStruct<__VA_ARGS__>); \ 350 LIBSHIT_LUA_TEMPLATE(DynStructTypeBind##nam, (name: #nam".type"), \ 351 ::Neptools::DynamicStruct<__VA_ARGS__>::Type); \ 352 LIBSHIT_LUA_TEMPLATE(DynStructBldBind##nam, (name: #nam".builder"), \ 353 ::Neptools::DynamicStruct<__VA_ARGS__>::TypeBuilder) 354 355 #endif 356 #endif