From b6b0f5f305eab272192f01bd5adb68adaa31c10b Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 20 Feb 2015 12:42:52 +0000 Subject: [PATCH] docs: Update GObject how-to for G_DECLARE_*_TYPE macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restructure the section of the how-to which covers the header and source code boilerplate for declaring and defining GObjects to use the new G_DECLARE_*_TYPE macros. Present both final and derivable types. Trim various supporting paragraphs. Rename ‘class functions’ to ‘virtual functions’ to use consistent, modern terminology. https://bugzilla.gnome.org/show_bug.cgi?id=744060 --- docs/reference/gobject/tut_howto.xml | 303 ++++++++++++++++----------- 1 file changed, 181 insertions(+), 122 deletions(-) diff --git a/docs/reference/gobject/tut_howto.xml b/docs/reference/gobject/tut_howto.xml index 97987fc51..42edfa645 100644 --- a/docs/reference/gobject/tut_howto.xml +++ b/docs/reference/gobject/tut_howto.xml @@ -7,8 +7,8 @@ This chapter tries to answer the real-life questions of users and presents - the most common scenario use cases I could come up with. - The use cases are presented from most likely to less likely. + the most common use cases in order from most likely to least + likely. @@ -30,24 +30,12 @@ The first step before writing the code for your GObject is to write the type's header which contains the needed type, function and macro definitions. Each of these elements is nothing but a convention which - is followed not only by GTK+'s code but also by most users of GObject. - If you feel the need not to obey the rules stated below, think about it - twice: - - If your users are a bit accustomed to GTK+ code or any - GLib code, they will be a bit surprised and getting used to the - conventions you decided upon will take time (money) and will make them - 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. - - 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. + is followed by almost all users of GObject, and has been refined over + multiple years of experience developing GObject-based code. If you are + writing a library, it is particularly important for you to adhere closely + to these conventions; users of your library will assume that you have. + Even if not writing a library, it will help other people who want to work + on your project. @@ -67,27 +55,28 @@ names easier for those with poor eyesight. - - When you need some private (internal) declarations in several - (sub)classes, you can define them in a private header file which - is often named by appending the private keyword - to the public header name. For example, one could use - maman-bar-private.h, - maman_bar_private.h or - mamanbarprivate.h. Typically, such private header - files are not installed. - - The basic conventions for any header which exposes a GType are described in . - If you want to declare a type named bar with prefix maman, name the type instance - MamanBar and its class MamanBarClass - (name is case-sensitive). It is customary to declare them with code similar to the - following: + If you want to declare a type named ‘bar’ in namespace ‘maman’, name the + type instance MamanBar and its class + MamanBarClass (names are case sensitive). The + recommended method of declaring a type differs based on whether the type + is final or derivable. + + + + Final types cannot be subclassed further, and should be the default choice + for new types — changing a final type to be derivable is always a change + that will be compatible with existing uses of the code, but the converse + will often cause problems. Final types are declared using + G_DECLARE_FINAL_TYPE, + and require a structure to hold the instance data to be declared in the + source code (not the header file). + /* * Copyright/Licensing information. @@ -102,77 +91,92 @@ * Potentially, include other headers on which this header depends. */ +G_BEGIN_DECLS + /* - * Type macros. + * Type declaration. */ -#define MAMAN_TYPE_BAR (maman_bar_get_type ()) -#define MAMAN_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_BAR, MamanBar)) -#define MAMAN_IS_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_BAR)) -#define MAMAN_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MAMAN_TYPE_BAR, MamanBarClass)) -#define MAMAN_IS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MAMAN_TYPE_BAR)) -#define MAMAN_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MAMAN_TYPE_BAR, MamanBarClass)) - -typedef struct _MamanBar MamanBar; -typedef struct _MamanBarClass MamanBarClass; - -struct _MamanBar -{ - /* Parent instance structure */ - GObject parent_instance; - - /* instance members */ -}; - -struct _MamanBarClass -{ - /* Parent class structure */ - GObjectClass parent_class; - - /* class members */ -}; - -/* used by MAMAN_TYPE_BAR */ -GType maman_bar_get_type (void); +#define MAMAN_TYPE_BAR maman_bar_get_type () +G_DECLARE_FINAL_TYPE (MamanBar, maman_bar, MAMAN, BAR, GObject) /* * Method definitions. */ +MamanBar *maman_bar_new (void); + +G_END_DECLS #endif /* __MAMAN_BAR_H__ */ - Finally, there are different header include conventions. Again, pick one - and stick to it. I personally use indifferently any of the two, depending - on the codebase I work on: the rule, as always, is consistency. - - - Some people add at the top of their headers a number of #include - directives to pull in all the headers needed to compile client - code. This allows client code to simply #include "maman-bar.h". - - - Other do not #include anything and expect the client to #include - themselves the headers they need before including your header. This - speeds up compilation because it minimizes the amount of - pre-processor work. This can be used in conjunction with the - re-declaration of certain unused types in the client code to - minimize compile-time dependencies and thus speed up compilation. - - + Derivable types can be subclassed further, and their class and + instance structures form part of the public API which must not be changed + if API stability is cared about. They are declared using + G_DECLARE_DERIVABLE_TYPE: + +/* + * Copyright/Licensing information. + */ + +/* inclusion guard */ +#ifndef __MAMAN_BAR_H__ +#define __MAMAN_BAR_H__ + +#include <glib-object.h> +/* + * Potentially, include other headers on which this header depends. + */ + +G_BEGIN_DECLS + +/* + * Type declaration. + */ +#define MAMAN_TYPE_BAR maman_bar_get_type () +G_DECLARE_DERIVABLE_TYPE (MamanBar, maman_bar, MAMAN, BAR, GObject) + +struct _MamanBarClass +{ + GObjectClass parent_class; + + /* Class virtual function fields. */ + void (* handle_frob) (MamanBar *bar, + guint n_frobs); + + /* Padding to allow adding up to 12 new virtual functions without + * breaking ABI. */ + gpointer padding[12]; +}; + +/* + * Method definitions. + */ +MamanBar *maman_bar_new (void); + +G_END_DECLS + +#endif /* __MAMAN_BAR_H__ */ + + + + + The convention for header includes is to add the minimum number of + #include directives to the top of your headers needed + to compile that header. This + allows client code to simply #include "maman-bar.h", + without needing to know the prerequisites for + maman-bar.h. - Boilerplate code - In your code, the first step is to #include the needed headers: depending - on your header include strategy, this can be as simple as - #include "maman-bar.h" or as complicated as tens - of #include lines ending with #include "maman-bar.h": + In your code, the first step is to #include the + needed headers: /* * Copyright information @@ -180,15 +184,11 @@ GType maman_bar_get_type (void); #include "maman-bar.h" -/* If you use Pimpls, include the private structure - * definition here. Some people create a maman-bar-private.h header - * which is included by the maman-bar.c file and which contains the - * definition for this private structure. - */ -struct _MamanBarPrivate { - int member_1; +/* Private structure definition. */ +typedef struct { + gint member1; /* stuff */ -}; +} MamanBarPrivate; /* * forward definitions @@ -197,7 +197,24 @@ struct _MamanBarPrivate { - Call the G_DEFINE_TYPE macro using the name + If the class is being declared as final using + G_DECLARE_FINAL_TYPE, its instance structure should + be defined in the C file: + +struct _MamanBar +{ + GObject parent_instance; + + /* Other members, including private data. */ +} + + + + + Call the G_DEFINE_TYPE macro (or + G_DEFINE_TYPE_WITH_PRIVATE if your class needs + private data — final types do not need private data) + using the name of the type, the prefix of the functions and the parent GType to reduce the amount of boilerplate needed. This macro will: @@ -206,21 +223,40 @@ struct _MamanBarPrivate { function define a parent class pointer accessible from the whole .c file + add private instance data to the type (if using + G_DEFINE_TYPE_WITH_PRIVATE) + + + + If the class has been declared as final using + G_DECLARE_FINAL_TYPE (see + ), private data should be placed in + the instance structure, MamanBar, and + G_DEFINE_TYPE should be used instead of + G_DEFINE_TYPE_WITH_PRIVATE. The instance structure + for a final class is not exposed publicly, and is not embedded in the + instance structures of any derived classes (because the class is final); + so its size can vary without causing incompatibilities for code which uses + the class. Conversely, private data for derivable classes + must be included in a private structure, and + G_DEFINE_TYPE_WITH_PRIVATE must be used. G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT) + +or + +G_DEFINE_TYPE_WITH_PRIVATE (MamanBar, maman_bar, G_TYPE_OBJECT) It is also possible to use the 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, - or call the G_ADD_PRIVATE macro will add an - instance private data structure. + get_type function implementation — for instance, to + add a call to the G_IMPLEMENT_INTERFACE macro to + implement an interface. @@ -477,11 +513,34 @@ maman_bar_do_action (MamanBar *self, /* parameters */) Virtual public methods - This is the preferred way to create polymorphic GObjects. All you - need to do is to define the common method and its class function in - the public header, implement the common method in the source file - and re-implement the class function in each object which inherits - from you. + This is the preferred way to create GObjects with overridable methods: + + + Define the common method and its virtual function in the + class structure in the public header + + + Define the common method in the header file and implement it in the + source file + + + Implement a base version of the virtual function in the source + file and initialize the virtual function pointer to this + implementation in the object’s class_init + function; or leave it as NULL for a ‘pure + virtual’ method which must be overridden by derived classes + + + Re-implement the virtual function in each derived class which needs + to override it + + + + + Note that virtual functions can only be defined if the class is + derivable, declared using + G_DECLARE_DERIVABLE_TYPE + so the class structure can be defined. /* declaration in maman-bar.h. */ struct _MamanBarClass @@ -503,8 +562,8 @@ maman_bar_do_action (MamanBar *self, /* parameters */) MAMAN_BAR_GET_CLASS (self)->do_action (self, /* parameters */); } - The code above simply redirects the do_action call to the relevant - class function. + The code above simply redirects the do_action call + to the relevant virtual function. @@ -569,7 +628,7 @@ maman_bar_do_action_two (MamanBar *self, /* parameters */) These are very similar to Virtual Public methods. They just don't have a public function to call the function directly. The header - file contains only a declaration of the class function: + file contains only a declaration of the virtual function: /* declaration in maman-bar.h. */ struct _MamanBarClass @@ -582,7 +641,7 @@ struct _MamanBarClass void maman_bar_do_any_action (MamanBar *self, /* parameters */); - These class functions are often used to delegate part of the job + These virtual functions are often used to delegate part of the job to child classes: /* this accessor function is static: it is not exported outside of this file. */ @@ -611,7 +670,7 @@ maman_bar_do_any_action (MamanBar *self, /* parameters */) Again, it is possible to provide a default implementation for this - private virtual class function: + private virtual function: static void maman_bar_class_init (MamanBarClass *klass) @@ -633,7 +692,7 @@ maman_bar_subtype_class_init (MamanBarSubTypeClass *klass) { MamanBarClass *bar_class = MAMAN_BAR_CLASS (klass); - /* implement pure virtual class function. */ + /* implement pure virtual function. */ bar_class->do_specific_action_one = maman_bar_subtype_do_specific_action_one; } @@ -667,7 +726,7 @@ maman_bar_subtype_class_init (MamanBarSubTypeClass *klass) To explicitly chain up to the implementation of the virtual method in the parent class, you first need a handle to the original parent class structure. This pointer can then be used to - access the original class function pointer and invoke it directly. + access the original virtual function pointer and invoke it directly. The original adjective used in this sentence is not innocuous. To fully @@ -1662,7 +1721,7 @@ klass->write_signal_id = - If the signal's default handler is just a class function pointer, it is also possible to override + If the signal's default handler is just a virtual function pointer, it is also possible to override it yourself from the class_init function of a type which derives from the parent. That way, when the signal is emitted, the parent class will use the function provided by the child as a signal default handler. Of course, it is also possible (and recommended) to chain up from the child to the parent's default signal @@ -1693,9 +1752,9 @@ klass->write_signal_id = Usually, the g_signal_new function is preferred over g_signal_newv. When g_signal_new - is used, the default closure is exported as a class function. For example, + is used, the default closure is exported as a virtual function. For example, gobject.h contains the declaration of GObjectClass - whose notify class function is the default handler for the notify + whose notify virtual function is the default handler for the notify signal: struct _GObjectClass @@ -1713,7 +1772,7 @@ struct _GObjectClass gobject.c's g_object_do_class_init function - registers the notify signal and initializes this class function + registers the notify signal and initializes this virtual function to NULL: static void @@ -1736,24 +1795,24 @@ g_object_do_class_init (GObjectClass *class) } g_signal_new creates a GClosure which dereferences the - type's class structure to access the class function pointer and invoke it if it not NULL. The - class function is ignored it is set to NULL. + type's class structure to access the virtual function pointer and invoke it if it not NULL. The + virtual function is ignored it is set to NULL. To understand the reason for such a complex scheme to access the signal's default handler, you must remember the whole reason for the use of these signals. The goal here is to delegate a part of the process to the user without requiring the user to subclass the object to override - one of the class functions. The alternative to subclassing, that is, the use of signals + one of the virtual functions. The alternative to subclassing, that is, the use of signals to delegate processing to the user, is, however, a bit less optimal in terms of speed: rather than just dereferencing a function pointer in a class structure, you must start the whole process of signal emission which is a bit heavyweight. - This is why some people decided to use class functions for some signal's default handlers: + This is why some people decided to use virtual functions for some signal's default handlers: rather than having users connect a handler to the signal and stop the signal emission - from within that handler, you just need to override the default class function which is + from within that handler, you just need to override the default virtual function which is supposedly more efficient.