neptools

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

dynamic_struct.hpp (8691B)


      1 #ifndef UUID_98C06485_8AE9_47DA_B99F_62CA5AF00FF4
      2 #define UUID_98C06485_8AE9_47DA_B99F_62CA5AF00FF4
      3 #pragma once
      4 
      5 #include "utils.hpp"
      6 
      7 #include <libshit/meta.hpp>
      8 #include <libshit/lua/intrusive_object.hpp>
      9 #include <libshit/lua/value_object.hpp>
     10 
     11 #include <atomic>
     12 #include <cstdint>
     13 #include <iostream>
     14 #include <memory>
     15 #include <typeindex>
     16 #include <vector>
     17 #include <boost/intrusive_ptr.hpp>
     18 
     19 namespace Neptools
     20 {
     21 
     22   template <typename Item, typename... Rest> struct IndexOf;
     23   template <typename Item, typename... Rest>
     24   struct IndexOf<Item, Item, Rest...>
     25     : std::integral_constant<size_t, 0> {};
     26 
     27   template <typename Item, typename Head, typename... Rest>
     28   struct IndexOf<Item, Head, Rest...>
     29     : std::integral_constant<size_t, 1 + IndexOf<Item, Rest...>::value> {};
     30 
     31   template <typename Item, typename... Args>
     32   constexpr auto IndexOfV = IndexOf<Item, Args...>::value;
     33 
     34   static_assert(IndexOfV<int, float, double, int> == 2);
     35 
     36   template <typename... Args>
     37   class LIBSHIT_LUAGEN(
     38     post_register: "::Neptools::DynamicStructLua</*$= template_args */>::Register(bld);")
     39   DynamicStruct final : public Libshit::Lua::IntrusiveObject
     40   {
     41     LIBSHIT_LUA_CLASS;
     42   private:
     43     template <typename Ret, size_t I, typename T, typename... TRest,
     44               typename Thiz, typename Fun, typename... FunArgs>
     45       static Ret VisitHlp(Thiz thiz, size_t i, Fun&& fun, FunArgs&&... args)
     46     {
     47       if (thiz->type->items[i].idx == I)
     48         return fun(thiz->template Get<T>(i), thiz->GetSize(i),
     49                    std::forward<FunArgs>(args)...);
     50       else
     51         return VisitHlp<Ret, I+1, TRest...>(
     52           thiz, i, std::forward<Fun>(fun), std::forward<FunArgs>(args)...);
     53     }
     54 
     55     template <typename Ret, size_t I, typename Thiz, typename Fun,
     56               typename... FunArgs>
     57     static Ret VisitHlp(Thiz, size_t, Fun&&, FunArgs&&...) { abort(); }
     58 
     59   public:
     60     template <typename T>
     61     LIBSHIT_NOLUA static constexpr size_t GetIndexFromType()
     62     { return IndexOfV<T, Args...>; }
     63 
     64     static constexpr const size_t SIZE_OF[] = { sizeof(Args)... };
     65     static constexpr const size_t ALIGN_OF[] = { alignof(Args)... };
     66 
     67     struct Type final : public Libshit::Lua::IntrusiveObject
     68     {
     69       LIBSHIT_LUA_CLASS;
     70     public:
     71       Type(const Type&) = delete;
     72       void operator=(const Type&) = delete;
     73       ~Type() = delete;
     74 
     75       LIBSHIT_NOLUA mutable std::atomic<size_t> refcount;
     76       LIBSHIT_LUAGEN(get: true) LIBSHIT_LUAGEN(get: true, name: "__len")
     77         size_t item_count;
     78       LIBSHIT_LUAGEN(get: true) size_t byte_size;
     79       struct Item
     80       {
     81         size_t idx;
     82         size_t size;
     83         size_t offset;
     84       };
     85       LIBSHIT_NOLUA Item items[1];
     86     };
     87     static_assert(std::is_standard_layout_v<Type>);
     88     using TypePtr = boost::intrusive_ptr<const Type>;
     89 
     90     friend void intrusive_ptr_add_ref(const Type* t)
     91     { t->refcount.fetch_add(1, std::memory_order_relaxed); }
     92 
     93     friend void intrusive_ptr_release(const Type* t)
     94     {
     95       if (t->refcount.fetch_sub(1, std::memory_order_acq_rel) == 1)
     96         ::operator delete(const_cast<Type*>(t));
     97     }
     98 
     99     class TypeBuilder final : public Libshit::Lua::ValueObject
    100     {
    101       LIBSHIT_LUA_CLASS;
    102     public:
    103       TypeBuilder() = default; // force lua ctor
    104 
    105       LIBSHIT_NOLUA auto& GetDesc() { return desc; }
    106       LIBSHIT_NOLUA const auto& GetDesc() const { return desc; }
    107 
    108       void Reserve(size_t size) { desc.reserve(size); }
    109 
    110       template <typename T>
    111       LIBSHIT_NOLUA void Add(size_t size = sizeof(T))
    112       { desc.emplace_back(IndexOfV<T, Args...>, size); }
    113 
    114       LIBSHIT_NOLUA void Add(size_t i, size_t size)
    115       {
    116         LIBSHIT_ASSERT_MSG(i < sizeof...(Args), "index out of range");
    117         desc.push_back({i, size});
    118       }
    119 
    120       TypePtr Build() const
    121       {
    122         auto ptr = operator new(
    123           sizeof(Type) + (desc.size() - 1) * sizeof(typename Type::Item));
    124         boost::intrusive_ptr<Type> ret{static_cast<Type*>(ptr), false};
    125         ret->refcount.store(1, std::memory_order_relaxed);
    126         ret->item_count = desc.size();
    127 
    128         size_t offs = 0;
    129         for (size_t i = 0; i < desc.size(); ++i)
    130         {
    131           ret->items[i].idx = desc[i].first;
    132           ret->items[i].size = desc[i].second;
    133           ret->items[i].offset = offs;
    134 
    135           offs += desc[i].second;
    136           auto al = ALIGN_OF[desc[i].first];
    137           offs = (offs + al - 1) / al * al;
    138         }
    139         ret->byte_size = offs;
    140 
    141         return ret;
    142       }
    143 
    144     private:
    145       std::vector<std::pair<size_t, size_t>> desc;
    146     };
    147 
    148     // actual class begin
    149     static boost::intrusive_ptr<DynamicStruct> New(TypePtr type)
    150     {
    151       auto ptr = ::operator new(sizeof(DynamicStruct) + type->byte_size - 1);
    152       try
    153       {
    154         auto obj = new (ptr) DynamicStruct{std::move(type)};
    155         return {obj, false};
    156       }
    157       catch (...)
    158       {
    159         ::operator delete(ptr);
    160         throw;
    161       }
    162     }
    163 
    164     DynamicStruct(const DynamicStruct&) = delete;
    165     void operator=(const DynamicStruct&) = delete;
    166     ~DynamicStruct()
    167     {
    168       if (type)
    169         ForEach(Destroy{});
    170     }
    171 
    172     LIBSHIT_LUAGEN() LIBSHIT_LUAGEN(name: "__len")
    173     size_t GetSize() const noexcept { return type->item_count; }
    174     LIBSHIT_NOLUA size_t GetSize(size_t i) const noexcept
    175     {
    176       LIBSHIT_ASSERT_MSG(i < GetSize(), "index out of range");
    177       return type->items[i].size;
    178     }
    179     LIBSHIT_NOLUA size_t GetTypeIndex(size_t i) const noexcept
    180     {
    181       LIBSHIT_ASSERT_MSG(i < GetSize(), "index out of range");
    182       return type->items[i].idx;
    183     }
    184 
    185     template <typename T>
    186     LIBSHIT_NOLUA bool Is(size_t i) const noexcept
    187     {
    188       return GetTypeIndex(i) == GetIndexFromType<T>();
    189     }
    190 
    191     const TypePtr& GetType() const noexcept { return type; }
    192     LIBSHIT_NOLUA void* GetData() noexcept { return data; }
    193     LIBSHIT_NOLUA const void* GetData() const noexcept { return data; }
    194 
    195     LIBSHIT_NOLUA void* GetData(size_t i) noexcept
    196     {
    197       LIBSHIT_ASSERT_MSG(i <= GetSize(), "index out of range");
    198       return &data[type->items[i].offset];
    199     }
    200     LIBSHIT_NOLUA const void* GetData(size_t i) const noexcept
    201     {
    202       LIBSHIT_ASSERT_MSG(i <= GetSize(), "index out of range");
    203       return &data[type->items[i].offset];
    204     }
    205 
    206     template <typename T>
    207     LIBSHIT_NOLUA T& Get(size_t i) noexcept
    208     {
    209       LIBSHIT_ASSERT_MSG(Is<T>(i), "specified item is not T");
    210       return *reinterpret_cast<T*>(data + type->items[i].offset);
    211     }
    212 
    213     template <typename T>
    214     LIBSHIT_NOLUA const T& Get(size_t i) const noexcept
    215     {
    216       LIBSHIT_ASSERT_MSG(Is<T>(i), "specified item is not T");
    217       return *reinterpret_cast<const T*>(data + type->items[i].offset);
    218     }
    219 
    220     template <typename Ret = void, typename... FunArgs>
    221     LIBSHIT_NOLUA Ret Visit(size_t i, FunArgs&&... f)
    222     { return VisitHlp<Ret, 0, Args...>(this, i, std::forward<FunArgs>(f)...); }
    223 
    224     template <typename... FunArgs>
    225     LIBSHIT_NOLUA void ForEach(FunArgs&&... f)
    226     {
    227       for (size_t i = 0; i < type->item_count; ++i)
    228         Visit(i, std::forward<FunArgs>(f)...);
    229     }
    230 
    231     // const version
    232     template <typename Ret = void, typename... FunArgs>
    233     LIBSHIT_NOLUA Ret Visit(size_t i, FunArgs&&... f) const
    234     { return VisitHlp<Ret, 0, Args...>(this, i, std::forward<FunArgs>(f)...); }
    235 
    236     template <typename... FunArgs>
    237     LIBSHIT_NOLUA void ForEach(FunArgs&&... args) const
    238     {
    239       for (size_t i = 0; i < type->item_count; ++i)
    240         Visit(i, std::forward<FunArgs>(args)...);
    241     }
    242 
    243   private:
    244     DynamicStruct(TypePtr type) : type{std::move(type)}
    245     {
    246       size_t i = 0;
    247       try
    248       {
    249         for (i = 0; i < this->type->item_count; ++i)
    250           Visit(i, Make{});
    251       }
    252       catch (...)
    253       {
    254         while (i > 0) Visit(--i, Destroy{});
    255         throw;
    256       }
    257     }
    258 
    259     struct Make
    260     {
    261       template <typename T>
    262       void operator()(T& x, size_t)
    263       { new (&x) T; }
    264     };
    265 
    266     struct Destroy
    267     {
    268       template <typename T>
    269       void operator()(T& x, size_t)
    270       { x.~T(); }
    271     };
    272 
    273     TypePtr type;
    274     mutable std::atomic<size_t> refcount{1};
    275     char data[1];
    276 
    277     friend void intrusive_ptr_add_ref(const DynamicStruct* t)
    278     { t->refcount.fetch_add(1, std::memory_order_relaxed); }
    279 
    280     friend void intrusive_ptr_release(const DynamicStruct* t)
    281     {
    282       if (t->refcount.fetch_sub(1, std::memory_order_acq_rel) == 1)
    283       {
    284         t->~DynamicStruct();
    285         ::operator delete(const_cast<DynamicStruct*>(t));
    286       }
    287     }
    288   };
    289 
    290   template <typename... Args>
    291   constexpr const size_t DynamicStruct<Args...>::SIZE_OF[];
    292 
    293   template <typename... Args>
    294   constexpr const size_t DynamicStruct<Args...>::ALIGN_OF[];
    295 
    296 }
    297 #endif