#ifndef GI_BOXED_HPP #define GI_BOXED_HPP #include #include #include namespace gi { namespace repository { // specialize to enable copyable boxed wrapper // ideally only used in case where the copy is (equivalently); // * cheap // * does not change the underlying pointer // * refcount based // * has no semantics effects // (e.g. GstMiniObject refcount affects writable) template struct enable_boxed_copy : #if GI_ENABLE_BOXED_COPY_ALL public std::true_type #else public std::false_type #endif {}; // should be used within proper namespace #define GI_ENABLE_BOXED_COPY(CType) \ template<> \ struct enable_boxed_copy : public std::true_type \ {}; } // namespace repository namespace detail { // class tags class Boxed {}; class CBoxed : public Boxed {}; class GBoxed : public Boxed {}; template class SharedWrapper { public: typedef SharedWrapper self; protected: CType *data_ = nullptr; public: CType *gobj_() { return data_; } const CType *gobj_() const { return data_; } explicit operator bool() const { return (bool)data_; } bool operator==(const SharedWrapper &other) const { return data_ == other.data_; } bool operator==(std::nullptr_t o) const { return data_ == o; } bool operator!=(const SharedWrapper &other) const { return data_ != other.data_; } bool operator!=(std::nullptr_t o) const { return data_ != o; } CType *release_() { auto tmp = this->data_; this->data_ = nullptr; return tmp; } }; template struct GBoxedFuncs { static CType *copy(const void *data) { return (CType *)g_boxed_copy(CppType::get_type_(), data); } static void free(void *data) { g_boxed_free(CppType::get_type_(), data); } }; struct CBoxedFuncsBase { static void free(void *data) { g_free(data); } }; template struct CBoxedFuncs : CBoxedFuncsBase { static CType *copy(const void *data) { #if GLIB_CHECK_VERSION(2, 68, 0) return (CType *)g_memdup2(data, sizeof(CType)); #else return (CType *)g_memdup(data, sizeof(CType)); #endif } }; template class BoxedWrapper : public SharedWrapper, public TagType { typedef BoxedWrapper self; protected: static void _deleter(CType *obj, ...) { if (obj) Funcs::free(obj); } static CType *_copy(const CType *obj) { return obj ? Funcs::copy(obj) : nullptr; } public: typedef CType BaseObjectType; BoxedWrapper(CType *obj = nullptr) noexcept { this->data_ = obj; } CType *gobj_copy_() const { return _copy(this->gobj_()); } // resulting casted type determines ownership template static Cpp wrap(const typename Cpp::BaseObjectType *obj) { static_assert(sizeof(Cpp) == sizeof(self), "type wrap not supported"); static_assert(std::is_base_of::value, "type wrap not supported"); BoxedWrapper w(const_cast(obj)); return std::move(*static_cast(&w)); } }; // in templates below, Base should be a subclass of BoxedWrapper // so we re-use the members it provides, as well as the wrap template // to avoid ambiguous reference to the latter // (if BoxedWrapper were inherited from again) // the nullptr_t constructor (indirectly) supports `= nullptr` (assignment) template using GBoxedWrapperBase = BoxedWrapper, GBoxed>; // assuming Base has suitable members such as above BoxedWrapper template class MoveWrapper : public Base { typedef Base super; public: using super::super; ~MoveWrapper() { this->_deleter(this->data_, static_cast(this)); } MoveWrapper(const MoveWrapper &) = delete; MoveWrapper &operator=(const MoveWrapper &) = delete; MoveWrapper(MoveWrapper &&o) noexcept { *this = std::move(o); } MoveWrapper &operator=(MoveWrapper &&o) noexcept { if (this != &o) { this->_deleter(this->data_, static_cast(this)); (Base &)(*this) = std::move(o); o.data_ = nullptr; } return *this; } }; template class CopyWrapper : public MoveWrapper { typedef MoveWrapper super; public: using super::super; CopyWrapper(const CopyWrapper &o) noexcept : super() { this->data_ = this->_copy(o.data_); } CopyWrapper &operator=(const CopyWrapper &o) noexcept { if (this != &o) { this->_deleter(this->data_, static_cast(this)); if (sizeof(Base) > sizeof(this->data_)) (Base &)(*this) = std::move(o); this->data_ = this->_copy(o.data_); } return *this; } CopyWrapper(CopyWrapper &&o) noexcept = default; CopyWrapper &operator=(CopyWrapper &&o) noexcept = default; // also accept from corresponding Reference (also based on Base) CopyWrapper(const Base &o) noexcept : super() { this->data_ = this->_copy(((CopyWrapper &)o).data_); } CopyWrapper(Base &&o) noexcept : super() { (super &)(*this) = std::move((super &)o); } }; template using SelectWrapper = typename std::conditional::value, CopyWrapper, MoveWrapper>::type; // basis for registered boxed types template, typename RefType = void> class GBoxedWrapper : public SelectWrapper { typedef SelectWrapper super; public: typedef RefType ReferenceType; using super::super; GBoxedWrapper(std::nullptr_t = nullptr) {} void allocate_() { if (this->data_) return; // make sure we match boxed allocation with boxed free // still guessing here that all-0 makes for a decent init :-( CType tmp; memset(&tmp, 0, sizeof(tmp)); this->data_ = (CType *)g_boxed_copy(this->get_type_(), &tmp); } // essentially g_boxed_copy CppType copy_() const { return super::template wrap(this->gobj_copy_()); } }; template using CBoxedWrapperBase = BoxedWrapper, CBoxed>; // basis for non-registered plain C boxed type template, typename RefType = void> class CBoxedWrapper : public MoveWrapper { typedef Base super; public: typedef RefType ReferenceType; CBoxedWrapper(std::nullptr_t = nullptr) {} void allocate_() { if (this->data_) return; this->data_ = g_new0(CType, 1); } static CppType new_() { return super::template wrap(g_new0(CType, 1)); } }; // allocate helper; // dispatch to method if available template struct allocator : public std::false_type { static void allocate(T &) {} }; template struct allocator : public std::true_type { static void allocate(T &v) { v.allocate_(); } }; template void allocate(T &v) { allocator::allocate(v); } // basis for ref-holding to registered box type template class GBoxedRefWrapper : public Base { typedef Base super; public: typedef CppType BoxType; GBoxedRefWrapper(std::nullptr_t = nullptr) {} // construct from owning version, but not the other way around // (which requires an explicit copy) GBoxedRefWrapper(const CppType &other) { (super &)(*this) = super::template wrap(other.gobj_()); } // support way to convert to owning box // (by means of copy as opposed to a simple ref) CppType copy_() const { return super::template wrap(this->gobj_copy_()); } }; // basis for ref-holding to non-registered plain C boxed type template class CBoxedRefWrapper : public Base { typedef Base super; public: typedef CppType BoxType; CBoxedRefWrapper(std::nullptr_t = nullptr) {} // construct from owning version, but not the other way around // (which requires an explicit copy) CBoxedRefWrapper(const CppType &other) { (super &)(*this) = super::template wrap(other.gobj_()); } }; } // namespace detail } // namespace gi #endif // GI_BOXED_HPP