neptools

Modding tools to Neptunia games
git clone https://git.neptards.moe/neptards/neptools.git
Log | Files | Refs | Submodules | README | LICENSE

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