namespace ffi_types { template struct CBox; /// C++ counterpart for Rust `Option>` managed by C++ ownership model. // /// The API design is similar to `std::unique_ptr`, though construction and destruction /// must be executed in Rust side. /// /// @warning The underlying memory must be allocated from Rust side. template struct OptionBox { using element_type = T; using pointer = T*; T* _ptr; // constructors OptionBox() = delete; OptionBox(OptionBox&) = delete; OptionBox(OptionBox&& b) noexcept : _ptr(b.release()) {} explicit OptionBox(std::nullptr_t) noexcept : _ptr(nullptr) {} explicit OptionBox(pointer p) noexcept : _ptr(p) {} // destructor and helper ~OptionBox() noexcept { if (this->get()) { this->_drop(); } } void _drop() noexcept; // assignment OptionBox& operator=(OptionBox&& b) noexcept { this->_ptr = b.release(); return *this; } /// Converts to `CBox` by moving the value. /// The value of `this` will be invalidated to null. CBox into() noexcept; /// Borrow as `CBox`. /// This is safe because CBox is a reference. CBox& as_c() noexcept { return *reinterpret_cast*>(this); } /// Borrow as `CBox`. /// This is safe because CBox is a reference. const CBox& as_c() const noexcept { return *reinterpret_cast*>(this); } // observers typename std::add_lvalue_reference::type operator*() const { return *get(); } pointer operator->() const { return get(); } pointer get() const { return this->_ptr; } explicit operator bool() const noexcept { return get() != nullptr; } // operators bool operator==(std::nullptr_t) const { return this->get() == nullptr; } bool operator!=(std::nullptr_t) const { return this->get() != nullptr; } // modifiers T* release() noexcept { auto* ptr = this->_ptr; this->_ptr = 0; return ptr; } void reset(pointer p) noexcept { if (this->get()) { this->_drop(); } this->_ptr = p; } }; static_assert(sizeof(OptionBox) == sizeof(int*)); static_assert(std::is_standard_layout>::value); /// C++ counterpart for Rust `Box` as alias of `OptionBox`. /// Since `Box` can be moved or released, a value of this type is still not guaranteed to be non-null. /// /// @warning The underlying memory must be allocated from Rust side. template struct Box : public OptionBox { explicit Box(T* p) noexcept : OptionBox(p) { assert(p != nullptr); } }; /// C++ wrapper for Rust `Box` with C ABI compatible layout. /// /// @warning This type does *NOT* implement a safe destructor. /// To avoid leak, see general note about C-prefixed types at the top of module documentation. template struct [[nodiscard]] CBox { CBox() = _COPY_DELETE; CBox(const CBox&) = _COPY_DELETE; CBox& operator=(const CBox& b) noexcept = _COPY_DELETE; T* _ptr; #if _MSC_VER /// Creates a CBox from a `OptionBox` by moving the value. /// This is a workaround for MSVC constructor limitation. static CBox from(OptionBox&& box) noexcept { CBox cbox; cbox._ptr = box.release(); return cbox; } #else CBox(CBox&&) = default; CBox& operator=(CBox&& b) noexcept = default; CBox(OptionBox&& box) noexcept : _ptr(box.release()) {} /// Creates a CBox from a `OptionBox` by moving the value. /// This is a workaround for MSVC constructor limitation. static CBox from(OptionBox&& box) noexcept { return CBox(std::move(box)); } #endif /// Conversion operator to check if the box is not null. explicit operator bool() const noexcept { return _ptr != nullptr; } /// Equality operator with nullptr. bool operator==(std::nullptr_t) noexcept { return _ptr == nullptr; } /// Conversion operator to `OptionBox`. /// /// Though the creation of `CBox` is always expected to be non-null by Rust Side, /// `CBox` itself still can be a null due to move. /// /// @note This conversion *MUST* be called unless the value is going to be passed to Rust side. OptionBox operator()() noexcept { auto box = OptionBox(nullptr); box.reset(this->_ptr); this->_ptr = nullptr; return box; } T* release() noexcept { auto* ptr = this->_ptr; this->_ptr = nullptr; return ptr; } }; static_assert(sizeof(CBox) == sizeof(int*)); static_assert(std::is_trivial>::value); static_assert(std::is_standard_layout>::value); /// C++ wrapper for Rust `Option>` as alias of `CBox`. /// /// This is a cbindgen helper to pass `Option>` to C++ side. /// /// @see CBox template using COptionBox = CBox; template inline CBox OptionBox::into() noexcept { return CBox::from(std::move(*this)); } // specializations to prohibit void box drop template struct CBox; template <> inline void OptionBox::_drop() noexcept { assert("void box doesn't support drop" && false); } } // namespace ffi_types