objectclass.hpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125
  1. #ifndef GI_OBJECTCLASS_HPP
  2. #define GI_OBJECTCLASS_HPP
  3. /**
  4. * The purposes of the code and classes here is to perform registration of
  5. * a new GType type for a custom class that (suitably) inherits from these.
  6. * Setting up a new GType involves registration of a new type (and its signals
  7. * and properties). This involves potentially custom overridden implementations
  8. * of the parent class(es) (C-style virtual) methods (= entries in class
  9. * structure) and likewise so for the interfaces. For either of the latter
  10. * cases, pointers to wrapper functions are inserted in the class/interface
  11. * struct, that will in turn call generated C++ virtual methods
  12. * (which have presumably been overridden by the custom subclass).
  13. *
  14. * The tricky part in the above is that GType/C methods are "optional",
  15. * in that an entry in a class/interface struct can be left as NULL,
  16. * and it is a common pattern for calling code to check for such
  17. * (rather than unconditionally calling). To preserve such behaviour,
  18. * an entry in a class/interface struct should only be "routed" to a
  19. * C++ virtual method if the custom SubClass actually has a definition for it.
  20. * The approach below tries to detect this (basically by checking if
  21. * SubClass::method is a different type than Base::method). But that might
  22. * fail (with compilation errors due to overload resolution failure), so there
  23. * is also a system to manually specify what is defined, e.g. whether the
  24. * corresponding entry in the class/interface struct should be filled
  25. * for the SubClass' generated GType.
  26. *
  27. */
  28. #include "callback.hpp"
  29. #include "object.hpp"
  30. #include <memory>
  31. #include <typeinfo>
  32. #include <vector>
  33. namespace gi
  34. {
  35. // slightly nasty; will be generated
  36. namespace repository
  37. {
  38. namespace GObject
  39. {
  40. enum class SignalFlags : std::underlying_type<::GSignalFlags>::type;
  41. }
  42. } // namespace repository
  43. namespace detail
  44. {
  45. class ObjectBaseClass : public noncopyable
  46. {
  47. public:
  48. virtual ~ObjectBaseClass() {}
  49. ObjectBaseClass() : gobject_(nullptr) {}
  50. ObjectBaseClass(ObjectBaseClass &&other)
  51. {
  52. gobject_ = other.gobject_;
  53. other.gobject_ = nullptr;
  54. }
  55. ObjectBaseClass &operator=(ObjectBaseClass &&other)
  56. {
  57. if (this != &other) {
  58. gobject_ = other.gobject_;
  59. other.gobject_ = nullptr;
  60. }
  61. return *this;
  62. }
  63. protected:
  64. typedef void (*interface_register_function)(GType class_type, gpointer init);
  65. void request_interface(interface_register_function reg, gpointer init)
  66. {
  67. itfs.emplace_back(reg, init);
  68. }
  69. GType base_gtype()
  70. {
  71. return gobject_ ? g_type_parent(G_OBJECT_TYPE(gobject_)) : G_TYPE_NONE;
  72. }
  73. protected:
  74. // owns 1 ref (possibly managed externally)
  75. GObject *gobject_;
  76. // additional type setup
  77. using interface_inits_t =
  78. std::vector<std::pair<interface_register_function, gpointer>>;
  79. interface_inits_t itfs;
  80. };
  81. //// interface setup ////
  82. // virtual inheritance as multiple inheritance will be used
  83. // however, only 1 base instance should be around
  84. class InterfaceClass : public virtual ObjectBaseClass
  85. {
  86. protected:
  87. static void add_interface(GType class_type, GType itf_type,
  88. GInterfaceInitFunc itf_init, gpointer init_data)
  89. {
  90. GInterfaceInfo itf_info = {
  91. itf_init, // interface_init
  92. nullptr, // interface_finalize
  93. init_data // interface_data
  94. };
  95. g_type_add_interface_static(class_type, itf_type, &itf_info);
  96. }
  97. };
  98. template<typename InterfaceDef>
  99. class InterfaceImpl : public InterfaceDef, public InterfaceClass
  100. {
  101. typedef typename InterfaceDef::instance_type instance_type_t;
  102. protected:
  103. static void register_interface(GType class_type, gpointer init_data)
  104. {
  105. add_interface(class_type, InterfaceDef::instance_type::get_type_(),
  106. InterfaceDef::interface_init, init_data);
  107. }
  108. InterfaceImpl(gpointer init_data = nullptr)
  109. {
  110. request_interface(InterfaceImpl::register_interface, init_data);
  111. }
  112. // conversion to regular interface side
  113. instance_type_t interface_(gi::interface_tag<instance_type_t>)
  114. {
  115. return gi::wrap(
  116. (typename instance_type_t::BaseObjectType *)g_object_ref(gobject_),
  117. gi::transfer_full);
  118. }
  119. instance_type_t interface_()
  120. {
  121. return gi::wrap(
  122. (typename instance_type_t::BaseObjectType *)g_object_ref(gobject_),
  123. gi::transfer_full);
  124. }
  125. };
  126. template<typename InterfaceImpl>
  127. class InterfaceClassImpl : public InterfaceImpl
  128. {
  129. typedef typename InterfaceImpl::instance_type::BaseObjectType *c_type;
  130. typedef typename InterfaceImpl::interface_type interface_type_t;
  131. // use runtime data to avoid code generation of template
  132. // interface_type_t *istruct_;
  133. GType klass_;
  134. protected:
  135. static interface_type_t *get_struct_(GType ktype)
  136. {
  137. auto klass = g_type_class_peek(ktype);
  138. return (interface_type_t *)g_type_interface_peek(
  139. klass, InterfaceImpl::instance_type::get_type_());
  140. }
  141. interface_type_t *get_struct_() { return get_struct_(klass_); }
  142. c_type gobj_() { return (c_type)this->gobject_; }
  143. InterfaceClassImpl(GType klass, gpointer itf_init_data = nullptr)
  144. : InterfaceImpl(itf_init_data), klass_(klass)
  145. {}
  146. };
  147. class ObjectClass;
  148. class PropertyBase;
  149. inline GQuark
  150. object_data_quark()
  151. {
  152. static const char *OBJECT_DATA_KEY = "GIOBJECT_OBJECT";
  153. static GQuark q = g_quark_from_static_string(OBJECT_DATA_KEY);
  154. return q;
  155. }
  156. using repository::GObject::Object;
  157. using repository::GObject::ParamSpec;
  158. //// init data collection ////
  159. // a function pointer that serves as factory for the actual class_init data
  160. // (e.g. holds data on which methods have been overridden)
  161. // (void return to avoid function cast warning; returns real data otherwise)
  162. typedef void (*type_init_data_factory_t)();
  163. template<typename ClassDef, typename SubClass,
  164. typename std::enable_if<std::is_same<SubClass, void>::value>::type * =
  165. nullptr>
  166. type_init_data_factory_t
  167. make_type_init_data()
  168. {
  169. return nullptr;
  170. }
  171. template<typename BaseDef, typename SubClass,
  172. typename std::enable_if<!std::is_same<SubClass, void>::value>::type * =
  173. nullptr>
  174. type_init_data_factory_t
  175. make_type_init_data()
  176. {
  177. // hard cast; should return meaningful data
  178. return (type_init_data_factory_t)&BaseDef::TypeInitData::template factory<
  179. SubClass>;
  180. }
  181. // helper macro to obtain data from factory (if provided)
  182. #define GI_MEMBER_INIT_DATA(ClassType, factory) \
  183. (factory ? ((ClassType(*)())(factory))() : ClassType());
  184. // in generated code;
  185. // each class/interface member (function) is assocated with a single type
  186. // base class for tagged boolean member types
  187. // (default to true for legacy case where no init data is captured from type)
  188. template<typename Tag>
  189. struct member_type
  190. {
  191. bool value;
  192. constexpr member_type(bool v = true) : value(v) {}
  193. explicit operator bool() { return value; }
  194. };
  195. // combine manual Spec with code generated Default
  196. template<typename Spec, typename Default>
  197. struct Combine : public Spec, public Default
  198. {
  199. using Default::has_definition;
  200. using Spec::defines;
  201. // dispatch from query signature to default if no manual specificiation
  202. template<typename MemberType, typename SubClass>
  203. constexpr static bool defines(const MemberType *m, const SubClass *cl)
  204. {
  205. return has_definition(m, cl);
  206. };
  207. };
  208. template<typename SubClass, typename Default>
  209. using DefinitionData = Combine<typename SubClass::DefinitionData, Default>;
  210. // conflicts might arise between interfaces and/or class
  211. // generate some dummy check types to force failure
  212. #define GI_MEMBER_CHECK_CONFLICT(member) _check_member_conflict_##member
  213. // generated code tries to detect a defined member in SubClass as follows
  214. #define GI_MEMBER_DEFAULT_HAS_DEFINITION(BaseDef, member) \
  215. template<typename SubClass> \
  216. constexpr static bool has_definition(const member##_t *, const SubClass *) \
  217. { \
  218. /* the use of conflict type check is only to trigger a compiler error \
  219. * if there is such a conflict \
  220. * in that case, manual specification of definitions are needed \
  221. * (which will then avoid this code path instantiation) \
  222. * (type should never be void, so merely serves as dummy check) \
  223. */ \
  224. return std::is_void<typename SubClass::GI_MEMBER_CHECK_CONFLICT( \
  225. member)>::value || \
  226. !std::is_same<decltype(&BaseDef::member##_), \
  227. decltype(&SubClass::member##_)>::value; \
  228. }
  229. // helper macro used in generated code
  230. #define GI_MEMBER_DEFINE(BaseDef, member) \
  231. struct member##_tag; \
  232. using member##_t = detail::member_type<member##_tag>; \
  233. member##_t member; \
  234. GI_MEMBER_DEFAULT_HAS_DEFINITION(BaseDef, member)
  235. // the automated way might/will fail in case of overload resolution failure
  236. // (due to member conflicts with interfaces)
  237. // so the following can be used to specify definition situation
  238. // should be used in an inner struct DefinitionData in the SubClass
  239. #define GI_DEFINES_MEMBER(BaseDef, member, _defines) \
  240. template<typename SubClass> \
  241. constexpr static bool defines( \
  242. const BaseDef::TypeInitData::member##_t *, const SubClass *) \
  243. { \
  244. return _defines; \
  245. }
  246. // uses function overload on all of the above to determine
  247. // if member of DefData is defined/overridden in SubClass
  248. // (and should then be registered in the class/interface struct)
  249. #define GI_MEMBER_HAS_DEFINITION(SubClass, DefData, member) \
  250. DefData::defines((member##_t *)(nullptr), (SubClass *)(nullptr))
  251. //// class setup ////
  252. typedef std::map<std::string, std::pair<PropertyBase *, ParamSpec>> properties;
  253. inline void property_class_init(
  254. ObjectClass *impl, gpointer g_class, gpointer class_data);
  255. inline gi::cstring_v
  256. klass_type_name(const std::type_info &ti, gi::cstring_v klassname)
  257. {
  258. return (klassname && klassname.at(0)) ? klassname : ti.name();
  259. }
  260. class ObjectClass : public virtual ObjectBaseClass
  261. {
  262. public:
  263. typedef gi::repository::GObject::Object instance_type;
  264. typedef GObjectClass class_type;
  265. // above init code refers to this inner type unconditionally
  266. // so arrange for a fallback in baseclass
  267. struct DefinitionData
  268. {
  269. constexpr static bool defines(...) { return false; }
  270. };
  271. protected:
  272. struct ClassInitNode
  273. {
  274. GClassInitFunc self;
  275. type_init_data_factory_t class_init_data_factory;
  276. const ClassInitNode *child;
  277. };
  278. private:
  279. static const constexpr char *CLASS_PREFIX = "GIOBJECT__";
  280. static std::string canonical_name(const std::string &name)
  281. {
  282. auto result = name;
  283. for (auto &p : result) {
  284. if (!(g_ascii_isalnum(p) || p == '_' || p == '-'))
  285. p = '+';
  286. }
  287. return result;
  288. }
  289. typedef std::vector<std::pair<GClassInitFunc, type_init_data_factory_t>>
  290. class_inits_t;
  291. struct class_data_t
  292. {
  293. std::unique_ptr<class_inits_t> class_inits;
  294. const properties *props;
  295. ObjectClass *impl;
  296. };
  297. static void class_init_all(gpointer g_class, gpointer class_data)
  298. {
  299. std::unique_ptr<class_data_t> data((class_data_t *)class_data);
  300. // class_init below is called with top-level class_data,
  301. // others (= code generated) with their own
  302. for (auto &&ci : *data->class_inits)
  303. ci.first(
  304. g_class, ci.first == &class_init ? class_data : gpointer(ci.second));
  305. }
  306. static void class_init(gpointer g_class, gpointer class_data)
  307. {
  308. class_data_t *data = ((class_data_t *)class_data);
  309. // delegate property handling
  310. property_class_init(data->impl, g_class, (gpointer *)data->props);
  311. }
  312. GType register_type(GType base_type, const gi::cstring_v klassname,
  313. const ClassInitNode &init_node, const interface_inits_t &itfs,
  314. const properties &props)
  315. {
  316. auto custom_name = std::string(CLASS_PREFIX) + canonical_name(klassname);
  317. // nothing to do if already registered
  318. GType custom_type = g_type_from_name(custom_name.c_str());
  319. if (custom_type)
  320. return custom_type;
  321. // otherwise create with same class/instance size as parent type
  322. GTypeQuery base_query = {
  323. 0,
  324. nullptr,
  325. 0,
  326. 0,
  327. };
  328. g_type_query(base_type, &base_query);
  329. const guint16 class_size = (guint16)base_query.class_size;
  330. const guint16 instance_size = (guint16)base_query.instance_size;
  331. // collect chain of class inits
  332. std::unique_ptr<class_inits_t> class_inits(new class_inits_t());
  333. auto node = &init_node;
  334. while (node) {
  335. class_inits->push_back({node->self, node->class_init_data_factory});
  336. node = node->child;
  337. }
  338. // assemble class_data
  339. std::unique_ptr<class_data_t> class_data(new class_data_t());
  340. // class creation will be triggered upon instance creation
  341. // which will happen shortly after this class
  342. // (so the list has to handled special, but the others will still be
  343. // around)
  344. class_data->class_inits = std::move(class_inits);
  345. class_data->impl = this;
  346. class_data->props = &props;
  347. const GTypeInfo derived_info = {
  348. class_size,
  349. nullptr, // base_init
  350. nullptr, // base_finalize
  351. class_init_all,
  352. nullptr, // class_finalize
  353. class_data.release(), // class_data
  354. instance_size,
  355. 0, // n_preallocs
  356. nullptr, // instance_init
  357. nullptr, // value_table
  358. };
  359. custom_type = g_type_register_static(
  360. base_type, custom_name.c_str(), &derived_info, GTypeFlags(0));
  361. // handle interfaces
  362. for (auto &&itf : itfs)
  363. itf.first(custom_type, itf.second);
  364. return custom_type;
  365. }
  366. void init(GType parent, const gi::cstring_v klassname,
  367. const ClassInitNode *node,
  368. const repository::GObject::construct_params &params,
  369. const properties &props, gpointer instance = nullptr)
  370. {
  371. GType gtype = register_type(
  372. parent, klassname, {class_init, nullptr, node}, itfs, props);
  373. itfs.clear();
  374. // create and link object instance
  375. // if needed, that is, otherwise use provided instance and tie onto that one
  376. GObject *obj = gobject_ =
  377. (GObject *)(instance ? instance : Object::new_(gtype, params));
  378. // should still be floating, then we assume ownership
  379. // if it is no longer, then it has already been stolen (e.g. GtkWindow),
  380. // and we need to add one here
  381. if (!instance && g_type_is_a(gtype, G_TYPE_INITIALLY_UNOWNED))
  382. g_object_ref_sink(gobject_);
  383. // mark this as associated wrapper object as retrieved by .instance()
  384. g_object_set_qdata_full(obj, object_data_quark(), this, destroy_notify);
  385. }
  386. static void destroy_notify(gpointer data)
  387. {
  388. ObjectClass *impl = (ObjectClass *)data;
  389. // sever link with object instance
  390. impl->gobject_ = nullptr;
  391. delete impl;
  392. }
  393. protected:
  394. ObjectClass(GType parent, const gi::cstring_v klassname,
  395. const ClassInitNode &node,
  396. const repository::GObject::construct_params &params,
  397. const properties &props)
  398. {
  399. init(parent, klassname, &node, params, props);
  400. }
  401. ObjectClass(const void *, GType parent, const gi::cstring_v klassname,
  402. const ClassInitNode &node,
  403. const repository::GObject::construct_params &params,
  404. const properties &props, gpointer instance)
  405. {
  406. init(parent, klassname, &node, params, props, instance);
  407. }
  408. ~ObjectClass()
  409. {
  410. // object destruction should typically be initiated from the
  411. // associated object instance based on refcount,
  412. // and so pass through destroy_notify, in which case no more gobject_
  413. // but it could come here first for a stack based custom object or alike
  414. if (gobject_) {
  415. // corresponding object should not have outstanding refs out there
  416. // not good otherwise, and why it should not be destructed this way
  417. if (gobject_->ref_count != 1)
  418. g_error("destroying object with outstanding object refs");
  419. // NOTE the unref might still trigger vmethod calls,
  420. // but destruction already happened down to this level,
  421. // so derived cast and vmethod call no longer possible
  422. // so sever link anyway to make that clear
  423. g_object_steal_qdata(gobject_, object_data_quark());
  424. g_object_unref(gobject_);
  425. }
  426. }
  427. public:
  428. ObjectClass(const std::type_info &ti,
  429. const repository::GObject::construct_params &params = {},
  430. const properties &props = {})
  431. {
  432. init(instance_type::get_type_(), ti.name(), nullptr, params, props);
  433. }
  434. template<typename SubClass>
  435. ObjectClass(const SubClass *,
  436. const repository::GObject::construct_params &params = {},
  437. const properties &props = {}, gpointer instance = nullptr,
  438. const gi::cstring_v klassname = nullptr)
  439. {
  440. const auto &ti = typeid(SubClass);
  441. init(instance_type::get_type_(), klass_type_name(ti, klassname), nullptr,
  442. params, props, instance);
  443. }
  444. operator Object() { return gi::wrap(gobject_, transfer_none); }
  445. GObjectClass *gobj_klass()
  446. {
  447. return (GObjectClass *)g_type_class_peek(G_OBJECT_TYPE(gobject_));
  448. }
  449. static ObjectClass *instance(GObject *gobject)
  450. {
  451. return (ObjectClass *)g_object_get_qdata(gobject, object_data_quark());
  452. }
  453. };
  454. // interfaces need to go left; constructors need to run first (to request
  455. // interface) class constructor deals with class_init last
  456. template<typename ClassDef, typename BaseClass, typename... Interfaces>
  457. class ClassTemplate : public ClassDef, public Interfaces..., public BaseClass
  458. {
  459. typedef typename ClassDef::instance_type::BaseObjectType *c_type;
  460. typedef typename ClassDef::class_type class_type_t;
  461. typedef typename ClassDef::instance_type instance_type_t;
  462. // make private
  463. using ClassDef::class_init;
  464. protected:
  465. class_type_t *get_struct_()
  466. {
  467. return (class_type_t *)g_type_class_peek(this->base_gtype());
  468. }
  469. c_type gobj_() { return (c_type)this->gobject_; }
  470. // constructor to be used by custom subclass
  471. [[deprecated]] ClassTemplate(const std::type_info &ti,
  472. const repository::GObject::construct_params &params = {},
  473. const properties &props = {})
  474. : Interfaces(instance_type_t::get_type_())...,
  475. BaseClass(instance_type_t::get_type_(), ti.name(),
  476. {&ClassDef::class_init, nullptr, nullptr}, params, props)
  477. {}
  478. // constructor for inner inheritance chain
  479. [[deprecated]] ClassTemplate(GType base, const gi::cstring_v klassname,
  480. const ObjectClass::ClassInitNode &node,
  481. const repository::GObject::construct_params &params,
  482. const properties &props)
  483. : Interfaces(instance_type_t::get_type_())...,
  484. BaseClass(base, klassname, {&ClassDef::class_init, nullptr, &node},
  485. params, props)
  486. {}
  487. // as above, new style
  488. // constructor to be used by custom subclass
  489. template<typename SubClass>
  490. ClassTemplate(const SubClass *sub,
  491. const repository::GObject::construct_params &params = {},
  492. const properties &props = {}, gpointer instance = nullptr,
  493. const gi::cstring_v klassname = nullptr)
  494. : Interfaces(instance_type_t::get_type_(),
  495. gpointer(make_type_init_data<Interfaces, SubClass>()))...,
  496. BaseClass(sub, instance_type_t::get_type_(),
  497. klass_type_name(typeid(SubClass), klassname),
  498. {&ClassDef::class_init, make_type_init_data<ClassDef, SubClass>(),
  499. nullptr},
  500. params, props, instance)
  501. {}
  502. // constructor for inner inheritance chain
  503. template<typename SubClass>
  504. ClassTemplate(const SubClass *sub, GType base, const gi::cstring_v klassname,
  505. const ObjectClass::ClassInitNode &node,
  506. const repository::GObject::construct_params &params,
  507. const properties &props, gpointer instance = nullptr)
  508. : Interfaces(instance_type_t::get_type_(),
  509. gpointer(make_type_init_data<Interfaces, SubClass>()))...,
  510. BaseClass(sub, base, klassname,
  511. {&ClassDef::class_init, make_type_init_data<ClassDef, SubClass>(),
  512. &node},
  513. params, props, instance)
  514. {}
  515. public:
  516. class_type_t *gobj_klass() { return (class_type_t *)BaseClass::gobj_klass(); }
  517. // repeat to disambiguate
  518. typedef typename ClassDef::instance_type instance_type;
  519. // access to regular object side
  520. instance_type_t object_()
  521. {
  522. return gi::wrap((typename instance_type_t::BaseObjectType *)g_object_ref(
  523. this->gobject_),
  524. gi::transfer_full);
  525. }
  526. };
  527. // NOTE as the impl and regular object side are both inherited here,
  528. // ambiguity might not only result from inheriting multiple interfaces,
  529. // but also between either of these sides
  530. // as such, no operator cast is added on the impl side
  531. // instead, use the object_() member to pass to/through regular side
  532. template<typename ObjectT, typename ClassT>
  533. class ObjectImpl : public ObjectT, public ClassT
  534. {
  535. public:
  536. typedef typename ClassT::instance_type baseclass_type;
  537. using ObjectT::gobj_;
  538. protected:
  539. [[deprecated]] ObjectImpl(const std::type_info &ti,
  540. const repository::GObject::construct_params &params =
  541. repository::GObject::construct_params{},
  542. const properties &props = properties{})
  543. : ClassT(ti, params, props)
  544. { // link object ptrs (untracked by ObjectBase)
  545. this->data_ = this->gobject_;
  546. }
  547. template<typename SubClass>
  548. ObjectImpl(const SubClass *sub,
  549. const repository::GObject::construct_params &params =
  550. repository::GObject::construct_params{},
  551. const properties &props = properties{})
  552. : ClassT(sub, params, props)
  553. { // link object ptrs (untracked by ObjectBase)
  554. this->data_ = this->gobject_;
  555. }
  556. // special advanced case (for internal/override use) by custom subclass
  557. // where constructed instance is associated with provided object instance
  558. // (rather than the latter created as part of construction, as usual)
  559. // if klassname KlassName is specified,
  560. // registered typename is GOBJECT__KlassName
  561. template<typename SubClass>
  562. ObjectImpl(ObjectT instance, const SubClass *sub,
  563. const gi::cstring_v klassname = nullptr,
  564. const repository::GObject::construct_params &params =
  565. repository::GObject::construct_params{},
  566. const properties &props = properties{})
  567. : ClassT(sub, params, props, instance.gobj_(), klassname)
  568. { // link object ptrs (untracked by ObjectBase)
  569. this->data_ = this->gobject_;
  570. }
  571. ~ObjectImpl()
  572. { // disconnect (avoid ObjectBase management)
  573. this->data_ = nullptr;
  574. }
  575. };
  576. // wrapper helper to call virtual method
  577. // used in implementation, so we can casually use types in default argument
  578. template<typename C, typename T, typename RetTransfer,
  579. typename ArgTransfers = void,
  580. typename CSig = typename map_cpp_function<T,
  581. typename std::conditional<std::is_null_pointer<RetTransfer>::value, T,
  582. void>::type>::type>
  583. struct method_wrapper;
  584. template<typename C, typename R, typename... Args, typename RetTransfer,
  585. typename... Transfers, typename CR, typename... CArgs>
  586. struct method_wrapper<C, R (*)(Args...), RetTransfer, std::tuple<Transfers...>,
  587. CR(CArgs...)>
  588. {
  589. private:
  590. typedef R (C::*member_type)(Args...);
  591. struct caller_data
  592. {
  593. C *this_;
  594. const member_type m;
  595. };
  596. static R caller(Args &&...args, void *user_data)
  597. {
  598. auto d = (caller_data *)user_data;
  599. return ((d->this_)->*(d->m))(std::forward<Args>(args)...);
  600. }
  601. public:
  602. template<member_type m>
  603. static CR wrapper(
  604. typename traits::ctype<typename C::instance_type>::type p, CArgs... args)
  605. {
  606. ObjectClass *oc = ObjectClass::instance((GObject *)p);
  607. C *c = dynamic_cast<C *>(oc);
  608. if (!oc) {
  609. // connection already severed by heap destruction
  610. // use refptr instead of stack allocation if this is a problem
  611. g_error("missing object");
  612. } else if (!c) {
  613. // on our way to crash anyway
  614. g_error("wrong object type");
  615. }
  616. caller_data d{c, m};
  617. return transform_caller<R(Args...), RetTransfer, std::tuple<Transfers...>,
  618. CR(CArgs...)>::wrapper(args..., caller, &d);
  619. }
  620. };
  621. // simplified special case for plain/raw fallback with no wrapping/transfer
  622. // (pick std::nullptr_t for specialization to represent absence of transfer)
  623. template<typename C, typename R, typename... Args>
  624. struct method_wrapper<C, R (*)(Args...), std::nullptr_t>
  625. {
  626. private:
  627. typedef R (C::*member_type)(Args...);
  628. public:
  629. template<member_type m>
  630. static R wrapper(
  631. typename traits::ctype<typename C::instance_type>::type p, Args... args)
  632. {
  633. ObjectClass *oc = ObjectClass::instance((GObject *)p);
  634. C *c = dynamic_cast<C *>(oc);
  635. if (!oc) {
  636. // connection already severed by heap destruction
  637. // use refptr instead of stack allocation if this is a problem
  638. g_error("missing object");
  639. } else if (!c) {
  640. // on our way to crash anyway
  641. g_error("wrong object type");
  642. }
  643. return ((c)->*(m))(std::forward<Args>(args)...);
  644. }
  645. };
  646. //// property handling ////
  647. class PropertyBase
  648. {
  649. typedef PropertyBase self_type;
  650. // subclass handles this
  651. virtual void set_property(const GValue *value) = 0;
  652. virtual void get_property(GValue *value) = 0;
  653. static GQuark get_instance_quark(guint prop_id)
  654. {
  655. auto str = std::string("GI__") +
  656. std::to_string((unsigned long)(&class_init)) + '_' +
  657. std::to_string(prop_id);
  658. return g_quark_from_string(str.c_str());
  659. }
  660. static PropertyBase *get_instance(
  661. GObject *object, GParamSpec *pspec, guint prop_id)
  662. {
  663. auto impl = ObjectClass::instance(object);
  664. auto poffset = g_param_spec_get_qdata(pspec, get_instance_quark(prop_id));
  665. return poffset ? (PropertyBase *)((char *)impl + GPOINTER_TO_INT(poffset))
  666. : nullptr;
  667. }
  668. static void get_property(
  669. GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
  670. {
  671. auto prop = get_instance(object, pspec, prop_id);
  672. if (prop) {
  673. prop->get_property(value);
  674. } else {
  675. G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
  676. }
  677. }
  678. static void set_property(
  679. GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
  680. {
  681. auto prop = get_instance(object, pspec, prop_id);
  682. if (prop) {
  683. prop->set_property(value);
  684. } else {
  685. G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
  686. }
  687. }
  688. protected:
  689. static GQuark get_prop_quark(const char *name)
  690. {
  691. auto str = std::string("GI__") + name;
  692. return g_quark_from_string(str.c_str());
  693. }
  694. public:
  695. static void install_property(ObjectClass *impl, GObjectClass *klass,
  696. self_type *self, ParamSpec pspec, const gi::cstring_v name = "")
  697. {
  698. g_return_if_fail(impl);
  699. g_return_if_fail(klass);
  700. g_return_if_fail(self);
  701. // only 1 real parameter
  702. g_return_if_fail(!pspec || !name.size());
  703. // identify any property tracked by this PropertyBase code
  704. // (may or may not be unique process-wise)
  705. static guint prop_id;
  706. auto gpspec = pspec.gobj_();
  707. if (name.size()) {
  708. g_object_class_override_property(klass, ++prop_id, name.c_str());
  709. // the overridden one will be passed to get/set
  710. // (and returned by find as well)
  711. gpspec = g_object_class_find_property(klass, name.c_str());
  712. } else {
  713. // mind transfer full
  714. g_object_class_install_property(klass, ++prop_id, pspec.gobj_copy_());
  715. }
  716. // add metadata to pspec to retrieve upon set/get
  717. auto offset = ((char *)self - (char *)impl);
  718. auto quark = get_instance_quark(prop_id);
  719. g_param_spec_set_qdata(gpspec, quark, GINT_TO_POINTER(offset));
  720. // mark property installed on this klass/type (by whatever code path)
  721. // sadly, in case of overridden properties,
  722. // glib does not expose sufficient info on whether it is already further up
  723. // or the override that we may be trying to add to this/our class
  724. // so, setup some parallel tracking using qdata
  725. auto gtype = G_OBJECT_CLASS_TYPE(klass);
  726. g_type_set_qdata(gtype, get_prop_quark(gpspec->name), GINT_TO_POINTER(1));
  727. }
  728. virtual ~PropertyBase() {}
  729. static void class_init(
  730. ObjectClass *impl, gpointer g_class, gpointer class_data)
  731. {
  732. auto klass = (GObjectClass *)g_class;
  733. auto props = (properties *)class_data;
  734. klass->get_property = get_property;
  735. klass->set_property = set_property;
  736. // need to add override properties at this point
  737. // (before creation of any instance or class struct)
  738. for (auto &&e : *props)
  739. install_property(impl, klass, e.second.first, e.second.second, e.first);
  740. }
  741. };
  742. inline void
  743. property_class_init(ObjectClass *impl, gpointer g_class, gpointer class_data)
  744. {
  745. PropertyBase::class_init(impl, g_class, class_data);
  746. }
  747. template<typename T>
  748. class property : protected property_proxy<T>, public PropertyBase
  749. {
  750. T val_;
  751. void add_property(ObjectClass *impl, const gi::cstring_v name)
  752. {
  753. auto pspec = this->object_.find_property(name);
  754. // could have been defined already upon prior object creation
  755. if (pspec) {
  756. this->pspec_ = pspec;
  757. // could be defined by a superclass
  758. // or already added to this class by prior instance
  759. auto gtype = G_OBJECT_CLASS_TYPE(impl->gobj_klass());
  760. if (!g_type_get_qdata(gtype, get_prop_quark(name.c_str())))
  761. install_property(impl, impl->gobj_klass(), this, nullptr, name);
  762. } else {
  763. pspec = this->pspec_;
  764. install_property(impl, impl->gobj_klass(), this, pspec);
  765. }
  766. // set value to default param value
  767. Value value;
  768. value.init<T>();
  769. g_param_value_set_default(pspec.gobj_(), &value);
  770. val_ = detail::get_value<T>(&value);
  771. // avoid circular ref loop
  772. g_object_unref(this->object_.gobj_());
  773. }
  774. protected:
  775. void set_property(const GValue *value) override
  776. {
  777. val_ = detail::get_value<T>(value);
  778. }
  779. void get_property(GValue *value) override { detail::set_value(value, val_); }
  780. public:
  781. template<typename... Args,
  782. typename std::enable_if<sizeof...(Args) != 1>::type * = nullptr>
  783. property(ObjectClass *impl, Args &&...args)
  784. : property_proxy<T>(
  785. (Object)(*impl), ParamSpec::new_<T>(std::forward<Args>(args)...))
  786. {
  787. add_property(impl, this->pspec_.get_name());
  788. }
  789. property(ObjectClass *impl, const gi::cstring_v name)
  790. : property_proxy<T>((Object)(*impl), ParamSpec())
  791. {
  792. add_property(impl, name);
  793. }
  794. ~property()
  795. {
  796. // clear link
  797. this->object_.release_();
  798. }
  799. void notify()
  800. {
  801. g_object_notify_by_pspec(this->object_.gobj_(), this->pspec_.gobj_());
  802. }
  803. PropertyBase &operator=(T value)
  804. {
  805. val_ = value;
  806. notify();
  807. return *this;
  808. }
  809. void set_value(T value, bool _notify = true)
  810. {
  811. val_ = value;
  812. if (_notify)
  813. notify();
  814. }
  815. T get_value() const { return val_; }
  816. operator T() const { return val_; }
  817. property_proxy<T> get_proxy() const { return *this; }
  818. };
  819. template<typename T>
  820. class property_read : public property<T>
  821. {
  822. public:
  823. template<typename... Args>
  824. property_read(ObjectClass *impl, Args &&...args)
  825. : property<T>(
  826. impl, std::forward<Args>(args)..., ParamFlags(G_PARAM_READABLE))
  827. {}
  828. property_proxy_read<T> get_proxy() const
  829. {
  830. return {this->object_, this->pspec_};
  831. }
  832. };
  833. template<typename T>
  834. class property_write : public property<T>
  835. {
  836. public:
  837. template<typename... Args>
  838. property_write(ObjectClass *impl, Args &&...args)
  839. : property<T>(
  840. impl, std::forward<Args>(args)..., ParamFlags(G_PARAM_WRITABLE))
  841. {}
  842. property_proxy_write<T> get_proxy() const
  843. {
  844. return {this->object_, this->pspec_};
  845. }
  846. };
  847. //// signal handling ////
  848. using repository::GObject::SignalFlags;
  849. template<typename T, typename Base = repository::GObject::Object>
  850. class signal;
  851. template<typename R, typename Instance, typename... Args, typename Base>
  852. class signal<R(Instance, Args...), Base>
  853. : public signal_proxy<R(Instance, Args...)>
  854. {
  855. typedef signal_proxy<R(Instance, Args...)> super;
  856. static guint new_(const gi::cstring_v name, GType itype, SignalFlags flags)
  857. {
  858. // collect array of types
  859. GType types[] = {traits::gtype<Args>::get_type()...};
  860. const int nparams = sizeof...(Args);
  861. const GType ret_type = traits::gtype<R>::get_type();
  862. // TODO accumulator
  863. return g_signal_newv(name.c_str(), itype, (GSignalFlags)flags, nullptr,
  864. nullptr, nullptr, nullptr, ret_type, nparams, types);
  865. }
  866. public:
  867. signal(
  868. Base *owner, const gi::cstring_v name, SignalFlags flags = (SignalFlags)0)
  869. : super(*owner, name)
  870. {
  871. const GType itype = owner->gobj_type_();
  872. if (!g_signal_lookup(name.c_str(), itype))
  873. new_(name, itype, flags);
  874. // sneak away ref to avoid ref loop
  875. g_object_unref(this->object_.gobj_());
  876. }
  877. ~signal()
  878. {
  879. // clear link
  880. this->object_.release_();
  881. }
  882. };
  883. template<typename T>
  884. struct ObjectDeleter
  885. {
  886. void operator()(T *ob)
  887. {
  888. if (ob->gobj_())
  889. g_object_unref(ob->gobj_());
  890. }
  891. };
  892. template<typename T>
  893. class ref_ptr : public std::unique_ptr<T, ObjectDeleter<T>>
  894. {
  895. typedef std::unique_ptr<T, ObjectDeleter<T>> super;
  896. typedef typename T::baseclass_type baseclass_type;
  897. public:
  898. ref_ptr(T *ptr = nullptr, bool own = true) : super(ptr)
  899. {
  900. if (ptr && !own)
  901. g_object_ref(ptr->gobj_());
  902. }
  903. ref_ptr(ref_ptr &&other) = default;
  904. ref_ptr(const ref_ptr &other) : super(nullptr)
  905. {
  906. if (other)
  907. g_object_ref(other->gobj_());
  908. this->reset(other.get());
  909. }
  910. ref_ptr &operator=(ref_ptr &&other) = default;
  911. ref_ptr &operator=(const ref_ptr &other)
  912. {
  913. if (other && &other != this)
  914. g_object_ref(other->gobj_());
  915. this->reset(other.get());
  916. return *this;
  917. }
  918. operator baseclass_type() { return *this->get(); }
  919. };
  920. template<typename T, typename... Args>
  921. ref_ptr<T>
  922. make_ref(Args &&...args)
  923. {
  924. // move ownership of ref acquired during creation
  925. return ref_ptr<T>(new T(std::forward<Args>(args)...));
  926. }
  927. template<typename T, typename traits::if_valid_type<
  928. typename T::baseclass_type>::type * = nullptr>
  929. ref_ptr<T>
  930. ref_ptr_cast(Object ob)
  931. {
  932. if (ob) {
  933. ObjectClass *instance = ObjectClass::instance(ob.gobj_());
  934. if (instance) {
  935. auto obj = dynamic_cast<T *>(instance);
  936. if (obj) {
  937. // arrange to obtain an extra ref
  938. return ref_ptr<T>(obj, false);
  939. }
  940. }
  941. }
  942. return nullptr;
  943. }
  944. } // namespace detail
  945. // TODO impl namespace ??
  946. using detail::property;
  947. using detail::property_read;
  948. using detail::property_write;
  949. using detail::signal;
  950. using detail::make_ref;
  951. using detail::ref_ptr;
  952. using detail::ref_ptr_cast;
  953. namespace repository
  954. {
  955. namespace GObject
  956. {
  957. namespace impl
  958. {
  959. // bring into namespaces as in code generation
  960. namespace internal
  961. {
  962. using ObjectClass = detail::ObjectClass;
  963. } // namespace internal
  964. using ObjectImpl = detail::ObjectImpl<Object, detail::ObjectClass>;
  965. } // namespace impl
  966. } // namespace GObject
  967. } // namespace repository
  968. } // namespace gi
  969. #endif // GI_OBJECTCLASS_HPP