base.hpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. #ifndef GI_BASE_HPP
  2. #define GI_BASE_HPP
  3. // attempt to auto-discover exception support:
  4. #ifndef GI_CONFIG_EXCEPTIONS
  5. #if defined(_MSC_VER)
  6. #include <cstddef> // for _HAS_EXCEPTIONS
  7. #endif
  8. #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS)
  9. #define GI_CONFIG_EXCEPTIONS 1
  10. #else
  11. #define GI_CONFIG_EXCEPTIONS 0
  12. #endif
  13. #endif
  14. // lots of declarations might be attributed as deprecated,
  15. // but not so annotated, so let's avoid warning floods
  16. // also handle complaints about const qualified casts
  17. // (due to silly const qualified scalar parameters)
  18. #define GI_DISABLE_DEPRECATED_WARN_BEGIN \
  19. _Pragma("GCC diagnostic push") \
  20. _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") \
  21. _Pragma("GCC diagnostic push") \
  22. _Pragma("GCC diagnostic ignored \"-Wignored-qualifiers\"")
  23. #define GI_DISABLE_DEPRECATED_WARN_END \
  24. _Pragma("GCC diagnostic pop") _Pragma("GCC diagnostic pop")
  25. #include "boxed.hpp"
  26. #include "objectbase.hpp"
  27. #include <cstddef>
  28. #include <functional>
  29. #include <string>
  30. #include <type_traits>
  31. // required for generated code
  32. #include <tuple>
  33. #if GI_DL
  34. #include <dlfcn.h>
  35. #include <vector>
  36. #endif
  37. namespace gi
  38. {
  39. namespace detail
  40. {
  41. template<typename E>
  42. [[noreturn]] inline void
  43. try_throw(E &&e)
  44. {
  45. #if GI_CONFIG_EXCEPTIONS
  46. throw std::forward<E>(e);
  47. #else
  48. (void)e;
  49. abort();
  50. #endif
  51. }
  52. // constructor does not appreciate NULL, so wrap that here
  53. // map NULL to empty string; not quite the same, but it will do
  54. inline std::string
  55. make_string(const char *s)
  56. {
  57. return std::string(s ? s : "");
  58. }
  59. // helper string subtype
  60. // used to overload unwrap of optional string argument
  61. // (transfrom empty string to null)
  62. // NOTE std::optional requires C++17
  63. class optional_string : public std::string
  64. {};
  65. class noncopyable
  66. {
  67. public:
  68. noncopyable() {}
  69. noncopyable(const noncopyable &) = delete;
  70. noncopyable &operator=(const noncopyable &) = delete;
  71. noncopyable(noncopyable &&) = default;
  72. noncopyable &operator=(noncopyable &&) = default;
  73. };
  74. class scope_guard : public noncopyable
  75. {
  76. private:
  77. std::function<void()> cleanup_;
  78. public:
  79. scope_guard(std::function<void()> &&cleanup) : cleanup_(std::move(cleanup)) {}
  80. ~scope_guard() noexcept(false)
  81. {
  82. #if GI_CONFIG_EXCEPTIONS
  83. #if __cplusplus >= 201703L
  84. auto pending = std::uncaught_exceptions();
  85. #else
  86. auto pending = std::uncaught_exception();
  87. #endif
  88. try {
  89. #endif
  90. cleanup_();
  91. #if GI_CONFIG_EXCEPTIONS
  92. } catch (...) {
  93. if (!pending)
  94. throw;
  95. }
  96. #endif
  97. }
  98. };
  99. // as in
  100. // http://ericniebler.com/2013/08/07/universal-references-and-the-copy-constructo/
  101. template<typename A, typename B>
  102. using disable_if_same_or_derived = typename std::enable_if<
  103. !std::is_base_of<A, typename std::remove_reference<B>::type>::value>::type;
  104. } // namespace detail
  105. namespace repository
  106. {
  107. // class types declare c type within class
  108. // others can do so using this (e.g. enum)
  109. template<typename CppType>
  110. struct declare_ctype_of
  111. {};
  112. // and for all cases the reverse cpp type
  113. template<typename CType>
  114. struct declare_cpptype_of
  115. {};
  116. // generate code must specialize appropriately
  117. template<typename T>
  118. struct is_enumeration : public std::false_type
  119. {};
  120. template<typename T>
  121. struct is_bitfield : public std::false_type
  122. {};
  123. } // namespace repository
  124. struct transfer_full_t;
  125. struct transfer_none_t;
  126. namespace traits
  127. {
  128. template<typename T, typename U = void>
  129. struct if_valid_type
  130. {
  131. typedef U type;
  132. };
  133. template<typename T, typename U = void>
  134. struct is_valid_type : public std::true_type
  135. {};
  136. template<typename, typename = void>
  137. struct is_type_complete : public std::false_type
  138. {};
  139. template<typename T>
  140. struct is_type_complete<T, typename if_valid_type<decltype(sizeof(T))>::type>
  141. : public std::true_type
  142. {};
  143. template<typename T>
  144. using is_decayed = std::is_same<typename std::decay<T>::type, T>;
  145. template<typename T>
  146. using is_cboxed =
  147. typename std::conditional<std::is_base_of<detail::CBoxed, T>::value,
  148. std::true_type, std::false_type>::type;
  149. template<typename T>
  150. using is_gboxed =
  151. typename std::conditional<std::is_base_of<detail::GBoxed, T>::value,
  152. std::true_type, std::false_type>::type;
  153. template<typename T>
  154. using is_boxed =
  155. typename std::conditional<std::is_base_of<detail::Boxed, T>::value,
  156. std::true_type, std::false_type>::type;
  157. // avoid derived cases
  158. template<typename T>
  159. using is_object =
  160. typename std::conditional<std::is_base_of<detail::ObjectBase, T>::value &&
  161. sizeof(T) == sizeof(gpointer),
  162. std::true_type, std::false_type>::type;
  163. template<typename T>
  164. using is_wrapper =
  165. typename std::conditional<std::is_base_of<detail::wrapper_tag, T>::value &&
  166. sizeof(T) == sizeof(gpointer),
  167. std::true_type, std::false_type>::type;
  168. // bring in to this namespace
  169. using repository::is_bitfield;
  170. using repository::is_enumeration;
  171. // aka passthrough
  172. template<typename T>
  173. using is_basic =
  174. typename std::conditional<std::is_same<T, gpointer>::value ||
  175. std::is_same<T, gconstpointer>::value ||
  176. std::is_arithmetic<T>::value,
  177. std::true_type, std::false_type>::type;
  178. // almost passthrough (on lower level at least)
  179. template<typename T>
  180. using is_plain = typename std::conditional<traits::is_basic<T>::value ||
  181. std::is_enum<T>::value,
  182. std::true_type, std::false_type>::type;
  183. template<typename T, typename E = void>
  184. struct is_reftype : public std::false_type
  185. {};
  186. template<typename T>
  187. struct is_reftype<T,
  188. typename if_valid_type<typename std::decay<T>::type::BoxType>::type>
  189. : public std::true_type
  190. {};
  191. template<typename T, typename Enable = void>
  192. struct has_ctype_member : public std::false_type
  193. {};
  194. template<typename T>
  195. struct has_ctype_member<T,
  196. typename if_valid_type<typename T::BaseObjectType>::type>
  197. : public std::true_type
  198. {};
  199. // return corresponding c type (if any)
  200. // (string and basic type not considered)
  201. // preserve const
  202. template<typename T, typename Enable = void>
  203. struct ctype
  204. {};
  205. // class case
  206. template<typename T>
  207. struct ctype<T, typename std::enable_if<is_valid_type<
  208. typename std::decay<T>::type::BaseObjectType>::value>::type>
  209. {
  210. typedef typename std::remove_reference<T>::type CppType;
  211. // make sure; avoid subclassed cases
  212. static_assert(is_wrapper<CppType>::value || is_boxed<CppType>::value,
  213. "must be object or boxed wrapper");
  214. typedef typename CppType::BaseObjectType CType;
  215. typedef typename std::conditional<std::is_const<CppType>::value, const CType,
  216. CType>::type *type;
  217. };
  218. // remaining cases
  219. template<typename T>
  220. struct ctype<T, typename if_valid_type<
  221. typename repository::declare_ctype_of<T>::type>::type>
  222. {
  223. typedef typename repository::declare_ctype_of<T>::type CType;
  224. typedef typename std::conditional<std::is_const<T>::value, const CType,
  225. CType>::type type;
  226. };
  227. // basic cases passthrough
  228. template<typename T>
  229. struct ctype<T,
  230. typename std::enable_if<(std::is_fundamental<T>::value &&
  231. !std::is_same<T, bool>::value) ||
  232. std::is_same<T, gpointer>::value ||
  233. std::is_same<T, gconstpointer>::value>::type>
  234. {
  235. typedef T type;
  236. };
  237. // ... exception though for bool
  238. template<>
  239. struct ctype<bool, void>
  240. {
  241. typedef gboolean type;
  242. };
  243. // as used in callback signatures
  244. // or in list (un)wrapping
  245. template<>
  246. struct ctype<const std::string &, void>
  247. {
  248. typedef const char *type;
  249. };
  250. template<>
  251. struct ctype<std::string, void>
  252. {
  253. typedef char *type;
  254. };
  255. template<typename T1, typename T2>
  256. struct ctype<std::pair<T1, T2>>
  257. {
  258. typedef std::pair<typename ctype<T1>::type, typename ctype<T2>::type> type;
  259. };
  260. // conversely
  261. // return corresponding cpp type (if known)
  262. // (string and basic type not considered)
  263. // preserve const
  264. template<typename T, typename Transfer = transfer_full_t,
  265. typename Enable = void>
  266. struct cpptype
  267. {};
  268. // generic
  269. template<typename T>
  270. struct cpptype<T *, transfer_full_t,
  271. typename if_valid_type<typename repository::declare_cpptype_of<
  272. typename std::remove_const<T>::type>::type>::type>
  273. {
  274. typedef typename repository::declare_cpptype_of<
  275. typename std::remove_const<T>::type>::type CppType;
  276. typedef typename std::conditional<std::is_const<T>::value, const CppType,
  277. CppType>::type type;
  278. };
  279. template<typename T>
  280. struct cpptype<T, transfer_full_t,
  281. typename if_valid_type<typename repository::declare_cpptype_of<
  282. typename std::remove_const<T>::type>::type>::type>
  283. {
  284. typedef typename repository::declare_cpptype_of<
  285. typename std::remove_const<T>::type>::type CppType;
  286. typedef typename std::conditional<std::is_const<T>::value, const CppType,
  287. CppType>::type type;
  288. };
  289. // basic cases passthrough
  290. template<typename T>
  291. struct cpptype<T, transfer_full_t,
  292. typename std::enable_if<std::is_fundamental<T>::value ||
  293. std::is_same<T, gpointer>::value ||
  294. std::is_same<T, gconstpointer>::value>::type>
  295. {
  296. typedef T type;
  297. };
  298. #if 0
  299. template<>
  300. struct cpptype<char *, transfer_full_t>
  301. {
  302. using type = std::string;
  303. };
  304. #endif
  305. // handle none transfer case
  306. template<typename T>
  307. struct cpptype<T, transfer_none_t>
  308. {
  309. using CppType = typename cpptype<T, transfer_full_t>::type;
  310. template<typename TT, typename Enable = void>
  311. struct map_type
  312. {
  313. using type = TT;
  314. };
  315. template<typename TT>
  316. struct map_type<TT, typename if_valid_type<typename TT::ReferenceType>::type>
  317. {
  318. using type = typename TT::ReferenceType;
  319. };
  320. using type = typename map_type<CppType>::type;
  321. };
  322. // map owning box type to corresponding reference box type
  323. template<typename T>
  324. struct reftype
  325. {
  326. typedef typename T::ReferenceType type;
  327. };
  328. } // namespace traits
  329. // specify transfer type when (un)wrapping
  330. // this approach is safer than some booleans and allows overload combinations
  331. struct transfer_t
  332. {
  333. const int value;
  334. constexpr explicit transfer_t(int v = 0) : value(v) {}
  335. };
  336. struct transfer_none_t : public transfer_t
  337. {
  338. constexpr transfer_none_t() : transfer_t(0) {}
  339. };
  340. struct transfer_full_t : public transfer_t
  341. {
  342. constexpr transfer_full_t() : transfer_t(1) {}
  343. };
  344. struct transfer_container_t : public transfer_t
  345. {
  346. constexpr transfer_container_t() : transfer_t(2) {}
  347. };
  348. const constexpr transfer_t transfer_dummy = transfer_t();
  349. const constexpr transfer_none_t transfer_none = transfer_none_t();
  350. const constexpr transfer_full_t transfer_full = transfer_full_t();
  351. const constexpr transfer_container_t transfer_container =
  352. transfer_container_t();
  353. template<typename Transfer>
  354. struct element_transfer
  355. {};
  356. template<>
  357. struct element_transfer<transfer_none_t>
  358. {
  359. typedef transfer_none_t type;
  360. };
  361. template<>
  362. struct element_transfer<transfer_full_t>
  363. {
  364. typedef transfer_full_t type;
  365. };
  366. template<>
  367. struct element_transfer<transfer_container_t>
  368. {
  369. typedef transfer_none_t type;
  370. };
  371. // unwrapping a callback
  372. // specify call scope type
  373. struct scope_t
  374. {
  375. const int value;
  376. constexpr explicit scope_t(int v = 0) : value(v) {}
  377. };
  378. struct scope_call_t : public scope_t
  379. {
  380. constexpr scope_call_t() : scope_t(0) {}
  381. };
  382. struct scope_async_t : public scope_t
  383. {
  384. constexpr scope_async_t() : scope_t(1) {}
  385. };
  386. struct scope_notified_t : public scope_t
  387. {
  388. constexpr scope_notified_t() : scope_t(2) {}
  389. };
  390. const constexpr scope_t scope_dummy = scope_t();
  391. const constexpr scope_call_t scope_call = scope_call_t();
  392. const constexpr scope_async_t scope_async = scope_async_t();
  393. const constexpr scope_notified_t scope_notified = scope_notified_t();
  394. // (dummy) helper tag to aid in overload resolution
  395. template<typename Interface>
  396. struct interface_tag
  397. {
  398. typedef Interface type;
  399. };
  400. #if GI_DL
  401. namespace detail
  402. {
  403. // dynamic load of symbol
  404. inline void *
  405. load_symbol(const std::vector<const char *> libs, const char *symbol)
  406. {
  407. void *s = nullptr;
  408. for (const auto &l : libs) {
  409. auto h = dlopen(l, RTLD_LAZY | RTLD_GLOBAL | RTLD_NODELETE);
  410. if (h) {
  411. s = dlsym(h, symbol);
  412. dlclose(h);
  413. if (s)
  414. break;
  415. }
  416. }
  417. return s;
  418. }
  419. } // namespace detail
  420. #endif // GI_DL
  421. } // namespace gi
  422. #endif // GI_BASE_HPP