value.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. #ifndef GI_VALUE_HPP
  2. #define GI_VALUE_HPP
  3. #include "exception.hpp"
  4. #include "wrap.hpp"
  5. #include <gobject/gvaluecollector.h>
  6. #include <string.h>
  7. #include <type_traits>
  8. namespace gi
  9. {
  10. namespace repository
  11. {
  12. // specialize to declare gtype info
  13. // if not within class get_type()
  14. // gvalue info can also be included this way
  15. // to inject support into Value wrapper
  16. template<typename T>
  17. struct declare_gtype_of : public std::false_type
  18. {};
  19. } // namespace repository
  20. namespace traits
  21. {
  22. namespace detail
  23. {
  24. template<typename T, class Enable = void>
  25. struct gtype : public std::false_type
  26. {
  27. static GType get_type()
  28. {
  29. // dummy test to trigger (almost) always
  30. static_assert(std::is_void<T>::value, "type is not a registered GType");
  31. return 0;
  32. }
  33. };
  34. // gboxed/gobject (or otherwise class) cases
  35. template<typename T>
  36. struct gtype<T, typename if_valid_type<decltype(T::get_type_())>::type>
  37. : public std::true_type
  38. {
  39. typedef typename std::remove_reference<T>::type CppType;
  40. static GType get_type() { return CppType::get_type_(); }
  41. };
  42. // otherwise externally declared
  43. template<typename T>
  44. struct gtype<T, typename if_valid_type<decltype(repository::declare_gtype_of<
  45. T>::get_type())>::type> : public std::true_type
  46. {
  47. static constexpr GType (
  48. *get_type)() = repository::declare_gtype_of<T>::get_type;
  49. };
  50. // gvalue helper info
  51. template<typename T, class Enable = void>
  52. struct gvalue : public std::false_type
  53. {};
  54. // as declared (both of set_value and get_value or neither)
  55. template<typename T>
  56. struct gvalue<T,
  57. typename if_valid_type<decltype(repository::declare_gtype_of<T>::get_value(
  58. nullptr))>::type> : public std::true_type
  59. {
  60. static T get(const GValue *val)
  61. {
  62. return repository::declare_gtype_of<T>::get_value(val);
  63. }
  64. static void set(GValue *val, T t)
  65. {
  66. repository::declare_gtype_of<T>::set_value(val, t);
  67. }
  68. };
  69. template<typename T>
  70. using is_enum_or_bitfield =
  71. typename std::conditional<std::is_enum<T>::value && gtype<T>::value,
  72. std::true_type, std::false_type>::type;
  73. // handle enum/flags cases, rather than many declares
  74. template<typename T>
  75. struct gvalue<T, typename std::enable_if<is_enum_or_bitfield<T>::value>::type>
  76. : public std::true_type
  77. {
  78. static T get(const GValue *val)
  79. {
  80. GType t = gtype<T>::get_type();
  81. if (G_TYPE_IS_FLAGS(t))
  82. return static_cast<T>(g_value_get_flags(val));
  83. // assume enum, let glib complain otherwise
  84. return static_cast<T>(g_value_get_enum(val));
  85. }
  86. static void set(GValue *val, T v)
  87. {
  88. GType t = gtype<T>::get_type();
  89. if (G_TYPE_IS_FLAGS(t)) {
  90. g_value_set_flags(val, (guint)v);
  91. } else {
  92. // assume enum, let glib complain otherwise
  93. g_value_set_enum(val, (gint)v);
  94. }
  95. }
  96. };
  97. } // namespace detail
  98. template<typename T>
  99. using gtype = detail::gtype<
  100. typename std::decay<typename std::remove_reference<T>::type>::type>;
  101. template<typename T>
  102. using gvalue = detail::gvalue<
  103. typename std::decay<typename std::remove_reference<T>::type>::type>;
  104. template<typename T>
  105. using is_enum_or_bitfield = detail::is_enum_or_bitfield<
  106. typename std::decay<typename std::remove_reference<T>::type>::type>;
  107. #if 0
  108. template<typename T, typename Enable = void>
  109. struct is_flag : public std::false_type {};
  110. // TODO extend fundamental type stuff ??
  111. template<typename T>
  112. struct is_flag<T,
  113. typename std::enable_if<std::is_enum<T>::value &&
  114. repository::declare_gtype_of<T>::get_fundamental_type() == G_TYPE_FLAGS>::type> :
  115. public std::true_type {};
  116. #endif
  117. } // namespace traits
  118. // C++ types are used below (e.g. int) instead of e.g. gint
  119. // since gint64 might map to same as glong (or not)
  120. // so some of the int types are "best approximation" from C++ type to GType
  121. // instead; use the following types to guide to the right overload
  122. typedef char vchar;
  123. typedef long vlong;
  124. typedef int vint;
  125. typedef long long vint64;
  126. typedef bool vboolean;
  127. typedef unsigned char vuchar;
  128. typedef unsigned long vulong;
  129. typedef unsigned int vuint;
  130. typedef unsigned long long vuint64;
  131. typedef float vfloat;
  132. typedef double vdouble;
  133. // NOTE: (plain) char might be signed or unsigned depending on platform
  134. // but gchar == char always anyway
  135. static_assert(std::is_same<gchar, char>::value, "now what");
  136. namespace repository
  137. {
  138. template<>
  139. struct declare_gtype_of<void>
  140. {
  141. static GType get_type() { return G_TYPE_NONE; }
  142. };
  143. #define GI_DECLARE_GTYPE(cpptype, g_type) \
  144. template<> \
  145. struct declare_gtype_of<cpptype> \
  146. { \
  147. static constexpr GType get_type() { return g_type; } \
  148. };
  149. #define GI_DECLARE_GTYPE_VALUE(cpptype, g_type, value_suffix) \
  150. template<> \
  151. struct declare_gtype_of<cpptype> \
  152. { \
  153. static constexpr GType get_type() { return g_type; } \
  154. static void set_value(GValue *val, cpptype v) \
  155. { \
  156. g_value_set_##value_suffix(val, v); \
  157. } \
  158. static cpptype get_value(const GValue *val) \
  159. { \
  160. return g_value_get_##value_suffix(val); \
  161. } \
  162. };
  163. // declare non-cv qualified type
  164. GI_DECLARE_GTYPE_VALUE(gpointer, G_TYPE_POINTER, pointer)
  165. GI_DECLARE_GTYPE_VALUE(bool, G_TYPE_BOOLEAN, boolean)
  166. GI_DECLARE_GTYPE_VALUE(char, G_TYPE_CHAR, schar)
  167. GI_DECLARE_GTYPE_VALUE(unsigned char, G_TYPE_UCHAR, uchar)
  168. GI_DECLARE_GTYPE_VALUE(int, G_TYPE_INT, int)
  169. GI_DECLARE_GTYPE_VALUE(unsigned int, G_TYPE_UINT, uint)
  170. GI_DECLARE_GTYPE_VALUE(long, G_TYPE_LONG, long)
  171. GI_DECLARE_GTYPE_VALUE(unsigned long, G_TYPE_ULONG, ulong)
  172. GI_DECLARE_GTYPE_VALUE(long long, G_TYPE_INT64, int64)
  173. GI_DECLARE_GTYPE_VALUE(unsigned long long, G_TYPE_UINT64, uint64)
  174. GI_DECLARE_GTYPE_VALUE(float, G_TYPE_FLOAT, float)
  175. GI_DECLARE_GTYPE_VALUE(double, G_TYPE_DOUBLE, double)
  176. // some custom set/get for these
  177. // remember; the pointer is non-const
  178. GI_DECLARE_GTYPE(const char *, G_TYPE_STRING)
  179. GI_DECLARE_GTYPE(char *, G_TYPE_STRING)
  180. GI_DECLARE_GTYPE(std::string, G_TYPE_STRING)
  181. GI_DECLARE_GTYPE(gi::cstring, G_TYPE_STRING)
  182. GI_DECLARE_GTYPE(gi::cstring_v, G_TYPE_STRING)
  183. #undef GI_DECLARE_GTYPE_VALUE
  184. #undef GI_DECLARE_GTYPE
  185. } // namespace repository
  186. namespace detail
  187. {
  188. // GValue helpers
  189. // set_value
  190. template<typename T,
  191. typename std::enable_if<traits::gvalue<T>::value>::type * = nullptr>
  192. inline void
  193. set_value(GValue *val, T v)
  194. {
  195. traits::gvalue<T>::set(val, v);
  196. }
  197. inline void
  198. set_value(GValue *val, const std::string &s)
  199. {
  200. g_value_set_string(val, s.c_str());
  201. }
  202. inline void
  203. set_value(GValue *val, gi::cstring_v s)
  204. {
  205. g_value_set_string(val, s.c_str());
  206. }
  207. inline void
  208. set_value(GValue *val, const char *s)
  209. {
  210. g_value_set_string(val, s);
  211. }
  212. template<typename T,
  213. typename std::enable_if<traits::is_object<T>::value>::type * = nullptr>
  214. inline void
  215. set_value(GValue *val, T v)
  216. {
  217. // set might not handle NULL case
  218. g_value_take_object(val, v.gobj_copy_());
  219. }
  220. template<typename T,
  221. typename std::enable_if<traits::is_gboxed<T>::value>::type * = nullptr>
  222. inline void
  223. set_value(GValue *val, T v)
  224. {
  225. // set might not handle NULL case
  226. g_value_take_boxed(val, v.gobj_copy_());
  227. }
  228. // container case
  229. template<typename T, typename T::_detail::DataType * = nullptr>
  230. inline void
  231. set_value(GValue *val, T v)
  232. {
  233. v._set_value(val);
  234. }
  235. // get_value
  236. template<typename T,
  237. typename std::enable_if<traits::is_object<T>::value>::type * = nullptr>
  238. inline T
  239. get_value(const GValue *val)
  240. {
  241. // ensure sanity
  242. static_assert(std::is_class<T>::value && !std::is_const<T>::value,
  243. "non cv-qualified class type required");
  244. auto cv =
  245. static_cast<typename traits::ctype<T>::type>(g_value_dup_object(val));
  246. if (cv && !g_type_is_a(G_OBJECT_TYPE(cv), traits::gtype<T>::get_type()))
  247. detail::try_throw(transform_error(G_OBJECT_TYPE(cv)));
  248. return gi::wrap(cv, transfer_full);
  249. }
  250. template<typename T,
  251. typename std::enable_if<traits::is_gboxed<T>::value &&
  252. !traits::is_reftype<T>::value>::type * = nullptr>
  253. inline T
  254. get_value(const GValue *val)
  255. {
  256. // no way to know whether boxed type is correct
  257. // ensure sanity
  258. static_assert(std::is_class<T>::value && !std::is_const<T>::value,
  259. "non cv-qualified class type required");
  260. auto cv =
  261. static_cast<typename traits::ctype<T>::type>(g_value_dup_boxed(val));
  262. return gi::wrap(cv, transfer_full);
  263. }
  264. template<typename T,
  265. typename std::enable_if<traits::is_gboxed<T>::value &&
  266. traits::is_reftype<T>::value>::type * = nullptr>
  267. inline T
  268. get_value(const GValue *val)
  269. {
  270. // no way to know whether boxed type is correct
  271. // ensure sanity
  272. static_assert(std::is_class<T>::value && !std::is_const<T>::value,
  273. "non cv-qualified class type required");
  274. auto cv =
  275. static_cast<typename traits::ctype<T>::type>(g_value_get_boxed(val));
  276. return gi::wrap(cv, transfer_none);
  277. }
  278. template<typename T,
  279. typename std::enable_if<traits::gvalue<T>::value>::type * = nullptr>
  280. inline T
  281. get_value(const GValue *val)
  282. {
  283. return traits::gvalue<T>::get(val);
  284. }
  285. // sigh ...
  286. template<typename T,
  287. typename std::enable_if<std::is_same<T, std::string>::value ||
  288. std::is_base_of<detail::String, T>::value>::type * =
  289. nullptr>
  290. inline T
  291. get_value(const GValue *val)
  292. {
  293. return gi::wrap(g_value_get_string(val), transfer_none);
  294. }
  295. // container case
  296. template<typename T, typename T::_detail::DataType * = nullptr>
  297. inline T
  298. get_value(const GValue *val)
  299. {
  300. static_assert(traits::is_decayed<T>::value, "");
  301. return T::template _get_value<T>(val);
  302. }
  303. // convenience helper ...
  304. template<typename T,
  305. typename std::enable_if<std::is_same<T, void>::value>::type * = nullptr>
  306. inline T
  307. get_value(const GValue * /*val*/)
  308. {}
  309. // simple (RAII) Value wrapper for (internal) use
  310. struct Value : public GValue, noncopyable
  311. {
  312. void clear() { memset((void *)this, 0, sizeof(*this)); }
  313. Value() { clear(); }
  314. template<typename T, typename Enable = disable_if_same_or_derived<T, Value>>
  315. explicit Value(T &&v)
  316. {
  317. clear();
  318. g_value_init(this, traits::gtype<T>::get_type());
  319. set_value(this, std::forward<T>(v));
  320. }
  321. template<typename T>
  322. void init()
  323. {
  324. // handle no-op void (return value) corner case
  325. const GType tp = traits::gtype<T>::get_type();
  326. if (tp != G_TYPE_NONE)
  327. g_value_init(this, tp);
  328. }
  329. // let's not copy, but ok to move around
  330. Value(Value &&other)
  331. {
  332. memcpy((void *)this, &other, sizeof(*this));
  333. other.clear();
  334. }
  335. Value &operator=(Value &&other)
  336. {
  337. if (this != &other) {
  338. memcpy((void *)this, &other, sizeof(*this));
  339. other.clear();
  340. }
  341. return *this;
  342. }
  343. ~Value()
  344. {
  345. if (G_VALUE_TYPE(this))
  346. g_value_unset(this);
  347. }
  348. };
  349. // we really rely upon this as part of the ABI
  350. // justifies operations above and some explicit casts above
  351. // (to avoid -Wclass-memaccess)
  352. static_assert(sizeof(Value) == sizeof(GValue), "unsupported compiler");
  353. template<typename R>
  354. inline R
  355. transform_value(const GValue *val)
  356. {
  357. detail::Value dest;
  358. dest.init<R>();
  359. if (!g_value_transform(val, &dest))
  360. detail::try_throw(detail::transform_error(G_VALUE_TYPE(&dest)));
  361. return detail::get_value<R>(&dest);
  362. }
  363. // hand-crafted Value wrapper with an interface as it would be generated
  364. // and used by generated code, along with additional convenience
  365. class ValueBase : public gi::detail::GBoxedWrapperBase<ValueBase, GValue>
  366. {
  367. using self_type = ValueBase;
  368. public:
  369. static GType get_type_() G_GNUC_CONST { return G_TYPE_VALUE; }
  370. void copy(self_type dest) const { g_value_copy(gobj_(), dest.gobj_()); }
  371. void reset() { g_value_reset(gobj_()); }
  372. void unset() { g_value_unset(gobj_()); }
  373. bool transform(self_type dest) const
  374. {
  375. return g_value_transform(gobj_(), dest.gobj_());
  376. }
  377. static bool type_compatible(GType src_type, GType dest_type)
  378. {
  379. return g_value_type_compatible(src_type, dest_type);
  380. }
  381. static bool type_transformable(GType src_type, GType dest_type)
  382. {
  383. return g_value_type_transformable(src_type, dest_type);
  384. }
  385. template<typename T>
  386. self_type &set_value(T &&v)
  387. {
  388. detail::set_value(gobj_(), std::forward<T>(v));
  389. return *this;
  390. }
  391. template<typename T>
  392. T get_value() const
  393. {
  394. return detail::get_value<T>(gobj_());
  395. }
  396. template<typename R>
  397. R transform_value()
  398. {
  399. return detail::transform_value<R>(gobj_());
  400. }
  401. };
  402. } // namespace detail
  403. namespace repository
  404. {
  405. namespace GObject
  406. {
  407. // build on above base with additional convenience (in owning case)
  408. class Value_Ref;
  409. class Value : public gi::detail::GBoxedWrapper<Value, GValue, detail::ValueBase,
  410. Value_Ref>
  411. {
  412. typedef gi::detail::GBoxedWrapper<Value, GValue, detail::ValueBase, Value_Ref>
  413. super_type;
  414. typedef Value self_type;
  415. public:
  416. using detail::ValueBase::copy;
  417. using super_type::copy;
  418. // hybrid GBoxed/CBoxed
  419. void allocate_()
  420. {
  421. if (this->data_)
  422. return;
  423. // make sure we match GValue boxed allocation with boxed free
  424. // (though last kown implementation uses g_new0/g_free)
  425. detail::Value tmp;
  426. this->data_ = (::GValue *)g_boxed_copy(G_TYPE_VALUE, &tmp);
  427. }
  428. // convenience
  429. Value() { allocate_(); }
  430. // allow non-explicit use for convenient calling
  431. // but avoid copy/move construct use
  432. template<typename T,
  433. typename std::enable_if<!std::is_base_of<Value,
  434. typename std::remove_reference<T>::type>::value>::type * = nullptr>
  435. Value(T &&t)
  436. {
  437. allocate_();
  438. init<T>(std::forward<T>(t));
  439. }
  440. Value &init(GType tp)
  441. {
  442. // no-op void corner case
  443. if (tp != G_TYPE_NONE)
  444. g_value_init(gobj_(), tp);
  445. return *this;
  446. }
  447. template<typename T>
  448. Value &init(T &&v)
  449. {
  450. g_value_init(gobj_(), traits::gtype<T>::get_type());
  451. set_value(std::forward<T>(v));
  452. return *this;
  453. }
  454. };
  455. class Value_Ref
  456. : public gi::detail::GBoxedRefWrapper<Value, ::GValue, detail::ValueBase>
  457. {
  458. typedef gi::detail::GBoxedRefWrapper<Value, ::GValue, detail::ValueBase>
  459. super_type;
  460. using super_type::super_type;
  461. };
  462. } // namespace GObject
  463. template<>
  464. struct declare_cpptype_of<GValue>
  465. {
  466. typedef GObject::Value type;
  467. };
  468. } // namespace repository
  469. } // namespace gi
  470. #endif // GI_VALUE_HPP