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