boxed.hpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. #ifndef GI_BOXED_HPP
  2. #define GI_BOXED_HPP
  3. #include <memory>
  4. #include <glib-object.h>
  5. #include <glib.h>
  6. namespace gi
  7. {
  8. namespace repository
  9. {
  10. // specialize to enable copyable boxed wrapper
  11. // ideally only used in case where the copy is (equivalently);
  12. // * cheap
  13. // * does not change the underlying pointer
  14. // * refcount based
  15. // * has no semantics effects
  16. // (e.g. GstMiniObject refcount affects writable)
  17. template<typename CType>
  18. struct enable_boxed_copy :
  19. #if GI_ENABLE_BOXED_COPY_ALL
  20. public std::true_type
  21. #else
  22. public std::false_type
  23. #endif
  24. {};
  25. // should be used within proper namespace
  26. #define GI_ENABLE_BOXED_COPY(CType) \
  27. template<> \
  28. struct enable_boxed_copy<CType> : public std::true_type \
  29. {};
  30. } // namespace repository
  31. namespace detail
  32. {
  33. // class tags
  34. class Boxed
  35. {};
  36. class CBoxed : public Boxed
  37. {};
  38. class GBoxed : public Boxed
  39. {};
  40. template<typename CType>
  41. class SharedWrapper
  42. {
  43. public:
  44. typedef SharedWrapper self;
  45. protected:
  46. CType *data_ = nullptr;
  47. public:
  48. CType *gobj_() { return data_; }
  49. const CType *gobj_() const { return data_; }
  50. explicit operator bool() const { return (bool)data_; }
  51. bool operator==(const SharedWrapper &other) const
  52. {
  53. return data_ == other.data_;
  54. }
  55. bool operator==(std::nullptr_t o) const { return data_ == o; }
  56. bool operator!=(const SharedWrapper &other) const
  57. {
  58. return data_ != other.data_;
  59. }
  60. bool operator!=(std::nullptr_t o) const { return data_ != o; }
  61. CType *release_()
  62. {
  63. auto tmp = this->data_;
  64. this->data_ = nullptr;
  65. return tmp;
  66. }
  67. };
  68. template<typename CppType, typename CType>
  69. struct GBoxedFuncs
  70. {
  71. static CType *copy(const void *data)
  72. {
  73. return (CType *)g_boxed_copy(CppType::get_type_(), data);
  74. }
  75. static void free(void *data) { g_boxed_free(CppType::get_type_(), data); }
  76. };
  77. struct CBoxedFuncsBase
  78. {
  79. static void free(void *data) { g_free(data); }
  80. };
  81. template<typename CppType, typename CType>
  82. struct CBoxedFuncs : CBoxedFuncsBase
  83. {
  84. static CType *copy(const void *data)
  85. {
  86. #if GLIB_CHECK_VERSION(2, 68, 0)
  87. return (CType *)g_memdup2(data, sizeof(CType));
  88. #else
  89. return (CType *)g_memdup(data, sizeof(CType));
  90. #endif
  91. }
  92. };
  93. template<typename CType, typename Funcs, typename TagType>
  94. class BoxedWrapper : public SharedWrapper<CType>, public TagType
  95. {
  96. typedef BoxedWrapper self;
  97. protected:
  98. static void _deleter(CType *obj, ...)
  99. {
  100. if (obj)
  101. Funcs::free(obj);
  102. }
  103. static CType *_copy(const CType *obj)
  104. {
  105. return obj ? Funcs::copy(obj) : nullptr;
  106. }
  107. public:
  108. typedef CType BaseObjectType;
  109. BoxedWrapper(CType *obj = nullptr) noexcept { this->data_ = obj; }
  110. CType *gobj_copy_() const { return _copy(this->gobj_()); }
  111. // resulting casted type determines ownership
  112. template<typename Cpp>
  113. static Cpp wrap(const typename Cpp::BaseObjectType *obj)
  114. {
  115. static_assert(sizeof(Cpp) == sizeof(self), "type wrap not supported");
  116. static_assert(std::is_base_of<self, Cpp>::value, "type wrap not supported");
  117. BoxedWrapper w(const_cast<typename Cpp::BaseObjectType *>(obj));
  118. return std::move(*static_cast<Cpp *>(&w));
  119. }
  120. };
  121. // in templates below, Base should be a subclass of BoxedWrapper
  122. // so we re-use the members it provides, as well as the wrap template
  123. // to avoid ambiguous reference to the latter
  124. // (if BoxedWrapper were inherited from again)
  125. // the nullptr_t constructor (indirectly) supports `= nullptr` (assignment)
  126. template<typename CppType, typename CType>
  127. using GBoxedWrapperBase =
  128. BoxedWrapper<CType, GBoxedFuncs<CppType, CType>, GBoxed>;
  129. // assuming Base has suitable members such as above BoxedWrapper
  130. template<typename Base>
  131. class MoveWrapper : public Base
  132. {
  133. typedef Base super;
  134. public:
  135. using super::super;
  136. ~MoveWrapper() { this->_deleter(this->data_, static_cast<Base *>(this)); }
  137. MoveWrapper(const MoveWrapper &) = delete;
  138. MoveWrapper &operator=(const MoveWrapper &) = delete;
  139. MoveWrapper(MoveWrapper &&o) noexcept { *this = std::move(o); }
  140. MoveWrapper &operator=(MoveWrapper &&o) noexcept
  141. {
  142. if (this != &o) {
  143. this->_deleter(this->data_, static_cast<Base *>(this));
  144. (Base &)(*this) = std::move(o);
  145. o.data_ = nullptr;
  146. }
  147. return *this;
  148. }
  149. };
  150. template<typename Base>
  151. class CopyWrapper : public MoveWrapper<Base>
  152. {
  153. typedef MoveWrapper<Base> super;
  154. public:
  155. using super::super;
  156. CopyWrapper(const CopyWrapper &o) noexcept : super()
  157. {
  158. this->data_ = this->_copy(o.data_);
  159. }
  160. CopyWrapper &operator=(const CopyWrapper &o) noexcept
  161. {
  162. if (this != &o) {
  163. this->_deleter(this->data_, static_cast<Base *>(this));
  164. if (sizeof(Base) > sizeof(this->data_))
  165. (Base &)(*this) = std::move(o);
  166. this->data_ = this->_copy(o.data_);
  167. }
  168. return *this;
  169. }
  170. CopyWrapper(CopyWrapper &&o) noexcept = default;
  171. CopyWrapper &operator=(CopyWrapper &&o) noexcept = default;
  172. // also accept from corresponding Reference (also based on Base)
  173. CopyWrapper(const Base &o) noexcept : super()
  174. {
  175. this->data_ = this->_copy(((CopyWrapper &)o).data_);
  176. }
  177. CopyWrapper(Base &&o) noexcept : super()
  178. {
  179. (super &)(*this) = std::move((super &)o);
  180. }
  181. };
  182. template<typename CType, typename Base>
  183. using SelectWrapper =
  184. typename std::conditional<repository::enable_boxed_copy<CType>::value,
  185. CopyWrapper<Base>, MoveWrapper<Base>>::type;
  186. // basis for registered boxed types
  187. template<typename CppType, typename CType,
  188. typename Base = GBoxedWrapperBase<CppType, CType>, typename RefType = void>
  189. class GBoxedWrapper : public SelectWrapper<CType, Base>
  190. {
  191. typedef SelectWrapper<CType, Base> super;
  192. public:
  193. typedef RefType ReferenceType;
  194. using super::super;
  195. GBoxedWrapper(std::nullptr_t = nullptr) {}
  196. void allocate_()
  197. {
  198. if (this->data_)
  199. return;
  200. // make sure we match boxed allocation with boxed free
  201. // still guessing here that all-0 makes for a decent init :-(
  202. CType tmp;
  203. memset(&tmp, 0, sizeof(tmp));
  204. this->data_ = (CType *)g_boxed_copy(this->get_type_(), &tmp);
  205. }
  206. // essentially g_boxed_copy
  207. CppType copy_() const
  208. {
  209. return super::template wrap<CppType>(this->gobj_copy_());
  210. }
  211. };
  212. template<typename CppType, typename CType>
  213. using CBoxedWrapperBase =
  214. BoxedWrapper<CType, CBoxedFuncs<CppType, CType>, CBoxed>;
  215. // basis for non-registered plain C boxed type
  216. template<typename CppType, typename CType,
  217. typename Base = CBoxedWrapperBase<CppType, CType>, typename RefType = void>
  218. class CBoxedWrapper : public MoveWrapper<Base>
  219. {
  220. typedef Base super;
  221. public:
  222. typedef RefType ReferenceType;
  223. CBoxedWrapper(std::nullptr_t = nullptr) {}
  224. void allocate_()
  225. {
  226. if (this->data_)
  227. return;
  228. this->data_ = g_new0(CType, 1);
  229. }
  230. static CppType new_()
  231. {
  232. return super::template wrap<CppType>(g_new0(CType, 1));
  233. }
  234. };
  235. // allocate helper;
  236. // dispatch to method if available
  237. template<typename T, typename Enable = void>
  238. struct allocator : public std::false_type
  239. {
  240. static void allocate(T &) {}
  241. };
  242. template<typename T>
  243. struct allocator<T, decltype(T().allocate_())> : public std::true_type
  244. {
  245. static void allocate(T &v) { v.allocate_(); }
  246. };
  247. template<typename T>
  248. void
  249. allocate(T &v)
  250. {
  251. allocator<T>::allocate(v);
  252. }
  253. // basis for ref-holding to registered box type
  254. template<typename CppType, typename CType, typename Base>
  255. class GBoxedRefWrapper : public Base
  256. {
  257. typedef Base super;
  258. public:
  259. typedef CppType BoxType;
  260. GBoxedRefWrapper(std::nullptr_t = nullptr) {}
  261. // construct from owning version, but not the other way around
  262. // (which requires an explicit copy)
  263. GBoxedRefWrapper(const CppType &other)
  264. {
  265. (super &)(*this) = super::template wrap<super>(other.gobj_());
  266. }
  267. // support way to convert to owning box
  268. // (by means of copy as opposed to a simple ref)
  269. CppType copy_() const
  270. {
  271. return super::template wrap<CppType>(this->gobj_copy_());
  272. }
  273. };
  274. // basis for ref-holding to non-registered plain C boxed type
  275. template<typename CppType, typename CType, typename Base>
  276. class CBoxedRefWrapper : public Base
  277. {
  278. typedef Base super;
  279. public:
  280. typedef CppType BoxType;
  281. CBoxedRefWrapper(std::nullptr_t = nullptr) {}
  282. // construct from owning version, but not the other way around
  283. // (which requires an explicit copy)
  284. CBoxedRefWrapper(const CppType &other)
  285. {
  286. (super &)(*this) = super::template wrap<super>(other.gobj_());
  287. }
  288. };
  289. } // namespace detail
  290. } // namespace gi
  291. #endif // GI_BOXED_HPP