mirror of
				https://github.com/slint-ui/slint.git
				synced 2025-10-31 20:08:35 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			186 lines
		
	
	
	
		
			4.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			186 lines
		
	
	
	
		
			4.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright © SixtyFPS GmbH <info@slint-ui.com>
 | |
| // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
 | |
| 
 | |
| #pragma once
 | |
| 
 | |
| #include <cstddef>
 | |
| #include <new>
 | |
| #include <algorithm>
 | |
| #include <optional>
 | |
| #include <atomic>
 | |
| 
 | |
| namespace vtable {
 | |
| 
 | |
| template<typename T>
 | |
| struct VRefMut
 | |
| {
 | |
|     const T *vtable;
 | |
|     void *instance;
 | |
| };
 | |
| 
 | |
| struct Layout
 | |
| {
 | |
|     std::size_t size;
 | |
|     std::size_t align;
 | |
| };
 | |
| 
 | |
| // For the C++'s purpose, they are all the same
 | |
| template<typename T>
 | |
| using VRef = VRefMut<T>;
 | |
| 
 | |
| template<typename T>
 | |
| using Pin = T;
 | |
| 
 | |
| template<typename T>
 | |
| struct VBox
 | |
| {
 | |
|     const T *vtable = nullptr;
 | |
|     void *instance = nullptr;
 | |
|     VBox(const VBox &) = delete;
 | |
|     VBox() = default;
 | |
|     VBox &operator=(const VBox &) = delete;
 | |
|     ~VBox()
 | |
|     {
 | |
|         if (vtable && instance) {
 | |
|             vtable->drop({ vtable, instance });
 | |
|         }
 | |
|     }
 | |
| };
 | |
| 
 | |
| struct AllowPin;
 | |
| 
 | |
| template<typename Base, typename T, typename Flag = void>
 | |
| struct VOffset
 | |
| {
 | |
|     const T *vtable;
 | |
|     std::uintptr_t offset;
 | |
| };
 | |
| 
 | |
| template<typename VTable, typename X>
 | |
| struct VRcInner
 | |
| {
 | |
|     template<typename VTable_, typename X_>
 | |
|     friend class VRc;
 | |
|     template<typename VTable_, typename X_>
 | |
|     friend class VWeak;
 | |
| 
 | |
| private:
 | |
|     VRcInner() : layout {} { }
 | |
|     const VTable *vtable = &X::static_vtable;
 | |
|     std::atomic<int> strong_ref = 1;
 | |
|     std::atomic<int> weak_ref = 1;
 | |
|     std::uint16_t data_offset = offsetof(VRcInner, data);
 | |
|     union {
 | |
|         X data;
 | |
|         Layout layout;
 | |
|     };
 | |
| 
 | |
|     void *data_ptr() { return reinterpret_cast<char *>(this) + data_offset; }
 | |
|     ~VRcInner() = delete;
 | |
| };
 | |
| 
 | |
| struct Dyn
 | |
| {
 | |
| };
 | |
| 
 | |
| template<typename VTable, typename X = Dyn>
 | |
| class VRc
 | |
| {
 | |
|     VRcInner<VTable, X> *inner;
 | |
|     VRc(VRcInner<VTable, X> *inner) : inner(inner) { }
 | |
|     template<typename VTable_, typename X_>
 | |
|     friend class VWeak;
 | |
| 
 | |
| public:
 | |
|     ~VRc()
 | |
|     {
 | |
|         if (!--inner->strong_ref) {
 | |
|             Layout layout = inner->vtable->drop_in_place({ inner->vtable, &inner->data });
 | |
|             layout.size += inner->data_offset;
 | |
|             layout.align = std::max<size_t>(layout.align, alignof(VRcInner<VTable, Dyn>));
 | |
|             inner->layout = layout;
 | |
|             if (!--inner->weak_ref) {
 | |
|                 inner->vtable->dealloc(inner->vtable, reinterpret_cast<uint8_t *>(inner), layout);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     VRc(const VRc &other) : inner(other.inner) { inner->strong_ref++; }
 | |
|     VRc &operator=(const VRc &other)
 | |
|     {
 | |
|         if (inner == other.inner)
 | |
|             return *this;
 | |
|         this->~VRc();
 | |
|         new (this) VRc(other);
 | |
|         return *this;
 | |
|     }
 | |
|     /// Construct a new VRc holding an X.
 | |
|     ///
 | |
|     /// The type X must have a static member `static_vtable` of type VTable
 | |
|     template<typename... Args>
 | |
|     static VRc make(Args... args)
 | |
|     {
 | |
|         auto mem = ::operator new(sizeof(VRcInner<VTable, X>),
 | |
|                                   static_cast<std::align_val_t>(alignof(VRcInner<VTable, X>)));
 | |
|         auto inner = new (mem) VRcInner<VTable, X>;
 | |
|         new (&inner->data) X(args...);
 | |
|         return VRc(inner);
 | |
|     }
 | |
| 
 | |
|     const X *operator->() const { return &inner->data; }
 | |
|     const X &operator*() const { return inner->data; }
 | |
|     X *operator->() { return &inner->data; }
 | |
|     X &operator*() { return inner->data; }
 | |
| 
 | |
|     VRc<VTable, Dyn> into_dyn() const { return *reinterpret_cast<const VRc<VTable, Dyn> *>(this); }
 | |
| 
 | |
|     VRef<VTable> borrow() const { return { inner->vtable, inner->data_ptr() }; }
 | |
| 
 | |
|     friend bool operator==(const VRc &a, const VRc &b) { return a.inner == b.inner; }
 | |
|     friend bool operator!=(const VRc &a, const VRc &b) { return a.inner != b.inner; }
 | |
|     const VTable *vtable() const { return inner->vtable; }
 | |
| };
 | |
| 
 | |
| template<typename VTable, typename X = Dyn>
 | |
| class VWeak
 | |
| {
 | |
|     VRcInner<VTable, X> *inner = nullptr;
 | |
| 
 | |
| public:
 | |
|     VWeak() = default;
 | |
|     ~VWeak()
 | |
|     {
 | |
|         if (inner && !--inner->weak_ref) {
 | |
|             inner->vtable->dealloc(inner->vtable, reinterpret_cast<uint8_t *>(inner),
 | |
|                                    inner->layout);
 | |
|         }
 | |
|     }
 | |
|     VWeak(const VWeak &other) : inner(other.inner) { inner && inner->weak_ref++; }
 | |
|     VWeak(const VRc<VTable, X> &other) : inner(other.inner) { inner && inner->weak_ref++; }
 | |
|     VWeak &operator=(const VWeak &other)
 | |
|     {
 | |
|         if (inner == other.inner)
 | |
|             return *this;
 | |
|         this->~VWeak();
 | |
|         new (this) VWeak(other);
 | |
|         return *this;
 | |
|     }
 | |
| 
 | |
|     std::optional<VRc<VTable, X>> lock() const
 | |
|     {
 | |
|         if (!inner || inner->strong_ref == 0)
 | |
|             return {};
 | |
|         inner->strong_ref++;
 | |
|         return { VRc<VTable, X>(inner) };
 | |
|     }
 | |
| 
 | |
|     VWeak<VTable, Dyn> into_dyn() const
 | |
|     {
 | |
|         return *reinterpret_cast<const VWeak<VTable, Dyn> *>(this);
 | |
|     }
 | |
| 
 | |
|     friend bool operator==(const VWeak &a, const VWeak &b) { return a.inner == b.inner; }
 | |
|     friend bool operator!=(const VWeak &a, const VWeak &b) { return a.inner != b.inner; }
 | |
|     const VTable *vtable() const { return inner ? inner->vtable : nullptr; }
 | |
| };
 | |
| 
 | |
| } // namespace vtable
 | 
