From: Emmanuele Bassi Date: Wed, 12 Jun 2013 14:18:29 +0000 (+0100) Subject: docs: Clean up the GObject tutorial a bit X-Git-Tag: 2.37.3~4 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=1f6f7e1c4d38a8eab41af3280fcfcdd23b270102;p=platform%2Fupstream%2Fglib.git docs: Clean up the GObject tutorial a bit Started off by using the new instance private data macro, ended up cleaning up the obscure, out of date, or simply broken concepts and paragraphs. https://bugzilla.gnome.org/show_bug.cgi?id=700035 --- diff --git a/docs/reference/gobject/tut_howto.xml b/docs/reference/gobject/tut_howto.xml index 1ee0051..436c636 100644 --- a/docs/reference/gobject/tut_howto.xml +++ b/docs/reference/gobject/tut_howto.xml @@ -40,8 +40,14 @@ grumpy (not a good thing) You must assess the fact that these conventions might have been designed by both smart and experienced people: maybe they - were at least partly right. Try to put your ego aside. + were at least partly right. Try to put your ego aside. + It is, nevertheless, important to note that these rules generally apply + to code that is meant to be called by third parties; it is perfectly + possible to write a valid, self-contained GObject types without most of + the boilerplate used in this tutorial; most of the boilerplate is also + not strictly required if you plan to use the GObject types only through + language bindings based on introspection. @@ -111,6 +117,7 @@ typedef struct _MamanBarClass MamanBarClass; struct _MamanBar { + /* Parent instance structure */ GObject parent_instance; /* instance members */ @@ -118,6 +125,7 @@ struct _MamanBar struct _MamanBarClass { + /* Parent class structure */ GObjectClass parent_class; /* class members */ @@ -134,51 +142,47 @@ GType maman_bar_get_type (void); - Most GTK+ types declare their private fields in the public header - with a /* private */ comment, relying on their user's intelligence - not to try to play with these fields. Fields not marked private - are considered public by default. The /* protected */ comment - (same semantics as those of C++) is also used, mainly in the GType - library, in code written by Tim Janik. + Types that require per-instance private data should use the + G_DEFINE_TYPE_WITH_PRIVATE() macro, or use the G_ADD_PRIVATE() + macro with the G_DEFINE_TYPE_WITH_CODE() or the G_DEFINE_TYPE_EXTENDED() + macros. The private structure is then defined in the .c file, + and can be accessed using the get_private() + function generated by the G_DEFINE_TYPE_* macros. -struct _MamanBar +struct _MamanBarPrivate { - GObject parent_instance; - - /*< private >*/ int hsize; }; - - - - All of Nautilus code and a lot of GNOME libraries use private - indirection members, as described by Herb Sutter in his Pimpl - articles(see Compilation Firewalls - and The Fast Pimpl Idiom: - he summarizes the different issues better than I will). - -typedef struct _MamanBarPrivate MamanBarPrivate; -struct _MamanBar +G_DEFINE_TYPE_WITH_PRIVATE (MamanBar, maman_bar, G_TYPE_OBJECT) + +static void +maman_bar_class_init (MamanBarClass *klass) { - GObject parent_instance; - - /*< private >*/ - MamanBarPrivate *priv; -}; +} + +static void +maman_bar_init (MamanBar *self) +{ + /* maman_bar_get_private() is generated by G_DEFINE_TYPE_WITH_PRIVATE() + * above, and it's local to the current compilation unit. + */ + MamanBarPrivate *priv = maman_bar_get_private (self); + + priv->hsize = 42; +} - Do not call this private, as - that is a registered c++ keyword. - - The private structure is then defined in the .c file, using the - g_type_class_add_private() function to notify the presence of - a private memory area for each instance and it can either - be retrieved using G_TYPE_INSTANCE_GET_PRIVATE() - each time is needed, or assigned to the priv - member of the instance structure inside the object's - init function. + + + + Most GNOME libraries use a pointer inside the instance structure + for simpler access to the private data structure, as described by + Herb Sutter in his Pimpl article (see Compilation Firewalls + and The Fast Pimpl Idiom + for reference). If you opt to use this idiom, you can assign the + pointer inside the instance initialization function, e.g.: -#define MAMAN_BAR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MAMAN_TYPE_BAR, MamanBarPrivate)) +G_DEFINE_TYPE_WITH_PRIVATE (MamanBar, maman_bar, G_TYPE_OBJECT) struct _MamanBarPrivate { @@ -188,27 +192,14 @@ struct _MamanBarPrivate static void maman_bar_class_init (MamanBarClass *klass) { - g_type_class_add_private (klass, sizeof (MamanBarPrivate)); } static void maman_bar_init (MamanBar *self) { - MamanBarPrivate *priv; - - self->priv = priv = MAMAN_BAR_GET_PRIVATE (self); - - priv->hsize = 42; + self->priv = maman_bar_get_private (self); + self->priv->hsize = 42; } - - - - - You don't need to free or allocate the private structure, only the - objects or pointers that it may contain. Another advantage of this - to the previous version is that is lessens memory fragmentation, - as the public and private parts of the instance memory are - allocated at once. @@ -280,7 +271,7 @@ struct _MamanBarPrivate { -G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT); +G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT) @@ -289,7 +280,9 @@ G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT); G_DEFINE_TYPE_WITH_CODE macro to control the get_type function implementation - for instance, to add a call to G_IMPLEMENT_INTERFACE macro which will - call the g_type_implement_interface function. + call the g_type_implement_interface function, + or call the G_ADD_PRIVATE macro will add an + instance private data structure. @@ -319,28 +312,36 @@ G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT); + It is important to note that object construction cannot ever + fail. If you require a fallible GObject construction, you can use the + GInitable and GAsyncInitable interfaces provided by the GIO library + + + As such, I would recommend writing the following code first: +G_DEFINE_TYPE_WITH_PRIVATE (MamanBar, maman_bar, G_TYPE_OBJECT) + +static void +maman_bar_class_init (MamanBarClass *klass) +{ +} + static void maman_bar_init (MamanBar *self) { - self->priv = MAMAN_BAR_GET_PRIVATE (self); + self->priv = maman_bar_get_private (self); /* initialize all public and private members to reasonable default values. */ - - /* If you need specific construction properties to complete initialization, - * delay initialization completion until the property is set. - */ } - Now, if you need special construction properties, install the properties in the class_init function, - override the set and get methods and implement the get and set methods as described in - . Make sure that these properties use a construct only - GParamSpec by setting the param spec's flag field to G_PARAM_CONSTRUCT_ONLY: this helps - GType ensure that these properties are not set again later by malicious user code. + If you need special construction properties, install the properties in + the class_init() function, override the set_property() + and get_property() methods of the GObject class, + and implement them as described by . enum { PROP_0, @@ -350,6 +351,7 @@ enum { N_PROPERTIES }; +/* Keep a pointer to the properties definition */ static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; static void @@ -365,32 +367,31 @@ bar_class_init (MamanBarClass *klass) "Maman construct prop", "Set maman's name", "no-name-set" /* default value */, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); g_object_class_install_properties (gobject_class, N_PROPERTIES, obj_properties); } - If you need this, make sure you can build and run code similar to the code shown above. Make sure - your construct properties can set correctly during construction, make sure you cannot set them - afterwards and make sure that if your users do not call g_object_new - with the required construction properties, these will be initialized with the default values. - - - - I consider good taste to halt program execution if a construction property is set its - default value. This allows you to catch client code which does not give a reasonable - value to the construction properties. Of course, you are free to disagree but you - should have a good reason to do so. + If you need this, make sure you can build and run code similar to the + code shown above. Also, make sure your construct properties can be set + without side effects during construction. - Some people sometimes need to construct their object but only after - the construction properties have been set. This is possible through - the use of the constructor class method as described in - or, more simply, using - the constructed class method available since GLib 2.12. + Some people sometimes need to complete the initialization of a instance + of a type only after the properties passed to the constructors have been + set. This is possible through the use of the constructor() + class method as described in or, + more simply, using the constructed() class method + available since GLib 2.12. Note that the constructed() + virtual function will only be invoked after the properties marked as + G_PARAM_CONSTRUCT_ONLY or G_PARAM_CONSTRUCT have been consumed, but + before the regular properties passed to g_object_new() + have been set. @@ -407,10 +408,11 @@ bar_class_init (MamanBarClass *klass) The destruction process of your object might be split in two different - phases: dispose and the finalize. + phases: dispose and the finalize. This split is necessary to handle + potential cycles due to the nature of the reference counting mechanism + used by GObject, as well as dealing with temporary vivification of + instances in case of signal emission during the destruction sequence. -#define MAMAN_BAR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MAMAN_TYPE_BAR, MamanBarPrivate)) - struct _MamanBarPrivate { GObject *an_object; @@ -418,31 +420,29 @@ struct _MamanBarPrivate gchar *a_string; }; -G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT); +G_DEFINE_TYPE_WITH_PRIVATE (MamanBar, maman_bar, G_TYPE_OBJECT) static void maman_bar_dispose (GObject *gobject) { MamanBar *self = MAMAN_BAR (gobject); - /* - * In dispose, you are supposed to free all types referenced from this + /* In dispose(), you are supposed to free all types referenced from this * object which might themselves hold a reference to self. Generally, * the most simple solution is to unref all members on which you own a * reference. */ - /* dispose might be called multiple times, so we must guard against - * calling g_object_unref() on an invalid GObject. + /* dispose() might be called multiple times, so we must guard against + * calling g_object_unref() on an invalid GObject by setting the member + * NULL; g_clear_object() does this for us, atomically. */ - if (self->priv->an_object) - { - g_object_unref (self->priv->an_object); - - self->priv->an_object = NULL; - } + g_clear_object (&self->priv->an_object); - /* Chain up to the parent class */ + /* Always chain up to the parent class; there is no need to check if + * the parent class implements the dispose() virtual function: it is + * always guaranteed to do so + */ G_OBJECT_CLASS (maman_bar_parent_class)->dispose (gobject); } @@ -453,7 +453,9 @@ maman_bar_finalize (GObject *gobject) g_free (self->priv->a_string); - /* Chain up to the parent class */ + /* Always chain up to the parent class; as with dispose(), finalize() + * is guaranteed to exist on the parent's class virtual function table + */ G_OBJECT_CLASS (maman_bar_parent_class)->finalize (gobject); } @@ -464,14 +466,12 @@ maman_bar_class_init (MamanBarClass *klass) gobject_class->dispose = maman_bar_dispose; gobject_class->finalize = maman_bar_finalize; - - g_type_class_add_private (klass, sizeof (MamanBarPrivate)); } static void maman_bar_init (MamanBar *self); { - self->priv = MAMAN_BAR_GET_PRIVATE (self); + self->priv = maman_bar_get_private (self); self->priv->an_object = g_object_new (MAMAN_TYPE_BAZ, NULL); self->priv->a_string = g_strdup ("Maman"); @@ -480,15 +480,10 @@ maman_bar_init (MamanBar *self); - Add similar code to your GObject, make sure the code still builds - and runs: dispose and finalize must be called during the last unref. - - - It is possible that object methods might be invoked after dispose is run and before finalize runs. GObject does not consider this to be a program error: you must gracefully detect this and neither crash nor - warn the user. + warn the user, by having a disposed instance revert to an inhert state. @@ -498,7 +493,7 @@ maman_bar_init (MamanBar *self); Just as with C++, there are many different ways to define object methods and extend them: the following list and sections draw on - C++ vocabulary. (Readers are expected to know basic C++ buzzwords. + C++ vocabulary. (Readers are expected to know basic C++ concepts. Those who have not had to write C++ code recently can refer to e.g. to refresh their memories.) @@ -537,8 +532,6 @@ maman_bar_do_action (MamanBar *self, /* parameters */) } - - There is really nothing scary about this. @@ -572,28 +565,7 @@ maman_bar_do_action (MamanBar *self, /* parameters */) } The code above simply redirects the do_action call to the relevant - class function. Some users, concerned about performance, do not - provide the maman_bar_do_action wrapper function - and require users to dereference the class pointer themselves. This - is not such a great idea in terms of encapsulation and makes it - difficult to change the object's implementation afterwards, should - this be needed. - - - - Other users, also concerned by performance issues, declare - the maman_bar_do_action function inline in the - header file. This, however, makes it difficult to change the - object's implementation later (although easier than requiring users - to directly dereference the class function) and is often difficult - to write in a portable way (the inline keyword - is part of the C99 standard but not every compiler supports it). - - - - In doubt, unless a user shows you hard numbers about the performance - cost of the function call, just implement maman_bar_do_action - in the source file. + class function. @@ -601,8 +573,8 @@ maman_bar_do_action (MamanBar *self, /* parameters */) implementation for this class method in the object's class_init function: initialize the klass->do_action field to a pointer to the actual implementation. - You can also make this class method pure virtual by initializing - the klass->do_action field to NULL: + By default, class method that are not inherited are initialized to + NULL, and thus are to be considered "pure virtual". static void maman_bar_real_do_action_two (MamanBar *self, /* parameters */) @@ -613,7 +585,10 @@ maman_bar_real_do_action_two (MamanBar *self, /* parameters */) static void maman_bar_class_init (BarClass *klass) { - /* pure virtual method: mandates implementation in children. */ + /* this is not necessary, except for demonstration purposes. + * + * pure virtual method: mandates implementation in children. + */ klass->do_action_one = NULL; /* merely virtual method. */ @@ -625,7 +600,17 @@ maman_bar_do_action_one (MamanBar *self, /* parameters */) { g_return_if_fail (MAMAN_IS_BAR (self)); - MAMAN_BAR_GET_CLASS (self)->do_action_one (self, /* parameters */); + /* if the method is purely virtual, then it is a good idea to + * check that it has been overridden before calling it, and, + * depending on the intent of the class, either ignore it silently + * or warn the user. + / + if (MAMAN_BAR_GET_CLASS (self)->do_action_one != NULL) + MAMAN_BAR_GET_CLASS (self)->do_action_one (self, /* parameters */); + else + g_warning ("Class '%s' does not override the mandatory " + "MamanBarClass.do_action_one() virtual function.", + G_OBJECT_TYPE_NAME (self)); } void @@ -728,21 +713,16 @@ maman_bar_subtype_class_init (MamanBarSubTypeClass *klass) Child class B re-implements method foo. In the method B::foo, the child class B calls its parent class method A::foo. - There are many uses to this idiom: + There are various uses to this idiom: - You need to change the behaviour of a class without modifying its code. You create + You need to extend the behaviour of a class without modifying its code. You create a subclass to inherit its implementation, re-implement a public virtual method to modify the behaviour - slightly and chain up to ensure that the previous behaviour is not really modified, just extended. + and chain up to ensure that the previous behaviour is not really modified, just extended. - You are lazy, you have access to the source code of the parent class but you don't want - to modify it to add method calls to new specialized method calls: it is faster to hack the child class - to chain up than to modify the parent to call down. You need to implement the Chain Of Responsibility pattern: each object of the inheritance tree chains up to its parent (typically, at the beginning or the end of the method) to ensure that they each handler is run in turn. - I am personally not really convinced any of the last two uses are really a good idea but since this - programming idiom is often used, this section attempts to explain how to implement it. @@ -763,22 +743,25 @@ maman_bar_subtype_class_init (MamanBarSubTypeClass *klass) - The function g_type_class_peek_parent is used to access the original parent - class structure. Its input is a pointer to the class of the derived object and it returns a pointer - to the original parent class structure. The code below shows how you could use it: + The function g_type_class_peek_parent + is used to access the original parent class structure. Its input is a + pointer to the class of the derived object and it returns a pointer to + the original parent class structure. Instead of using this function + directly, though, you should use the parent_class + pointer created and initialized for us by the G_DEFINE_TYPE_* family of + macros, for instance: static void b_method_to_call (B *obj, int a) { - BClass *klass; - AClass *parent_class; - - klass = B_GET_CLASS (obj); - parent_class = g_type_class_peek_parent (klass); - /* do stuff before chain up */ - parent_class->method_to_call (obj, a); + /* call the method_to_call() virtual function on the + * parent of BClass, AClass. + * + * remember the explicit cast to AClass* + */ + A_CLASS (b_parent_class)->method_to_call (obj, a); /* do stuff after chain up */ } @@ -963,7 +946,7 @@ static void maman_ibaz_interface_init (MamanIbazInterface *iface); G_DEFINE_TYPE_WITH_CODE (MamanBar, maman_bar, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (MAMAN_TYPE_IBAZ, - maman_ibaz_interface_init)); + maman_ibaz_interface_init)) This definition is very much like all the similar functions we looked at previously. The only interface-specific code present here is the call to @@ -1095,7 +1078,7 @@ G_DEFINE_TYPE_WITH_CODE (MamanBar, maman_bar, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (MAMAN_TYPE_IBAZ, maman_ibaz_interface_init) G_IMPLEMENT_INTERFACE (MAMAN_TYPE_IBAR, - maman_ibar_interface_init)); + maman_ibar_interface_init)) It is very important to notice that the order in which interface implementations are added to the main object is not random: @@ -1326,18 +1309,16 @@ maman_derived_ibaz_interface_init (MamanIbazInterface *iface) G_DEFINE_TYPE_WITH_CODE (MamanDerivedBaz, maman_derived_baz, MAMAN_TYPE_BAZ, G_IMPLEMENT_INTERFACE (MAMAN_TYPE_IBAZ, - maman_derived_ibaz_interface_init) + maman_derived_ibaz_interface_init)) static void maman_derived_baz_class_init (MamanDerivedBazClass *klass) { - } static void maman_derived_baz_init (MamanDerivedBaz *self) { - } @@ -1357,8 +1338,8 @@ maman_derived_baz_init (MamanDerivedBaz *self) exists) A Python callback can be connected to any signal on any - C-based GObject. - + C-based GObject, and vice versa, assuming that the Python object + inherits from GObject. to any signal and to stop the emission of any signal at any state of the signal emission process. This flexibility makes it