#pragma once #include #include #include #include #include #if __cpp_lib_ranges #include #endif #if __cpp_lib_string_view #include #endif #if __cpp_lib_span #include #endif #include //! @file rust_types.hh //! @brief This file contains matching C++ types for `ffi_types` crate. //! //! The types can be used for either bindgen, cbindgen or hand-crafted C/C++ code. //! The types without C-prefixes are ownership-aware types compatible with Rust types. //! The types with C-prefixes are C ABI compatible layout of its paired owned types. //! C-prefixes types are very fragile! Please read the following warning to use it safe. //! //! @warning All types prefixed with `C` in this file are C ABI compatible layout of owned types without `C` prefix. //! To avoid memory leak, you must convert it to owned type or pass it to Rust side. //! You *MUST* run one of the following to avoid memory leak: //! //! 1. Calling operator() to convert to owned type. //! 1.1 When used in bindgen generated functions' arguments. //! ```c++ //! void foo(CBoxedSlice ints) { //! auto owned = ints(); // owned is managed as BoxedSlice //! } //! ``` //! //! 1.2 When used in cbindgen generated functions' return value. //! ```rust //! fn foo() -> CBoxedSlice { ... } //! ``` //! ```c++ //! auto owned = foo()(); // owned is managed as BoxedSlice //! ``` //! //! 1.3 Dropping in C++ side. //! The owned value in either 1.1 or 1.2 must be returned to Rust side or dropped. //! See 2.1 to return value. //! Implement template specialization for `_drop()` for the related type to drop it in C++ side. //! (e.g. BoxedSlice::_drop() for BoxedSlice) //! The _drop function must call `std::mem::drop()` from rust side. //! //! 2. Passing as an argument of Rust function. //! 2.1 When used in cbindgen generated functions' arguments. //! ```rust //! fn foo() -> CBoxedSlice { ... } // See 1.2 for this case //! fn bar(ints: CBoxedSlice) { ... } //! ``` //! ```c++ //! auto owned = foo()(); // owned is managed as BoxedSlice //! bar(owned); // BoxedSlice can be passed to CBoxedSlice //! ``` //! //! 3. Returning from C++ function. Note that this case will be very unlikely to happen because Box must be //! created from Rust side. //! //! @note allocate or deallocate a boxed type in C++ side is not allowed. namespace ffi_types { /// Alias for Rust `std::usize`. using usize = uintptr_t; /// Alias for Rust `[T; N]`. Interpreted as std::array in C++ side. template using Array = std::array; } // namespace ffi_types #if _MSC_VER #define _COPY_DELETE default #else #define _COPY_DELETE delete #endif 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 namespace ffi_types { #define EMPTY_SLICE_BEGIN(T) reinterpret_cast(1) template T* _wrap_null(T* ptr) { return ptr ? ptr : reinterpret_cast(1); } // C++ std::ranges compatibility layer until C++17. #if __cpp_lib_ranges namespace ranges { using std::ranges::data; using std::ranges::size; } // namespace ranges #define SAFE_R R&& #else namespace ranges { template auto data(C& x) noexcept { auto begin = x.begin(); using iterator_type = decltype(begin); using pointer_type = decltype(&*begin); if constexpr (std::is_same_v) { // same type return begin; } else if constexpr (sizeof(iterator_type) == sizeof(uintptr_t)) { // likely to be a pointer return *reinterpret_cast(&begin); } else { if (begin != x.end()) { return &*begin; } else { return static_cast(nullptr); } } } template size_t size(C& x) noexcept { return x.end() - x.begin(); } } // namespace ranges // R&& is not safe without actual ranges #define SAFE_R const R& #endif struct CharStrRef; struct StrRef; struct CStrRef; struct CByteSliceRef; class BoxedStr; template struct CMutSliceRef; template struct CSliceRef; template struct CBoxedSlice; /// A minimal range type of slices to be compatible with internal ranges. template struct _SliceRange { T* _data; usize _size; auto* begin() const noexcept { return this->_data; } auto* end() const noexcept { return this->_data + _size; } }; /// Common C++ STL-like interface for &[T] and &str. /// @see std::span template typename I> struct _SliceInterface { // constants and types using element_type = T; using value_type = typename std::remove_cv_t; using size_type = uintptr_t; using difference_type = intptr_t; using pointer = element_type*; using const_pointer = const element_type*; using reference = element_type&; using const_reference = const element_type&; #if __cpp_lib_span using iterator = typename std::span::iterator; #else using iterator = pointer; #endif using reverse_iterator = std::reverse_iterator; // observers size_type size() const noexcept { return static_cast*>(this)->_size; } size_type size_bytes() const noexcept { return this->size() * sizeof(element_type); } bool empty() const noexcept { return this->size() == 0; } // element access reference operator[](size_type idx) const noexcept { assert(idx < this->size()); return this->data()[idx]; } reference front() const noexcept { return this->data()[0]; } reference back() const noexcept { return this->data()[size() - 1]; } pointer data() const noexcept { return static_cast*>(this)->_data; } // iterator #if __cpp_lib_span iterator begin() const noexcept { return span().begin(); } #else iterator begin() const noexcept { return data(); } #endif iterator end() const noexcept { return begin() + size(); } reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); } reverse_iterator rend() const noexcept { return reverse_iterator(begin()); } _SliceRange get() const noexcept { return {data(), size()}; } #if __cpp_lib_span /// Returns the slice as a `std::span`. std::span span() const noexcept { return std::span(data(), size()); } #endif }; /// C++ counterpart of Rust `&mut [T]` and &[T]`. /// The interface is following `std::span` design. template struct MutSliceRef : public _SliceInterface { T* _data; usize _size; MutSliceRef() noexcept : _data(EMPTY_SLICE_BEGIN(T)), _size(0) {} MutSliceRef(const MutSliceRef&) = default; MutSliceRef(MutSliceRef&&) = default; explicit MutSliceRef(T* head, usize size) noexcept : _data(_wrap_null(head)), _size(size) {} template MutSliceRef(SAFE_R range) noexcept : _data(_wrap_null(ranges::data(range))), _size(static_cast(ranges::size(range))) {} MutSliceRef& operator=(const MutSliceRef&) = default; MutSliceRef& operator=(MutSliceRef&&) = default; CMutSliceRef into() const noexcept; /// Convert &[u8] to C++ string wrapper. /// This is &[u8] specialization as bytes template std::enable_if_t, CharStrRef> as_char_str() const noexcept; /// Convert &[u8] to Rust string wrapper without UTF-8 checking. /// This is &[u8] specialization as bytes /// /// @warning Safety: Only when the data is a valid UTF-8 string. template std::enable_if_t, StrRef> as_str_unchecked() const noexcept; }; static_assert(std::is_trivially_copyable>::value); static_assert(std::is_standard_layout>::value); /// SliceRef is a read-only slice `&[T]` in Rust and a conceptional alias to `MutSliceRef` in C++. /// This is not a hard alias because: /// - template alias doesn't support generic parameter deduction. /// - bindgen resolve template alias to the original type, which we don't supposed to do. template struct SliceRef : public MutSliceRef { SliceRef() noexcept : MutSliceRef(){}; SliceRef(const SliceRef&) = default; SliceRef(SliceRef&&) = default; explicit SliceRef(const T* head, usize size) noexcept : MutSliceRef(head, size) {} template SliceRef(SAFE_R range) noexcept : MutSliceRef(range) {} SliceRef& operator=(const SliceRef&) = default; SliceRef& operator=(SliceRef&&) = default; typename std::conditional, CByteSliceRef, CSliceRef>::type into() const noexcept; template static std::enable_if_t, SliceRef> from_buffer(const B& buffer) noexcept { return SliceRef(reinterpret_cast(&buffer), sizeof(B)); } }; static_assert(std::is_trivially_copyable>::value); static_assert(std::is_standard_layout>::value); /// C++ counterpart of `&[u8]` as alias for `SliceRef`. /// This alias is useful to pass `&[u8]` in cbindgen without configuration and boilerplate. using ByteSliceRef = SliceRef; template struct CMutSliceRef { CMutSliceRef() = _COPY_DELETE; CMutSliceRef(const CMutSliceRef&) = default; CMutSliceRef& operator=(const CMutSliceRef&) = default; #if _MSC_VER /// Creates a `CMutSliceRef` from a `MutSliceRef`. /// This is a workaround for MSVC constructor limitation. static CMutSliceRef from(const MutSliceRef& slice) noexcept { CMutSliceRef s; s._data = slice._data; s._size = slice._size; return s; } #else CMutSliceRef(const MutSliceRef& slice) noexcept { this->_data = slice._data; this->_size = slice._size; } /// Creates a `CMutSliceRef` from a `MutSliceRef`. /// This is a workaround for MSVC constructor limitation. static CMutSliceRef from(const MutSliceRef& slice) noexcept { return CMutSliceRef(slice); } #endif T* _data; usize _size; MutSliceRef operator()() const noexcept { return MutSliceRef(this->_data, this->_size); } }; static_assert(std::is_trivial>::value); static_assert(std::is_standard_layout>::value); template struct CSliceRef { CSliceRef() = _COPY_DELETE; CSliceRef(const CSliceRef&) = default; CSliceRef& operator=(const CSliceRef&) = default; #if _MSC_VER /// Creates a `CSliceRef` from a `SliceRef`. /// This is a workaround for MSVC constructor limitation. static CSliceRef from(const SliceRef& slice) noexcept { CSliceRef s; s._data = slice._data; s._size = slice._size; return s; } #else CSliceRef(const SliceRef& slice) noexcept { this->_data = slice._data; this->_size = slice._size; } /// Creates a `CSliceRef` from a `SliceRef`. /// This is a workaround for MSVC constructor limitation. static CSliceRef from(const SliceRef& slice) noexcept { return CSliceRef(slice); } #endif const T* _data; usize _size; SliceRef operator()() const noexcept { return SliceRef(this->_data, this->_size); } }; static_assert(std::is_trivial>::value); static_assert(std::is_standard_layout>::value); // Alias is not a C type in MSVC, so this line doesn't work. // ```c++ // using CByteSliceRef = CSliceRef; // ``` struct CByteSliceRef { CByteSliceRef() = _COPY_DELETE; CByteSliceRef(const CByteSliceRef&) = default; CByteSliceRef& operator=(const CByteSliceRef&) = default; #if _MSC_VER /// Creates a `CByteSliceRef` from a `ByteSliceRef`. /// This is a workaround for MSVC constructor limitation. static CByteSliceRef from(const ByteSliceRef& slice) noexcept { CByteSliceRef s; s._data = slice._data; s._size = slice._size; return s; } #else CByteSliceRef(const ByteSliceRef& slice) noexcept { this->_data = slice._data; this->_size = slice._size; } /// Creates a `CByteSliceRef` from a `ByteSliceRef`. /// This is a workaround for MSVC constructor limitation. static CByteSliceRef from(const ByteSliceRef& slice) noexcept { return CByteSliceRef(slice); } #endif const uint8_t* _data; usize _size; ByteSliceRef operator()() const noexcept { ByteSliceRef slice; slice._data = this->_data; slice._size = this->_size; return slice; } }; static_assert(std::is_trivial::value); static_assert(std::is_standard_layout::value); /// C++ counterpart of `Box` /// The ownership API is following `std::unique_ptr` design and the slice API is following `std::span` design. template class BoxedSlice : public MutSliceRef { public: BoxedSlice() = delete; BoxedSlice(const BoxedSlice&) = delete; BoxedSlice(BoxedSlice&& s) noexcept : MutSliceRef(s) { s._data = EMPTY_SLICE_BEGIN(T); s._size = 0; } BoxedSlice(std::nullptr_t) noexcept : MutSliceRef() {} ~BoxedSlice() noexcept { if (this->_size > 0) { this->_drop(); } } BoxedSlice& operator=(BoxedSlice&& b) noexcept { this->reset(b.release()); return *this; } void _drop() noexcept; CBoxedSlice into() noexcept; const CBoxedSlice& as_c() const noexcept { return *reinterpret_cast*>(this); } CBoxedSlice& as_c() noexcept { return *reinterpret_cast*>(this); } void reset(_SliceRange s) noexcept { if (this->_size > 0) { this->_drop(); } this->_data = s._data; this->_size = s._size; } _SliceRange release() noexcept { const auto range = this->get(); this->_data = EMPTY_SLICE_BEGIN(T); this->_size = 0; return range; } /// Returns a mutable slice of the boxed slice. auto as_slice() noexcept { return MutSliceRef(this->_data, this->_size); } /// Returns a read-only slice of the boxed slice. auto as_slice() const noexcept { return SliceRef(this->_data, this->_size); } }; static_assert(sizeof(usize) * 2 == sizeof(BoxedSlice)); static_assert(std::is_standard_layout>::value); /// C++ wrapper for a boxed slice 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]] CBoxedSlice { CBoxedSlice() = _COPY_DELETE; CBoxedSlice(const CBoxedSlice&) = _COPY_DELETE; CBoxedSlice& operator=(const CBoxedSlice&) = _COPY_DELETE; #if _MSC_VER /// This is a workaround for MSVC constructor limitation. static CBoxedSlice from(BoxedSlice&& slice) noexcept { auto r = slice.release(); CBoxedSlice s; s._data = r._data; s._size = r._size; return s; } #else CBoxedSlice(CBoxedSlice&&) = default; CBoxedSlice& operator=(CBoxedSlice&&) = default; /// Constructs a `CBoxedSlice` by moving a `BoxedSlice`. CBoxedSlice(BoxedSlice&& slice) noexcept { auto r = slice.release(); this->_data = r._data; this->_size = r._size; } /// This is a workaround for MSVC constructor limitation. static CBoxedSlice from(BoxedSlice&& slice) noexcept { return CBoxedSlice(std::move(slice)); } #endif T* _data; usize _size; /// Conversion operator to `BoxedSlice`. /// /// @note This conversion *MUST* be called unless the value is going to be passed to Rust side. BoxedSlice operator()() noexcept { auto slice = BoxedSlice(nullptr); auto r = this->release(); slice._data = r._data; slice._size = r._size; return slice; } _SliceRange get() const noexcept { return {this->_data, this->_size}; } _SliceRange release() noexcept { const auto range = this->get(); this->_data = EMPTY_SLICE_BEGIN(T); this->_size = 0; return range; } }; static_assert(std::is_trivial>::value); static_assert(std::is_standard_layout>::value); /// C++ unsafe counterpart of Rust `&str`. /// /// Because `StrRef` in C++ side doesn't have any UTF-8 validation checking, /// `CharStrRef` is an alternative of `StrRef` creation in C++ side. /// The value can be converted to either `&[u8]` or `&str` with UTF-8 validation in Rust side. struct CharStrRef { CharStrRef() = _COPY_DELETE; CharStrRef(const CharStrRef&) = default; CharStrRef& operator=(const CharStrRef&) = default; // #if !_MSC_VER CharStrRef(const char* head, usize size) noexcept : _data(_wrap_null(head)), _size(size) {} template CharStrRef(SAFE_R range) noexcept : _data(_wrap_null(ranges::data(range))), _size(ranges::size(range)) {} CharStrRef(std::nullptr_t) noexcept : _data(EMPTY_SLICE_BEGIN(const char)), _size(0) {} // #endif const char* _data; usize _size; // constants and types using element_type = const char; using value_type = typename std::remove_cv_t; using size_type = uintptr_t; using difference_type = intptr_t; using pointer = element_type*; using const_pointer = const element_type*; using reference = element_type&; using const_reference = const element_type&; using iterator = typename std::string_view::iterator; using reverse_iterator = std::reverse_iterator; // observers size_type size() const noexcept { return this->_size; } size_type size_bytes() const noexcept { return this->size() * sizeof(element_type); } bool empty() const noexcept { return this->size() == 0; } // element access reference operator[](size_type idx) const noexcept { assert(idx < this->size()); return this->data()[idx]; } reference front() const noexcept { return this->data()[0]; } reference back() const noexcept { return this->data()[size() - 1]; } pointer data() const noexcept { return this->_data; } // iterator iterator begin() const noexcept { return view().begin(); } iterator end() const noexcept { return begin() + size(); } reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); } reverse_iterator rend() const noexcept { return reverse_iterator(begin()); } _SliceRange get() const noexcept { return {data(), size()}; } #if __cpp_lib_span /// Returns the slice as a `std::span`. std::span span() const noexcept { return std::span(data(), size()); } #endif StrRef as_str_unchecked() const noexcept; #if __cpp_lib_string_view /// Constructs a `CharStrRef` from a null-terminated string. CharStrRef(const char* s) noexcept : CharStrRef(std::string_view(s)) {} /// Returns a `std::string_view` of the string. std::string_view view() const noexcept { return std::string_view(this->data(), this->size()); } /// Conversion operator to `std::string_view`. operator std::string_view() const noexcept { return this->view(); } /// Create a `std::string` by copying data. /// @return The newly created `std::string`. std::string to_string() const noexcept { return std::string(this->view()); } #endif }; static_assert(sizeof(SliceRef) == sizeof(CharStrRef)); static_assert(std::is_trivial::value); static_assert(std::is_standard_layout::value); /// C++ counterpart of Rust `&str`. /// /// Because `StrRef` in C++ side doesn't have any UTF-8 validation checking, /// this type is highly recommended to be created from Rust side. /// /// To create `StrRef` in C++ side, use `ByteSliceRef::as_str_unchecked()` or `CharStrRef::as_str_unchecked()` /// at your own risk. struct StrRef : public CharStrRef { StrRef() = delete; StrRef(const StrRef&) = default; StrRef(const BoxedStr&) noexcept; StrRef(std::nullptr_t) noexcept : CharStrRef(EMPTY_SLICE_BEGIN(const char), 0) {} StrRef& operator=(const StrRef&) = default; }; static_assert(std::is_trivially_copyable::value); static_assert(std::is_standard_layout::value); /// C++ wrapper for a str with C ABI compatible layout. struct CStrRef { CStrRef() = _COPY_DELETE; CStrRef(const CStrRef&) = default; CStrRef& operator=(const CStrRef&) = default; #if !_MSC_VER CStrRef(const StrRef& slice) noexcept { this->_data = slice.data(); this->_size = slice.size(); } #endif const char* _data; usize _size; StrRef operator()() const noexcept { auto s = StrRef(nullptr); s._data = this->_data; s._size = this->_size; return s; } }; static_assert(std::is_trivial::value); static_assert(std::is_standard_layout::value); /// C++ counterpart of Rust `Box`. class BoxedStr : public StrRef { public: BoxedStr() = delete; BoxedStr(BoxedStr&) = delete; BoxedStr(BoxedStr&& s) noexcept : StrRef(s) { s._data = EMPTY_SLICE_BEGIN(const char); s._size = 0; } BoxedStr(std::nullptr_t) noexcept : StrRef(nullptr) {} ~BoxedStr() noexcept { if (this->_size > 0) { this->_drop(); } } BoxedStr& operator=(BoxedStr&& b) noexcept { this->reset(b.release()); return *this; } void _drop() noexcept; void reset(_SliceRange s) noexcept { if (this->_size > 0) { this->_drop(); } this->_data = s._data; this->_size = s._size; } _SliceRange release() noexcept { const auto range = this->get(); this->_data = EMPTY_SLICE_BEGIN(const char); this->_size = 0; return range; } StrRef as_str() const noexcept { return this->as_str_unchecked(); } }; static_assert(sizeof(StrRef) == sizeof(BoxedStr)); static_assert(std::is_standard_layout::value); /// 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. struct [[nodiscard]] CBoxedStr { CBoxedStr() = _COPY_DELETE; CBoxedStr(const CBoxedStr&) = _COPY_DELETE; CBoxedStr& operator=(const CBoxedStr&) = _COPY_DELETE; const char* _data; usize _size; #if _MSC_VER /// This is a workaround for MSVC constructor limitation. static CBoxedStr from(BoxedStr&& str) noexcept { auto r = str.release(); CBoxedStr s; s._data = r._data; s._size = r._size; return s; } #else CBoxedStr(CBoxedStr&&) = default; CBoxedStr& operator=(CBoxedStr&&) = default; CBoxedStr(BoxedStr&& str) noexcept { auto r = str.release(); this->_data = r._data; this->_size = r._size; } /// This is a workaround for MSVC constructor limitation. static CBoxedStr from(BoxedStr&& slice) noexcept { return CBoxedStr(std::move(slice)); } #endif BoxedStr operator()() noexcept { auto slice = BoxedStr(nullptr); auto r = this->release(); slice._data = r._data; slice._size = r._size; return slice; } _SliceRange release() noexcept { const auto range = _SliceRange{_data, _size}; this->_data = EMPTY_SLICE_BEGIN(const char); this->_size = 0; return range; } }; static_assert(std::is_trivial::value); static_assert(std::is_standard_layout::value); template inline CMutSliceRef MutSliceRef::into() const noexcept { return CMutSliceRef::from(*this); } template inline typename std::conditional, CByteSliceRef, CSliceRef>::type SliceRef::into() const noexcept { if constexpr (std::is_same_v) { return CByteSliceRef::from(*this); } else { return CSliceRef::from(*this); } } template inline CBoxedSlice BoxedSlice::into() noexcept { return CBoxedSlice::from(std::move(*this)); } inline StrRef::StrRef(const BoxedStr& owned) noexcept : CharStrRef(owned._data, owned._size) {} inline StrRef CharStrRef::as_str_unchecked() const noexcept { return *reinterpret_cast(this); } template <> template <> inline CharStrRef MutSliceRef::as_char_str() const noexcept { return CharStrRef(reinterpret_cast(this->_data), this->_size); } template <> template <> inline StrRef MutSliceRef::as_str_unchecked() const noexcept { return as_char_str().as_str_unchecked(); } #undef SAFE_R #undef EMPTY_SLICE_BEGIN } // namespace ffi_types #pragma once //! This header is intended to be included in rust_types.hh file. namespace ffi_types { extern "C" { void _rust_ffi_boxed_str_drop(ffi_types::CBoxedStr _string); void _rust_ffi_boxed_bytes_drop(ffi_types::CBoxedSlice _slice); } // extern "C" } // namespace ffi_types namespace ffi_types { inline void BoxedStr::_drop() noexcept { ffi_types::_rust_ffi_boxed_str_drop(CBoxedStr::from(std::move(*this))); } template <> inline void BoxedSlice::_drop() noexcept { ffi_types::_rust_ffi_boxed_bytes_drop(CBoxedSlice::from(std::move(*this))); } } // namespace ffi_types #undef _COPY_DELETE namespace rust { using namespace ffi_types; }