diff --git a/docs/reference/gobject/gobject-docs.xml b/docs/reference/gobject/gobject-docs.xml index ecac914d7..cb7a74229 100644 --- a/docs/reference/gobject/gobject-docs.xml +++ b/docs/reference/gobject/gobject-docs.xml @@ -43,7 +43,6 @@ - diff --git a/docs/reference/gobject/gobject.toml.in b/docs/reference/gobject/gobject.toml.in index 2e879e1aa..881971449 100644 --- a/docs/reference/gobject/gobject.toml.in +++ b/docs/reference/gobject/gobject.toml.in @@ -43,6 +43,7 @@ urlmap_file = "urlmap.js" # The same order will be used when generating the index content_files = [ "concepts.md", + "tutorial.md", "floating-refs.md", "boxed.md", "enum-types.md", diff --git a/docs/reference/gobject/meson.build b/docs/reference/gobject/meson.build index 30c2b6d99..3a608ddfe 100644 --- a/docs/reference/gobject/meson.build +++ b/docs/reference/gobject/meson.build @@ -38,7 +38,6 @@ if get_option('gtk_doc') 'glib-mkenums.xml', 'glib-genmarshal.xml', 'gobject-query.xml', - 'tut_howto.xml', 'tut_tools.xml' ], html_assets : [ @@ -72,6 +71,7 @@ expand_content_files = [ 'enum-types.md', 'floating-refs.md', 'gvalue.md', + 'tutorial.md', ] gobject_gir = meson.current_source_dir() / 'GObject-2.0.gir' diff --git a/docs/reference/gobject/tut_howto.xml b/docs/reference/gobject/tut_howto.xml deleted file mode 100644 index 82419239a..000000000 --- a/docs/reference/gobject/tut_howto.xml +++ /dev/null @@ -1,1535 +0,0 @@ - - - - Tutorial - - - This chapter tries to answer the real-life questions of users and presents - the most common use cases in order from most likely to least - likely. - - - - - How to define and implement a new GObject - - - This chapter focuses on the implementation of a subtype of GObject, for - example to create a custom class hierarchy, or to subclass a GTK widget. - - - - Throughout the chapter, a running example of a file viewer program is used, - which has a ViewerFile class to represent a single file being - viewed, and various derived classes for different types of files with - special functionality, such as audio files. The example application also - supports editing files (for example, to tweak a photo being viewed), using - a ViewerEditable interface. - - - - Boilerplate header code - - - 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 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. - - - - Pick a name convention for your headers and source code and stick to it: - - use a dash to separate the prefix from the typename: - viewer-file.h and viewer-file.c - (this is the convention used by Nautilus and most GNOME libraries). - use an underscore to separate the prefix from the - typename: viewer_file.h and - viewer_file.c. - Do not separate the prefix from the typename: - viewerfile.h and viewerfile.c. - (this is the convention used by GTK) - - Some people like the first two solutions better: it makes reading file - names easier for those with poor eyesight. - - - - The basic conventions for any header which exposes a GType are described - in . - - - - If you want to declare a type named ‘file’ in namespace ‘viewer’, name the - type instance ViewerFile and its class - ViewerFileClass (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. - */ - -/* inclusion guard */ -#ifndef __VIEWER_FILE_H__ -#define __VIEWER_FILE_H__ - -#include <glib-object.h> -/* - * Potentially, include other headers on which this header depends. - */ - -G_BEGIN_DECLS - -/* - * Type declaration. - */ -#define VIEWER_TYPE_FILE viewer_file_get_type () -G_DECLARE_FINAL_TYPE (ViewerFile, viewer_file, VIEWER, FILE, GObject) - -/* - * Method definitions. - */ -ViewerFile *viewer_file_new (void); - -G_END_DECLS - -#endif /* __VIEWER_FILE_H__ */ - - - - - 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 __VIEWER_FILE_H__ -#define __VIEWER_FILE_H__ - -#include <glib-object.h> -/* - * Potentially, include other headers on which this header depends. - */ - -G_BEGIN_DECLS - -/* - * Type declaration. - */ -#define VIEWER_TYPE_FILE viewer_file_get_type () -G_DECLARE_DERIVABLE_TYPE (ViewerFile, viewer_file, VIEWER, FILE, GObject) - -struct _ViewerFileClass -{ - GObjectClass parent_class; - - /* Class virtual function fields. */ - void (* open) (ViewerFile *file, - GError **error); - - /* Padding to allow adding up to 12 new virtual functions without - * breaking ABI. */ - gpointer padding[12]; -}; - -/* - * Method definitions. - */ -ViewerFile *viewer_file_new (void); - -G_END_DECLS - -#endif /* __VIEWER_FILE_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 "viewer-file.h", - without needing to know the prerequisites for - viewer-file.h. - - - - - Boilerplate code - - - In your code, the first step is to #include the - needed headers: - -/* - * Copyright information - */ - -#include "viewer-file.h" - -/* Private structure definition. */ -typedef struct { - gchar *filename; - /* stuff */ -} ViewerFilePrivate; - -/* - * forward definitions - */ - - - - - If the class is being declared as final using - G_DECLARE_FINAL_TYPE, its instance structure should - be defined in the C file: - -struct _ViewerFile -{ - 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: - - - implement the viewer_file_get_type - 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, ViewerFile, 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 (ViewerFile, viewer_file, G_TYPE_OBJECT) - -or - -G_DEFINE_TYPE_WITH_PRIVATE (ViewerFile, viewer_file, 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 the G_IMPLEMENT_INTERFACE macro to - implement an interface. - - - - - Object construction - - - People often get confused when trying to construct their GObjects because of the - sheer number of different ways to hook into the objects's construction process: it is - difficult to figure which is the correct, recommended way. - - - - shows what user-provided functions - are invoked during object instantiation and in which order they are invoked. - A user looking for the equivalent of the simple C++ constructor function should use - the instance_init method. It will be invoked after - all the parents’ instance_init - functions have been invoked. It cannot take arbitrary construction parameters - (as in C++) but if your object needs arbitrary parameters to complete initialization, - you can use construction properties. - - - - Construction properties will be set only after all - instance_init functions have run. - No object reference will be returned to the client of g_object_new - until all the construction properties have been set. - - - - 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. - - - - You should write the following code first: - -G_DEFINE_TYPE_WITH_PRIVATE (ViewerFile, viewer_file, G_TYPE_OBJECT) - -static void -viewer_file_class_init (ViewerFileClass *klass) -{ -} - -static void -viewer_file_init (ViewerFile *self) -{ - ViewerFilePrivate *priv = viewer_file_get_instance_private (self); - - /* initialize all public and private members to reasonable default values. - * They are all automatically initialized to 0 to begin with. */ -} - - - - - If you need special construction properties (with - G_PARAM_CONSTRUCT_ONLY - set), 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 . - - - - Property IDs must start from 1, as 0 is reserved for internal use by - GObject. - -enum -{ - PROP_FILENAME = 1, - PROP_ZOOM_LEVEL, - N_PROPERTIES -}; - -static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; - -static void -viewer_file_class_init (ViewerFileClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->set_property = viewer_file_set_property; - object_class->get_property = viewer_file_get_property; - - obj_properties[PROP_FILENAME] = - g_param_spec_string ("filename", - "Filename", - "Name of the file to load and display from.", - NULL /* default value */, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); - - obj_properties[PROP_ZOOM_LEVEL] = - g_param_spec_uint ("zoom-level", - "Zoom level", - "Zoom level to view the file at.", - 0 /* minimum value */, - 10 /* maximum value */, - 2 /* default value */, - G_PARAM_READWRITE); - - g_object_class_install_properties (object_class, - N_PROPERTIES, - obj_properties); -} - - 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 complete the initialization of an 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. - 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. - - - - - Object destruction - - - Again, it is often difficult to figure out which mechanism to use to - hook into the object's destruction process: when the last - g_object_unref - function call is made, a lot of things happen as described in - . - - - - The destruction process of your object is in two phases: dispose and - 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 revival of - instances in case of signal emission during the destruction sequence. - See for more information. - -struct _ViewerFilePrivate -{ - gchar *filename; - guint zoom_level; - - GInputStream *input_stream; -}; - -G_DEFINE_TYPE_WITH_PRIVATE (ViewerFile, viewer_file, G_TYPE_OBJECT) - -static void -viewer_file_dispose (GObject *gobject) -{ - ViewerFilePrivate *priv = viewer_file_get_instance_private (VIEWER_FILE (gobject)); - - /* 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 by setting the member - * NULL; g_clear_object() does this for us. - */ - g_clear_object (&priv->input_stream); - - /* 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 (viewer_file_parent_class)->dispose (gobject); -} - -static void -viewer_file_finalize (GObject *gobject) -{ - ViewerFilePrivate *priv = viewer_file_get_instance_private (VIEWER_FILE (gobject)); - - g_free (priv->filename); - - /* 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 (viewer_file_parent_class)->finalize (gobject); -} - -static void -viewer_file_class_init (ViewerFileClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = viewer_file_dispose; - object_class->finalize = viewer_file_finalize; -} - -static void -viewer_file_init (ViewerFile *self); -{ - ViewerFilePrivate *priv = viewer_file_get_instance_private (self); - - priv->input_stream = g_object_new (VIEWER_TYPE_INPUT_STREAM, NULL); - priv->filename = /* would be set as a property */; -} - - - - - 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, by having a disposed instance revert to an inert state. - - - - - Object methods - - - 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++ concepts. - Those who have not had to write C++ code recently can refer to e.g. - to refresh - their memories.) - - - non-virtual public methods, - - - virtual public methods and - - - virtual private methods - - - - - - Non-virtual public methods - - - These are the simplest, providing a simple method which - acts on the object. Provide a function - prototype in the header and an implementation of that prototype - in the source file. - -/* declaration in the header. */ -void viewer_file_open (ViewerFile *self, - GError **error); - -/* implementation in the source file */ -void -viewer_file_open (ViewerFile *self, - GError **error) -{ - g_return_if_fail (VIEWER_IS_FILE (self)); - g_return_if_fail (error == NULL || *error == NULL); - - /* do stuff here. */ -} - - - - - - Virtual public methods - - - 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 viewer-file.h. */ -#define VIEWER_TYPE_FILE viewer_file_get_type () -G_DECLARE_DERIVABLE_TYPE (ViewerFile, viewer_file, VIEWER, FILE, GObject) - -struct _ViewerFileClass -{ - GObjectClass parent_class; - - /* stuff */ - void (*open) (ViewerFile *self, - GError **error); - - /* Padding to allow adding up to 12 new virtual functions without - * breaking ABI. */ - gpointer padding[12]; -}; - -void viewer_file_open (ViewerFile *self, - GError **error); - -/* implementation in viewer-file.c */ -void -viewer_file_open (ViewerFile *self, - GError **error) -{ - ViewerFileClass *klass; - - g_return_if_fail (VIEWER_IS_FILE (self)); - g_return_if_fail (error == NULL || *error == NULL); - - klass = VIEWER_FILE_GET_CLASS (self); - g_return_if_fail (klass->open != NULL); - - klass->open (self, error); -} - - The code above simply redirects the open call - to the relevant virtual function. - - - - It is possible to provide a default - implementation for this class method in the object's - class_init function: initialize the - klass->open field to a pointer to the - actual implementation. - By default, class methods that are not inherited are initialized to - NULL, and thus are to be considered "pure virtual". - -static void -viewer_file_real_close (ViewerFile *self, - GError **error) -{ - /* Default implementation for the virtual method. */ -} - -static void -viewer_file_class_init (ViewerFileClass *klass) -{ - /* this is not necessary, except for demonstration purposes. - * - * pure virtual method: mandates implementation in children. - */ - klass->open = NULL; - - /* merely virtual method. */ - klass->close = viewer_file_real_close; -} - -void -viewer_file_open (ViewerFile *self, - GError **error) -{ - ViewerFileClass *klass; - - g_return_if_fail (VIEWER_IS_FILE (self)); - g_return_if_fail (error == NULL || *error == NULL); - - klass = VIEWER_FILE_GET_CLASS (self); - - /* 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. - */ - g_return_if_fail (klass->open != NULL); - klass->open (self, error); -} - -void -viewer_file_close (ViewerFile *self, - GError **error) -{ - ViewerFileClass *klass; - - g_return_if_fail (VIEWER_IS_FILE (self)); - g_return_if_fail (error == NULL || *error == NULL); - - klass = VIEWER_FILE_GET_CLASS (self); - if (klass->close != NULL) - klass->close (self, error); -} - - - - - - Virtual private Methods - - - These are very similar to virtual - public methods. They just don't - have a public function to call directly. The header - file contains only a declaration of the virtual function: - -/* declaration in viewer-file.h. */ -struct _ViewerFileClass -{ - GObjectClass parent; - - /* Public virtual method as before. */ - void (*open) (ViewerFile *self, - GError **error); - - /* Private helper function to work out whether the file can be loaded via - * memory mapped I/O, or whether it has to be read as a stream. */ - gboolean (*can_memory_map) (ViewerFile *self); - - /* Padding to allow adding up to 12 new virtual functions without - * breaking ABI. */ - gpointer padding[12]; -}; - -void viewer_file_open (ViewerFile *self, GError **error); - - 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. */ -static gboolean -viewer_file_can_memory_map (ViewerFile *self) -{ - return VIEWER_FILE_GET_CLASS (self)->can_memory_map (self); -} - -void -viewer_file_open (ViewerFile *self, - GError **error) -{ - g_return_if_fail (VIEWER_IS_FILE (self)); - g_return_if_fail (error == NULL || *error == NULL); - - /* - * Try to load the file using memory mapped I/O, if the implementation of the - * class determines that is possible using its private virtual method. - */ - if (viewer_file_can_memory_map (self)) - { - /* Load the file using memory mapped I/O. */ - } - else - { - /* Fall back to trying to load the file using streaming I/O… */ - } -} - - - - - Again, it is possible to provide a default implementation for this - private virtual function: - -static gboolean -viewer_file_real_can_memory_map (ViewerFile *self) -{ - /* As an example, always return false. Or, potentially return true if the - * file is local. */ - return FALSE; -} - -static void -viewer_file_class_init (ViewerFileClass *klass) -{ - /* non-pure virtual method; does not have to be implemented in children. */ - klass->can_memory_map = viewer_file_real_can_memory_map; -} - - - - - Derived classes can then override the method with code such as: - -static void -viewer_audio_file_class_init (ViewerAudioFileClass *klass) -{ - ViewerFileClass *file_class = VIEWER_FILE_CLASS (klass); - - /* implement pure virtual function. */ - file_class->can_memory_map = viewer_audio_file_can_memory_map; -} - - - - - - - Chaining up - - Chaining up is often loosely defined by the following set of - conditions: - - Parent class A defines a public virtual method named foo and - provides a default implementation. - Child class B re-implements method foo. - B’s implementation of foo calls (‘chains up to’) its parent class A’s implementation of foo. - - There are various uses of this idiom: - - 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 - and chain up to ensure that the previous behaviour is not really modified, just extended. - - 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 - each handler is run in turn. - - - - - 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 virtual function pointer and invoke it directly. - - - The original adjective used in this sentence is not innocuous. To fully - understand its meaning, recall how class structures are initialized: for each object type, - the class structure associated with this object is created by first copying the class structure of its - parent type (a simple memcpy) and then by invoking the class_init callback on - the resulting class structure. Since the class_init callback is responsible for overwriting the class structure - with the user re-implementations of the class methods, the modified copy of the parent class - structure stored in the derived instance cannot be used. A copy of the class structure of an instance of the parent - class is needed. - - - - - - Use the parent_class pointer created and initialized - by the - G_DEFINE_TYPE - family of macros, for instance: - -static void -b_method_to_call (B *obj, gint some_param) -{ - /* do stuff before chain up */ - - /* 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, some_param); - - /* do stuff after chain up */ -} - - - - - - - - - - How to define and implement interfaces - - - Defining interfaces - - - The theory behind how GObject interfaces work is given in - ; this section covers how to - define and implement an interface. - - - - The first step is to get the header right. This interface - defines three methods: - -/* - * Copyright/Licensing information. - */ - -#ifndef __VIEWER_EDITABLE_H__ -#define __VIEWER_EDITABLE_H__ - -#include <glib-object.h> - -G_BEGIN_DECLS - -#define VIEWER_TYPE_EDITABLE viewer_editable_get_type () -G_DECLARE_INTERFACE (ViewerEditable, viewer_editable, VIEWER, EDITABLE, GObject) - -struct _ViewerEditableInterface -{ - GTypeInterface parent_iface; - - void (*save) (ViewerEditable *self, - GError **error); - void (*undo) (ViewerEditable *self, - guint n_steps); - void (*redo) (ViewerEditable *self, - guint n_steps); -}; - -void viewer_editable_save (ViewerEditable *self, - GError **error); -void viewer_editable_undo (ViewerEditable *self, - guint n_steps); -void viewer_editable_redo (ViewerEditable *self, - guint n_steps); - -G_END_DECLS - -#endif /* __VIEWER_EDITABLE_H__ */ - - This code is the same as the code for a normal GType - which derives from a GObject except for a few details: - - - The _GET_CLASS function is called - _GET_IFACE (and is defined by - G_DECLARE_INTERFACE). - - - The instance type, ViewerEditable, is not fully defined: it is - used merely as an abstract type which represents an instance of - whatever object which implements the interface. - - - The parent of the ViewerEditableInterface is - GTypeInterface, not GObjectClass. - - - - - - The implementation of the ViewerEditable type itself is trivial: - - G_DEFINE_INTERFACE - creates a viewer_editable_get_type function which registers the - type in the type system. The third argument is used to define a - prerequisite interface - (which we'll talk about more later). Just pass 0 for this - argument when an interface has no prerequisite. - - viewer_editable_default_init is expected - to register the interface's signals if there are any (we will see a bit - later how to use them). - The interface methods viewer_editable_save, - viewer_editable_undo and viewer_editable_redo dereference the interface - structure to access its associated interface function and call it. - - - -G_DEFINE_INTERFACE (ViewerEditable, viewer_editable, G_TYPE_OBJECT) - -static void -viewer_editable_default_init (ViewerEditableInterface *iface) -{ - /* add properties and signals to the interface here */ -} - -void -viewer_editable_save (ViewerEditable *self, - GError **error) -{ - ViewerEditableInterface *iface; - - g_return_if_fail (VIEWER_IS_EDITABLE (self)); - g_return_if_fail (error == NULL || *error == NULL); - - iface = VIEWER_EDITABLE_GET_IFACE (self); - g_return_if_fail (iface->save != NULL); - iface->save (self, error); -} - -void -viewer_editable_undo (ViewerEditable *self, - guint n_steps) -{ - ViewerEditableInterface *iface; - - g_return_if_fail (VIEWER_IS_EDITABLE (self)); - - iface = VIEWER_EDITABLE_GET_IFACE (self); - g_return_if_fail (iface->undo != NULL); - iface->undo (self, n_steps); -} - -void -viewer_editable_redo (ViewerEditable *self, - guint n_steps) -{ - ViewerEditableInterface *iface; - - g_return_if_fail (VIEWER_IS_EDITABLE (self)); - - iface = VIEWER_EDITABLE_GET_IFACE (self); - g_return_if_fail (iface->redo != NULL); - iface->redo (self, n_steps); -} - - - - - - Implementing interfaces - - - Once the interface is defined, implementing it is rather trivial. - - - - The first step is to define a normal final GObject class exactly as in - . - - - - The second step is to implement ViewerFile by defining - it using - G_DEFINE_TYPE_WITH_CODE - and - G_IMPLEMENT_INTERFACE - instead of - G_DEFINE_TYPE: - -static void viewer_file_editable_interface_init (ViewerEditableInterface *iface); - -G_DEFINE_TYPE_WITH_CODE (ViewerFile, viewer_file, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE, - viewer_file_editable_interface_init)) - - This definition is very much like all the similar functions seen - previously. The only interface-specific code present here is the use of - G_IMPLEMENT_INTERFACE. - - - Classes can implement multiple interfaces by using multiple calls to - G_IMPLEMENT_INTERFACE - inside the call to - G_DEFINE_TYPE_WITH_CODE - - - - viewer_file_editable_interface_init, the interface - initialization function: inside it every virtual method of the interface - must be assigned to its implementation: - -static void -viewer_file_editable_save (ViewerFile *self, - GError **error) -{ - g_print ("File implementation of editable interface save method: %s.\n", - self->filename); -} - -static void -viewer_file_editable_undo (ViewerFile *self, - guint n_steps) -{ - g_print ("File implementation of editable interface undo method: %s.\n", - self->filename); -} - -static void -viewer_file_editable_redo (ViewerFile *self, - guint n_steps) -{ - g_print ("File implementation of editable interface redo method: %s.\n", - self->filename); -} - -static void -viewer_file_editable_interface_init (ViewerEditableInterface *iface) -{ - iface->save = viewer_file_editable_save; - iface->undo = viewer_file_editable_undo; - iface->redo = viewer_file_editable_redo; -} - -static void -viewer_file_init (ViewerFile *self) -{ - /* Instance variable initialisation code. */ -} - - - - If the object is not of final type, e.g. was declared using - G_DECLARE_DERIVABLE_TYPE - then - G_ADD_PRIVATE - macro should be added. The private structure should be declared exactly - as for a normal derivable object, see . - -G_DEFINE_TYPE_WITH_CODE (ViewerFile, viewer_file, G_TYPE_OBJECT, - G_ADD_PRIVATE (ViewerFile) - G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE, - viewer_file_editable_interface_init)) - - - - - - Interface definition prerequisites - - - To specify that an interface requires the presence of other interfaces - when implemented, GObject introduces the concept of - prerequisites: it is possible to associate - a list of prerequisite types to an interface. For example, if - object A wishes to implement interface I1, and if interface I1 has a - prerequisite on interface I2, A has to implement both I1 and I2. - - - - The mechanism described above is, in practice, very similar to - Java's interface I1 extends interface I2. The example below shows - the GObject equivalent: - -/* Make the ViewerEditableLossy interface require ViewerEditable interface. */ -G_DEFINE_INTERFACE (ViewerEditableLossy, viewer_editable_lossy, VIEWER_TYPE_EDITABLE) - - In the G_DEFINE_INTERFACE - call above, the third parameter defines the prerequisite type. This - is the GType of either an interface or a class. In this case - the ViewerEditable interface is a prerequisite of - ViewerEditableLossy. The code - below shows how an implementation can implement both interfaces and - register their implementations: - -static void -viewer_file_editable_lossy_compress (ViewerEditableLossy *editable) -{ - ViewerFile *self = VIEWER_FILE (editable); - - g_print ("File implementation of lossy editable interface compress method: %s.\n", - self->filename); -} - -static void -viewer_file_editable_lossy_interface_init (ViewerEditableLossyInterface *iface) -{ - iface->compress = viewer_file_editable_lossy_compress; -} - -static void -viewer_file_editable_save (ViewerEditable *editable, - GError **error) -{ - ViewerFile *self = VIEWER_FILE (editable); - - g_print ("File implementation of editable interface save method: %s.\n", - self->filename); -} - -static void -viewer_file_editable_undo (ViewerEditable *editable, - guint n_steps) -{ - ViewerFile *self = VIEWER_FILE (editable); - - g_print ("File implementation of editable interface undo method: %s.\n", - self->filename); -} - -static void -viewer_file_editable_redo (ViewerEditable *editable, - guint n_steps) -{ - ViewerFile *self = VIEWER_FILE (editable); - - g_print ("File implementation of editable interface redo method: %s.\n", - self->filename); -} - -static void -viewer_file_editable_interface_init (ViewerEditableInterface *iface) -{ - iface->save = viewer_file_editable_save; - iface->undo = viewer_file_editable_undo; - iface->redo = viewer_file_editable_redo; -} - -static void -viewer_file_class_init (ViewerFileClass *klass) -{ - /* Nothing here. */ -} - -static void -viewer_file_init (ViewerFile *self) -{ - /* Instance variable initialisation code. */ -} - -G_DEFINE_TYPE_WITH_CODE (ViewerFile, viewer_file, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE, - viewer_file_editable_interface_init) - G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE_LOSSY, - viewer_file_editable_lossy_interface_init)) - - It is very important to notice that the order in which interface - implementations are added to the main object is not random: - g_type_add_interface_static, - which is called by - G_IMPLEMENT_INTERFACE, - must be invoked first on the interfaces which have no prerequisites and then on - the others. - - - - - Interface properties - - - GObject interfaces can also have - properties. Declaration of the interface properties is similar to - declaring the properties of ordinary GObject types as explained in - , except that - g_object_interface_install_property - is used to declare the properties instead of - g_object_class_install_property. - - - - To include a property named 'autosave-frequency' of type gdouble in the - ViewerEditable interface example code above, we only need to - add one call in viewer_editable_default_init as shown - below: - -static void -viewer_editable_default_init (ViewerEditableInterface *iface) -{ - g_object_interface_install_property (iface, - g_param_spec_double ("autosave-frequency", - "Autosave frequency", - "Frequency (in per-seconds) to autosave backups of the editable content at. " - "Or zero to disable autosaves.", - 0.0, /* minimum */ - G_MAXDOUBLE, /* maximum */ - 0.0, /* default */ - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -} - - - - - One point worth noting is that the declared property wasn't assigned an - integer ID. The reason being that integer IDs of properties are used - only inside the get_property and - set_property virtual methods. Since interfaces - declare but do not implement properties, there is no - need to assign integer IDs to them. - - - - An implementation declares and defines its properties in the usual - way as explained in , except for one - small change: it can declare the properties of the interface it - implements using g_object_class_override_property - instead of g_object_class_install_property. - The following code snippet shows the modifications needed in the - ViewerFile declaration and implementation above: - -struct _ViewerFile -{ - GObject parent_instance; - - gdouble autosave_frequency; -}; - -enum -{ - PROP_AUTOSAVE_FREQUENCY = 1, - N_PROPERTIES -}; - -static void -viewer_file_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - ViewerFile *file = VIEWER_FILE (object); - - switch (prop_id) - { - case PROP_AUTOSAVE_FREQUENCY: - file->autosave_frequency = g_value_get_double (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -viewer_file_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - ViewerFile *file = VIEWER_FILE (object); - - switch (prop_id) - { - case PROP_AUTOSAVE_FREQUENCY: - g_value_set_double (value, file->autosave_frequency); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -viewer_file_class_init (ViewerFileClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->set_property = viewer_file_set_property; - object_class->get_property = viewer_file_get_property; - - g_object_class_override_property (object_class, PROP_AUTOSAVE_FREQUENCY, "autosave-frequency"); -} - - - - - - - Overriding interface methods - - - If a base class already implements an interface and a derived - class needs to implement the same interface but needs to override certain - methods, you must reimplement the interface and set only the interface - methods which need overriding. - - - - In this example, ViewerAudioFile is derived from - ViewerFile. Both implement the ViewerEditable - interface. ViewerAudioFile only implements one method of the - ViewerEditable interface and uses the base class implementation of - the other. - -static void -viewer_audio_file_editable_save (ViewerEditable *editable, - GError **error) -{ - ViewerAudioFile *self = VIEWER_AUDIO_FILE (editable); - - g_print ("Audio file implementation of editable interface save method.\n"); -} - -static void -viewer_audio_file_editable_interface_init (ViewerEditableInterface *iface) -{ - /* Override the implementation of save(). */ - iface->save = viewer_audio_file_editable_save; - - /* - * Leave iface->undo and ->redo alone, they are already set to the - * base class implementation. - */ -} - -G_DEFINE_TYPE_WITH_CODE (ViewerAudioFile, viewer_audio_file, VIEWER_TYPE_FILE, - G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE, - viewer_audio_file_editable_interface_init)) - -static void -viewer_audio_file_class_init (ViewerAudioFileClass *klass) -{ - /* Nothing here. */ -} - -static void -viewer_audio_file_init (ViewerAudioFile *self) -{ - /* Nothing here. */ -} - - - - - To access the base class interface implementation use - g_type_interface_peek_parent - from within an interface's default_init function. - - - - To call the base class implementation of an interface - method from a derived class where than interface method has been - overridden, stash away the pointer returned from - g_type_interface_peek_parent - in a global variable. - - - - In this example ViewerAudioFile overrides the - save interface method. In its overridden method - it calls the base class implementation of the same interface method. - -static ViewerEditableInterface *viewer_editable_parent_interface = NULL; - -static void -viewer_audio_file_editable_save (ViewerEditable *editable, - GError **error) -{ - ViewerAudioFile *self = VIEWER_AUDIO_FILE (editable); - - g_print ("Audio file implementation of editable interface save method.\n"); - - /* Now call the base implementation */ - viewer_editable_parent_interface->save (editable, error); -} - -static void -viewer_audio_file_editable_interface_init (ViewerEditableInterface *iface) -{ - viewer_editable_parent_interface = g_type_interface_peek_parent (iface); - - iface->save = viewer_audio_file_editable_save; -} - -G_DEFINE_TYPE_WITH_CODE (ViewerAudioFile, viewer_audio_file, VIEWER_TYPE_FILE, - G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE, - viewer_audio_file_editable_interface_init)) - -static void -viewer_audio_file_class_init (ViewerAudioFileClass *klass) -{ - /* Nothing here. */ -} - -static void -viewer_audio_file_init (ViewerAudioFile *self) -{ - /* Nothing here. */ -} - - - - - - - - - - How to create and use signals - - - The signal system in GType is pretty complex and - flexible: it is possible for its users to connect at runtime any - number of callbacks (implemented in any language for which a binding - exists) - - A Python callback can be connected to any signal on any - 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 - possible to use GSignal for much more than just emitting signals to - multiple clients. - - - - Simple use of signals - - - The most basic use of signals is to implement event - notification. For example, given a ViewerFile object with - a write method, a signal could be emitted whenever - the file is changed using that method. - The code below shows how the user can connect a callback to the - "changed" signal. - -file = g_object_new (VIEWER_FILE_TYPE, NULL); - -g_signal_connect (file, "changed", (GCallback) changed_event, NULL); - -viewer_file_write (file, buffer, strlen (buffer)); - - - - - The ViewerFile signal is registered in the - class_init function: - -file_signals[CHANGED] = - g_signal_newv ("changed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, - NULL /* closure */, - NULL /* accumulator */, - NULL /* accumulator data */, - NULL /* C marshaller */, - G_TYPE_NONE /* return_type */, - 0 /* n_params */, - NULL /* param_types */); - - and the signal is emitted in viewer_file_write: - -void -viewer_file_write (ViewerFile *self, - const guint8 *buffer, - gsize size) -{ - g_return_if_fail (VIEWER_IS_FILE (self)); - g_return_if_fail (buffer != NULL || size == 0); - - /* First write data. */ - - /* Then, notify user of data written. */ - g_signal_emit (self, file_signals[CHANGED], 0 /* details */); -} - - As shown above, the details parameter can safely be set to zero if no - detail needs to be conveyed. For a discussion of what it can be used for, - see - - - - The C signal marshaller should always be NULL, in which - case the best marshaller for the given closure type will be chosen by - GLib. This may be an internal marshaller specific to the closure type, or - g_cclosure_marshal_generic, which implements generic - conversion of arrays of parameters to C callback invocations. GLib used to - require the user to write or generate a type-specific marshaller and pass - that, but that has been deprecated in favour of automatic selection of - marshallers. - - - - Note that g_cclosure_marshal_generic is slower than - non-generic marshallers, so should be avoided for performance critical - code. However, performance critical code should rarely be using signals - anyway, as emitting a signal blocks on emitting it to all listeners, which - has potentially unbounded cost. - - - - diff --git a/docs/reference/gobject/tutorial.md b/docs/reference/gobject/tutorial.md new file mode 100644 index 000000000..ec5eab939 --- /dev/null +++ b/docs/reference/gobject/tutorial.md @@ -0,0 +1,1288 @@ +Title: GObject Tutorial + +# GObject Tutorial + +## How to define and implement a new GObject + +This document focuses on the implementation of a subtype of GObject, for +example to create a custom class hierarchy, or to subclass a GTK widget. + +Throughout the chapter, a running example of a file viewer program is used, +which has a `ViewerFile` class to represent a single file being viewed, and +various derived classes for different types of files with special +functionality, such as audio files. The example application also supports +editing files (for example, to tweak a photo being viewed), using a +`ViewerEditable` interface. + +### Boilerplate header code + +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 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. + +Pick a name convention for your headers and source code and stick to it: + +- use a dash to separate the prefix from the typename: `viewer-file.h` and + `viewer-file.c` (this is the convention used by most GNOME libraries and + applications) +- use an underscore to separate the prefix from the typename: + `viewer_file.h` and `viewer_file.c` +- do not separate the prefix from the typename: `viewerfile.h` and + `viewerfile.c` (this is the convention used by GTK) + +Some people like the first two solutions better: it makes reading file names +easier for those with poor eyesight. + +The basic conventions for any header which exposes a GType are described in +the section of the Type system introduction called +["Conventions"](concepts.html#conventions). + +If you want to declare a type named "file" in the namespace "viewer", name +the type instance `ViewerFile` and its class `ViewerFileClass` (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 the +`G_DECLARE_FINAL_TYPE` macro, and require a structure to hold the instance +data to be declared in the source code (not the header file). + +```c +/* + * Copyright/Licensing information. + */ + +/* inclusion guard */ +#pragma once + +#include + +/* + * Potentially, include other headers on which this header depends. + */ + +G_BEGIN_DECLS + +/* + * Type declaration. + */ +#define VIEWER_TYPE_FILE viewer_file_get_type() +G_DECLARE_FINAL_TYPE (ViewerFile, viewer_file, VIEWER, FILE, GObject) + +/* + * Method definitions. + */ +ViewerFile *viewer_file_new (void); + +G_END_DECLS +``` + +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 the +`G_DECLARE_DERIVABLE_TYPE` macro: + +```c +/* + * Copyright/Licensing information. + */ + +/* inclusion guard */ +#pragma once + +#include + +/* + * Potentially, include other headers on which this header depends. + */ + +G_BEGIN_DECLS + +/* + * Type declaration. + */ +#define VIEWER_TYPE_FILE viewer_file_get_type() +G_DECLARE_DERIVABLE_TYPE (ViewerFile, viewer_file, VIEWER, FILE, GObject) + +struct _ViewerFileClass +{ + GObjectClass parent_class; + + /* Class virtual function fields. */ + void (* open) (ViewerFile *file, + GError **error); + + /* Padding to allow adding up to 12 new virtual functions without + * breaking ABI. */ + gpointer padding[12]; +}; + +/* + * Method definitions. + */ +ViewerFile *viewer_file_new (void); + +G_END_DECLS +``` + +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 "viewer-file.h"`, +without needing to know the prerequisites for `viewer-file.h`. + +### Boilerplate code + +In your code, the first step is to `#include` the needed headers: + +```c +/* + * Copyright/Licensing information + */ + +#include "viewer-file.h" + +/* Private structure definition. */ +typedef struct { + char *filename; + + /* other private fields */ +} ViewerFilePrivate; + +/* + * forward definitions + */ +``` + +If the class is being declared as final using `G_DECLARE_FINAL_TYPE`, its instance structure should be defined in the C file: + +```c +struct _ViewerFile +{ + 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: + +- implement the `viewer_file_get_type` 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` private +data should be placed in the instance structure, `ViewerFile`, 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. + +```c +G_DEFINE_TYPE (ViewerFile, viewer_file, G_TYPE_OBJECT) +``` + +or + +```c +G_DEFINE_TYPE_WITH_PRIVATE (ViewerFile, viewer_file, 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 the +`G_IMPLEMENT_INTERFACE` macro to implement an interface. + +### Object construction + +People often get confused when trying to construct their GObjects because of +the sheer number of different ways to hook into the objects's construction +process: it is difficult to figure which is the correct, recommended way. + +The [documentation on object +instantiation](concepts.html#object-instantiation) shows what user-provided +functions are invoked during object instantiation and in which order they +are invoked. A user looking for the equivalent of the simple C++ constructor +function should use the `instance_init` method. It will be invoked after all +the parents’ `instance_init` functions have been invoked. It cannot take +arbitrary construction parameters (as in C++) but if your object needs +arbitrary parameters to complete initialization, you can use construction +properties. + +Construction properties will be set only after all `instance_init` functions have run. No object reference will be returned to the client of `g_object_new()` until all the construction properties have been set. + +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. + +You should write the following code first: + +```c +G_DEFINE_TYPE_WITH_PRIVATE (ViewerFile, viewer_file, G_TYPE_OBJECT) + +static void +viewer_file_class_init (ViewerFileClass *klass) +{ +} + +static void +viewer_file_init (ViewerFile *self) +{ + ViewerFilePrivate *priv = viewer_file_get_instance_private (self); + + /* initialize all public and private members to reasonable default values. + * They are all automatically initialized to 0 to begin with. */ +} +``` + +If you need special construction properties (with `G_PARAM_CONSTRUCT_ONLY` +set), 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 the section called ["Object +properties"](concepts.html#object-properties). + +Property identifiers must start from 1, as 0 is reserved for internal use by GObject. + +```c +enum +{ + PROP_FILENAME = 1, + PROP_ZOOM_LEVEL, + N_PROPERTIES +}; + +static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; + +static void +viewer_file_class_init (ViewerFileClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = viewer_file_set_property; + object_class->get_property = viewer_file_get_property; + + obj_properties[PROP_FILENAME] = + g_param_spec_string ("filename", + "Filename", + "Name of the file to load and display from.", + NULL /* default value */, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); + + obj_properties[PROP_ZOOM_LEVEL] = + g_param_spec_uint ("zoom-level", + "Zoom level", + "Zoom level to view the file at.", + 0 /* minimum value */, + 10 /* maximum value */, + 2 /* default value */, + G_PARAM_READWRITE); + + g_object_class_install_properties (object_class, + N_PROPERTIES, + obj_properties); +} +``` + +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 complete the initialization of an 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 the section called "Object instantiation" or, more simply, +using the `constructed()` class method. 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. + +### Object destruction + +Again, it is often difficult to figure out which mechanism to use to hook +into the object's destruction process: when the last `g_object_unref()` function +call is made, a lot of things happen as described in [the "Object memory +management" section](concepts.html#object-memory-management) of the +documentation. + +The destruction process of your object is in two phases: dispose and +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 revival of instances in case of signal emission +during the destruction sequence. + +```c +struct _ViewerFilePrivate +{ + gchar *filename; + guint zoom_level; + + GInputStream *input_stream; +}; + +G_DEFINE_TYPE_WITH_PRIVATE (ViewerFile, viewer_file, G_TYPE_OBJECT) + +static void +viewer_file_dispose (GObject *gobject) +{ + ViewerFilePrivate *priv = viewer_file_get_instance_private (VIEWER_FILE (gobject)); + + /* 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 by setting the member + * NULL; g_clear_object() does this for us. + */ + g_clear_object (&priv->input_stream); + + /* 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 (viewer_file_parent_class)->dispose (gobject); +} + +static void +viewer_file_finalize (GObject *gobject) +{ + ViewerFilePrivate *priv = viewer_file_get_instance_private (VIEWER_FILE (gobject)); + + g_free (priv->filename); + + /* 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 (viewer_file_parent_class)->finalize (gobject); +} + +static void +viewer_file_class_init (ViewerFileClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = viewer_file_dispose; + object_class->finalize = viewer_file_finalize; +} + +static void +viewer_file_init (ViewerFile *self); +{ + ViewerFilePrivate *priv = viewer_file_get_instance_private (self); + + priv->input_stream = g_object_new (VIEWER_TYPE_INPUT_STREAM, NULL); + priv->filename = /* would be set as a property */; +} +``` + +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, by +having a disposed instance revert to an inert state. + +### Object methods + +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++ concepts. Those who have not had to +write C++ code recently can refer to a [C++ +tutorial](http://www.cplusplus.com/doc/tutorial/) to refresh their +memories.) + +- non-virtual public methods, +- virtual public methods and +- virtual private methods +- non-virtual private methods + +#### Non-Virtual Methods + +These are the simplest, providing a simple method which acts on the object. +Provide a function prototype in the header and an implementation of that +prototype in the source file. + +```c +/* declaration in the header. */ +void viewer_file_open (ViewerFile *self, + GError **error); +``` + +```c +/* implementation in the source file */ +void +viewer_file_open (ViewerFile *self, + GError **error) +{ + g_return_if_fail (VIEWER_IS_FILE (self)); + g_return_if_fail (error == NULL || *error == NULL); + + /* do stuff here. */ +} +``` + +#### Virtual public methods + +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. + +```c +/* declaration in viewer-file.h. */ +#define VIEWER_TYPE_FILE viewer_file_get_type () +G_DECLARE_DERIVABLE_TYPE (ViewerFile, viewer_file, VIEWER, FILE, GObject) + +struct _ViewerFileClass +{ + GObjectClass parent_class; + + /* stuff */ + void (*open) (ViewerFile *self, + GError **error); + + /* Padding to allow adding up to 12 new virtual functions without + * breaking ABI. */ + gpointer padding[12]; +}; + +void viewer_file_open (ViewerFile *self, + GError **error); +``` + +```c +/* implementation in viewer-file.c */ +void +viewer_file_open (ViewerFile *self, + GError **error) +{ + ViewerFileClass *klass; + + g_return_if_fail (VIEWER_IS_FILE (self)); + g_return_if_fail (error == NULL || *error == NULL); + + klass = VIEWER_FILE_GET_CLASS (self); + g_return_if_fail (klass->open != NULL); + + klass->open (self, error); +} +``` + +The code above simply redirects the open call to the relevant virtual +function. + +It is possible to provide a default implementation for this class method in +the object's `class_init` function: initialize the `klass->open` field to a +pointer to the actual implementation. By default, class methods that are not +inherited are initialized to `NULL`, and thus are to be considered "pure +virtual". + +```c +static void +viewer_file_real_close (ViewerFile *self, + GError **error) +{ + /* Default implementation for the virtual method. */ +} + +static void +viewer_file_class_init (ViewerFileClass *klass) +{ + /* this is not necessary, except for demonstration purposes. + * + * pure virtual method: mandates implementation in children. + */ + klass->open = NULL; + + /* merely virtual method. */ + klass->close = viewer_file_real_close; +} + +void +viewer_file_open (ViewerFile *self, + GError **error) +{ + ViewerFileClass *klass; + + g_return_if_fail (VIEWER_IS_FILE (self)); + g_return_if_fail (error == NULL || *error == NULL); + + klass = VIEWER_FILE_GET_CLASS (self); + + /* 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. + */ + g_return_if_fail (klass->open != NULL); + klass->open (self, error); +} + +void +viewer_file_close (ViewerFile *self, + GError **error) +{ + ViewerFileClass *klass; + + g_return_if_fail (VIEWER_IS_FILE (self)); + g_return_if_fail (error == NULL || *error == NULL); + + klass = VIEWER_FILE_GET_CLASS (self); + if (klass->close != NULL) + klass->close (self, error); +} +``` + +#### Virtual private Methods + +These are very similar to virtual public methods. They just don't have a +public function to call directly. The header file contains only a +declaration of the virtual function: + +```c +/* declaration in viewer-file.h. */ +struct _ViewerFileClass +{ + GObjectClass parent; + + /* Public virtual method as before. */ + void (*open) (ViewerFile *self, + GError **error); + + /* Private helper function to work out whether the file can be loaded via + * memory mapped I/O, or whether it has to be read as a stream. */ + gboolean (*can_memory_map) (ViewerFile *self); + + /* Padding to allow adding up to 12 new virtual functions without + * breaking ABI. */ + gpointer padding[12]; +}; + +void viewer_file_open (ViewerFile *self, GError **error); +``` + +These virtual functions are often used to delegate part of the job to child classes: + +```c +/* this accessor function is static: it is not exported outside of this file. */ +static gboolean +viewer_file_can_memory_map (ViewerFile *self) +{ + return VIEWER_FILE_GET_CLASS (self)->can_memory_map (self); +} + +void +viewer_file_open (ViewerFile *self, + GError **error) +{ + g_return_if_fail (VIEWER_IS_FILE (self)); + g_return_if_fail (error == NULL || *error == NULL); + + /* + * Try to load the file using memory mapped I/O, if the implementation of the + * class determines that is possible using its private virtual method. + */ + if (viewer_file_can_memory_map (self)) + { + /* Load the file using memory mapped I/O. */ + } + else + { + /* Fall back to trying to load the file using streaming I/O… */ + } +} +``` + +Again, it is possible to provide a default implementation for this private virtual function: + +```c +static gboolean +viewer_file_real_can_memory_map (ViewerFile *self) +{ + /* As an example, always return false. Or, potentially return true if the + * file is local. */ + return FALSE; +} + +static void +viewer_file_class_init (ViewerFileClass *klass) +{ + /* non-pure virtual method; does not have to be implemented in children. */ + klass->can_memory_map = viewer_file_real_can_memory_map; +} +``` + +Derived classes can then override the method with code such as: + +```c +static void +viewer_audio_file_class_init (ViewerAudioFileClass *klass) +{ + ViewerFileClass *file_class = VIEWER_FILE_CLASS (klass); + + /* implement pure virtual function. */ + file_class->can_memory_map = viewer_audio_file_can_memory_map; +} +``` + +### Chaining up + +Chaining up is often loosely defined by the following set of conditions: + +- parent class A defines a public virtual method named `foo` and provides a + default implementation +- child class B re-implements method `foo` +- B’s implementation of `foo` calls (‘chains up to’) its parent class A’s + implementation of `foo` + +There are various uses of this idiom: + +- 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 and chain up to ensure that the + previous behaviour is not really modified, just extended +- you need to implement the + [Chain of Responsibility pattern](https://en.wikipedia.org/wiki/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 each handler is run in turn + +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 virtual +function pointer and invoke it directly + +The "original" adjective used in the sentence above is not innocuous. To +fully understand its meaning, recall how class structures are initialized: +for each object type, the class structure associated with this object is +created by first copying the class structure of its parent type (a simple +memcpy) and then by invoking the `class_init` callback on the resulting class +structure. Since the `class_init` callback is responsible for overwriting the +class structure with the user re-implementations of the class methods, the +modified copy of the parent class structure stored in the derived instance +cannot be used. A copy of the class structure of an instance of the parent +class is needed. + +To chain up, you can use the `parent_class` pointer created and initialized +by the `G_DEFINE_TYPE` family of macros, for instance: + +```c +static void +b_method_to_call (B *obj, int some_param) +{ + /* do stuff before chain up */ + + /* 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, some_param); + + /* do stuff after chain up */ +} +``` + +## How to define and implement interfaces + +### Defining interfaces + +The theory behind how GObject interfaces work is given in the section called +["Non-instantiatable classed types: +interfaces"](concepts.html#non-instantiatable-classed-types-interfaces); +this section covers how to define and implement an interface. + +The first step is to get the header right. This interface defines three +methods: + +```c +/* + * Copyright/Licensing information. + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define VIEWER_TYPE_EDITABLE viewer_editable_get_type() +G_DECLARE_INTERFACE (ViewerEditable, viewer_editable, VIEWER, EDITABLE, GObject) + +struct _ViewerEditableInterface +{ + GTypeInterface parent_iface; + + void (*save) (ViewerEditable *self, + GError **error); + void (*undo) (ViewerEditable *self, + guint n_steps); + void (*redo) (ViewerEditable *self, + guint n_steps); +}; + +void viewer_editable_save (ViewerEditable *self, + GError **error); +void viewer_editable_undo (ViewerEditable *self, + guint n_steps); +void viewer_editable_redo (ViewerEditable *self, + guint n_steps); + +G_END_DECLS +``` + +This code is the same as the code for a normal GType which derives from a +GObject except for a few details: + +- the `_GET_CLASS` function is called `_GET_IFACE` (and is defined by `G_DECLARE_INTERFACE`) +- the instance type, `ViewerEditable`, is not fully defined: it is used merely as an abstract type which represents an instance of whatever object which implements the interface +- the parent of the `ViewerEditableInterface` is `GTypeInterface`, not `GObjectClass` + +The implementation of the `ViewerEditable` type itself is trivial: + +- `G_DEFINE_INTERFACE` creates a `viewer_editable_get_type` function which registers the type in the type system. The third argument is used to define a prerequisite interface (which we'll talk about more later). Just pass 0 for this argument when an interface has no prerequisite +- `viewer_editable_default_init` is expected to register the interface's signals if there are any (we will see a bit later how to use them) +- the interface methods `viewer_editable_save`, `viewer_editable_undo` and `viewer_editable_redo` dereference the interface structure to access its associated interface function and call it + +```c +G_DEFINE_INTERFACE (ViewerEditable, viewer_editable, G_TYPE_OBJECT) + +static void +viewer_editable_default_init (ViewerEditableInterface *iface) +{ + /* add properties and signals to the interface here */ +} + +void +viewer_editable_save (ViewerEditable *self, + GError **error) +{ + ViewerEditableInterface *iface; + + g_return_if_fail (VIEWER_IS_EDITABLE (self)); + g_return_if_fail (error == NULL || *error == NULL); + + iface = VIEWER_EDITABLE_GET_IFACE (self); + g_return_if_fail (iface->save != NULL); + iface->save (self, error); +} + +void +viewer_editable_undo (ViewerEditable *self, + guint n_steps) +{ + ViewerEditableInterface *iface; + + g_return_if_fail (VIEWER_IS_EDITABLE (self)); + + iface = VIEWER_EDITABLE_GET_IFACE (self); + g_return_if_fail (iface->undo != NULL); + iface->undo (self, n_steps); +} + +void +viewer_editable_redo (ViewerEditable *self, + guint n_steps) +{ + ViewerEditableInterface *iface; + + g_return_if_fail (VIEWER_IS_EDITABLE (self)); + + iface = VIEWER_EDITABLE_GET_IFACE (self); + g_return_if_fail (iface->redo != NULL); + iface->redo (self, n_steps); +} +``` + +### Implementing interfaces + +Once the interface is defined, implementing it is rather trivial. + +The first step is to define a normal final GObject class exactly as usual. + +The second step is to implement `ViewerFile` by defining it using +`G_DEFINE_TYPE_WITH_CODE` and `G_IMPLEMENT_INTERFACE` instead of +`G_DEFINE_TYPE`: + +```c +static void viewer_file_editable_interface_init (ViewerEditableInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (ViewerFile, viewer_file, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE, + viewer_file_editable_interface_init)) +``` + +This definition is very much like all the similar functions seen previously. +The only interface-specific code present here is the use of +`G_IMPLEMENT_INTERFACE`. + +Classes can implement multiple interfaces by using multiple calls to +`G_IMPLEMENT_INTERFACE` inside the call to `G_DEFINE_TYPE_WITH_CODE`. + +`viewer_file_editable_interface_init` is the interface initialization +function: inside it, every virtual method of the interface must be assigned +to its implementation: + +```c +static void +viewer_file_editable_save (ViewerFile *self, + GError **error) +{ + g_print ("File implementation of editable interface save method: %s.\n", + self->filename); +} + +static void +viewer_file_editable_undo (ViewerFile *self, + guint n_steps) +{ + g_print ("File implementation of editable interface undo method: %s.\n", + self->filename); +} + +static void +viewer_file_editable_redo (ViewerFile *self, + guint n_steps) +{ + g_print ("File implementation of editable interface redo method: %s.\n", + self->filename); +} + +static void +viewer_file_editable_interface_init (ViewerEditableInterface *iface) +{ + iface->save = viewer_file_editable_save; + iface->undo = viewer_file_editable_undo; + iface->redo = viewer_file_editable_redo; +} + +static void +viewer_file_init (ViewerFile *self) +{ + /* Instance variable initialisation code. */ +} +``` + +If the object is not of final type, e.g. was declared using +`G_DECLARE_DERIVABLE_TYPE` then `G_ADD_PRIVATE` macro should be added. The +private structure should be declared exactly as for a normal derivable +object. + +```c +G_DEFINE_TYPE_WITH_CODE (ViewerFile, viewer_file, G_TYPE_OBJECT, + G_ADD_PRIVATE (ViewerFile) + G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE, + viewer_file_editable_interface_init)) +``` + +### Interface definition prerequisites + +To specify that an interface requires the presence of other interfaces when +implemented, GObject introduces the concept of prerequisites: it is possible +to associate a list of prerequisite types to an interface. For example, if +object A wishes to implement interface I1, and if interface I1 has a +prerequisite on interface I2, A has to implement both I1 and I2. + +The mechanism described above is, in practice, very similar to Java's +interface I1 extends interface I2. The example below shows the GObject +equivalent: + +``` +/* Make the ViewerEditableLossy interface require ViewerEditable interface. */ +G_DEFINE_INTERFACE (ViewerEditableLossy, viewer_editable_lossy, VIEWER_TYPE_EDITABLE) +``` + +In the `G_DEFINE_INTERFACE` call above, the third parameter defines the +prerequisite type. This is the GType of either an interface or a class. In +this case the `ViewerEditable` interface is a prerequisite of +`ViewerEditableLossy`. The code below shows how an implementation can +implement both interfaces and register their implementations: + +```c +static void +viewer_file_editable_lossy_compress (ViewerEditableLossy *editable) +{ + ViewerFile *self = VIEWER_FILE (editable); + + g_print ("File implementation of lossy editable interface compress method: %s.\n", + self->filename); +} + +static void +viewer_file_editable_lossy_interface_init (ViewerEditableLossyInterface *iface) +{ + iface->compress = viewer_file_editable_lossy_compress; +} + +static void +viewer_file_editable_save (ViewerEditable *editable, + GError **error) +{ + ViewerFile *self = VIEWER_FILE (editable); + + g_print ("File implementation of editable interface save method: %s.\n", + self->filename); +} + +static void +viewer_file_editable_undo (ViewerEditable *editable, + guint n_steps) +{ + ViewerFile *self = VIEWER_FILE (editable); + + g_print ("File implementation of editable interface undo method: %s.\n", + self->filename); +} + +static void +viewer_file_editable_redo (ViewerEditable *editable, + guint n_steps) +{ + ViewerFile *self = VIEWER_FILE (editable); + + g_print ("File implementation of editable interface redo method: %s.\n", + self->filename); +} + +static void +viewer_file_editable_interface_init (ViewerEditableInterface *iface) +{ + iface->save = viewer_file_editable_save; + iface->undo = viewer_file_editable_undo; + iface->redo = viewer_file_editable_redo; +} + +static void +viewer_file_class_init (ViewerFileClass *klass) +{ + /* Nothing here. */ +} + +static void +viewer_file_init (ViewerFile *self) +{ + /* Instance variable initialisation code. */ +} + +G_DEFINE_TYPE_WITH_CODE (ViewerFile, viewer_file, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE, + viewer_file_editable_interface_init) + G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE_LOSSY, + viewer_file_editable_lossy_interface_init)) +``` + +It is very important to notice that the order in which interface +implementations are added to the main object is not random: +`g_type_add_interface_static()`, which is called by `G_IMPLEMENT_INTERFACE`, must +be invoked first on the interfaces which have no prerequisites and then on +the others. + +### Interface properties + +GObject interfaces can also have properties. Declaration of the interface +properties is similar to declaring the properties of ordinary GObject types +as explained in the section called ["Object +properties"](concepts.html#object-properties), except that +`g_object_interface_install_property()` is used to declare the properties +instead of `g_object_class_install_property()`. + +To include a property named 'autosave-frequency' of type gdouble in the +`ViewerEditable` interface example code above, we only need to add one call in +`viewer_editable_default_init()` as shown below: + +```c +static void +viewer_editable_default_init (ViewerEditableInterface *iface) +{ + g_object_interface_install_property (iface, + g_param_spec_double ("autosave-frequency", + "Autosave frequency", + "Frequency (in per-seconds) to autosave backups of the editable content at. " + "Or zero to disable autosaves.", + 0.0, /* minimum */ + G_MAXDOUBLE, /* maximum */ + 0.0, /* default */ + G_PARAM_READWRITE)); +} +``` + +One point worth noting is that the declared property wasn't assigned an +integer ID. The reason being that integer IDs of properties are used only +inside the `get_property` and `set_property` virtual methods. Since interfaces +declare but do not implement properties, there is no need to assign integer +IDs to them. + +An implementation declares and defines its properties in the usual way as +explained in the section called “Object properties”, except for one small +change: it can declare the properties of the interface it implements using +`g_object_class_override_property()` instead of `g_object_class_install_property()`. +The following code snippet shows the modifications needed in the `ViewerFile` +declaration and implementation above: + +```c +struct _ViewerFile +{ + GObject parent_instance; + + double autosave_frequency; +}; + +enum +{ + PROP_AUTOSAVE_FREQUENCY = 1, + N_PROPERTIES +}; + +static void +viewer_file_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ViewerFile *file = VIEWER_FILE (object); + + switch (prop_id) + { + case PROP_AUTOSAVE_FREQUENCY: + file->autosave_frequency = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +viewer_file_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ViewerFile *file = VIEWER_FILE (object); + + switch (prop_id) + { + case PROP_AUTOSAVE_FREQUENCY: + g_value_set_double (value, file->autosave_frequency); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +viewer_file_class_init (ViewerFileClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = viewer_file_set_property; + object_class->get_property = viewer_file_get_property; + + g_object_class_override_property (object_class, PROP_AUTOSAVE_FREQUENCY, "autosave-frequency"); +} +``` + +### Overriding interface methods + +If a base class already implements an interface and a derived class needs to +implement the same interface but needs to override certain methods, you must +reimplement the interface and set only the interface methods which need +overriding. + +In this example, `ViewerAudioFile` is derived from `ViewerFile`. Both implement +the `ViewerEditable` interface. `ViewerAudioFile` only implements one method of +the `ViewerEditable` interface and uses the base class implementation of the +other. + +```c +static void +viewer_audio_file_editable_save (ViewerEditable *editable, + GError **error) +{ + ViewerAudioFile *self = VIEWER_AUDIO_FILE (editable); + + g_print ("Audio file implementation of editable interface save method.\n"); +} + +static void +viewer_audio_file_editable_interface_init (ViewerEditableInterface *iface) +{ + /* Override the implementation of save(). */ + iface->save = viewer_audio_file_editable_save; + + /* + * Leave iface->undo and ->redo alone, they are already set to the + * base class implementation. + */ +} + +G_DEFINE_TYPE_WITH_CODE (ViewerAudioFile, viewer_audio_file, VIEWER_TYPE_FILE, + G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE, + viewer_audio_file_editable_interface_init)) + +static void +viewer_audio_file_class_init (ViewerAudioFileClass *klass) +{ + /* Nothing here. */ +} + +static void +viewer_audio_file_init (ViewerAudioFile *self) +{ + /* Nothing here. */ +} +``` + +To access the base class interface implementation use +`g_type_interface_peek_parent()` from within an interface's `default_init` +function. + +To call the base class implementation of an interface method from a derived +class where than interface method has been overridden, stash away the +pointer returned from `g_type_interface_peek_parent()` in a global variable. + +In this example `ViewerAudioFile` overrides the save interface method. In +its overridden method it calls the base class implementation of the same +interface method. + +```c +static ViewerEditableInterface *viewer_editable_parent_interface = NULL; + +static void +viewer_audio_file_editable_save (ViewerEditable *editable, + GError **error) +{ + ViewerAudioFile *self = VIEWER_AUDIO_FILE (editable); + + g_print ("Audio file implementation of editable interface save method.\n"); + + /* Now call the base implementation */ + viewer_editable_parent_interface->save (editable, error); +} + +static void +viewer_audio_file_editable_interface_init (ViewerEditableInterface *iface) +{ + viewer_editable_parent_interface = g_type_interface_peek_parent (iface); + + iface->save = viewer_audio_file_editable_save; +} + +G_DEFINE_TYPE_WITH_CODE (ViewerAudioFile, viewer_audio_file, VIEWER_TYPE_FILE, + G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE, + viewer_audio_file_editable_interface_init)) + +static void +viewer_audio_file_class_init (ViewerAudioFileClass *klass) +{ + /* Nothing here. */ +} + +static void +viewer_audio_file_init (ViewerAudioFile *self) +{ + /* Nothing here. */ +} +``` + +## How to create and use signals + +The signal system in GType is pretty complex and flexible: it is possible +for its users to connect at runtime any number of callbacks (implemented in +any language for which a binding exists) to any signal and to stop the +emission of any signal at any state of the signal emission process. This +flexibility makes it possible to use GSignal for much more than just +emitting signals to multiple clients. + +### Simple use of signals + +The most basic use of signals is to implement event notification. For +example, given a `ViewerFile` object with a write method, a signal could be +emitted whenever the file is changed using that method. The code below shows +how the user can connect a callback to the "changed" signal. + +```c +file = g_object_new (VIEWER_FILE_TYPE, NULL); + +g_signal_connect (file, "changed", (GCallback) changed_event, NULL); + +viewer_file_write (file, buffer, strlen (buffer)); +``` + +The ViewerFile signal is registered in the `class_init` function: + +```c +file_signals[CHANGED] = + g_signal_newv ("changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, + NULL /* closure */, + NULL /* accumulator */, + NULL /* accumulator data */, + NULL /* C marshaller */, + G_TYPE_NONE /* return_type */, + 0 /* n_params */, + NULL /* param_types */); +``` + +and the signal is emitted in `viewer_file_write`: + +```c +void +viewer_file_write (ViewerFile *self, + const guint8 *buffer, + gsize size) +{ + g_return_if_fail (VIEWER_IS_FILE (self)); + g_return_if_fail (buffer != NULL || size == 0); + + /* First write data. */ + + /* Then, notify user of data written. */ + g_signal_emit (self, file_signals[CHANGED], 0 /* details */); +} +``` + +As shown above, the details parameter can safely be set to zero if no detail +needs to be conveyed. For a discussion of what it can be used for, see the +section called [“The detail argument”](concepts.html#the-detail-argument). + +The C signal marshaller should always be `NULL`, in which case the best +marshaller for the given closure type will be chosen by GLib. This may be an +internal marshaller specific to the closure type, or +`g_cclosure_marshal_generic()`, which implements generic conversion of arrays of +parameters to C callback invocations. GLib used to require the user to write +or generate a type-specific marshaller and pass that, but that has been +deprecated in favour of automatic selection of marshallers. + +Note that `g_cclosure_marshal_generic()` is slower than non-generic +marshallers, so should be avoided for performance critical code. However, +performance critical code should rarely be using signals anyway, as signals +are synchronous, and the emission blocks until all listeners are invoked, +which has potentially unbounded cost.