summaryrefslogtreecommitdiff
path: root/src/glibext/helpers.h
blob: cfcc85b7d52fffc0ffda90137fba21d0a34a56d3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197

/* Chrysalide - Outil d'analyse de fichiers binaires
 * helpers.h - prototypes pour la simplification des interactions de base avec GLib
 *
 * Copyright (C) 2024 Cyrille Bagard
 *
 *  This file is part of Chrysalide.
 *
 *  Chrysalide is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Chrysalide is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
 */


#ifndef _GLIBEXT_HELPERS_H
#define _GLIBEXT_HELPERS_H


#include <assert.h>
#include <glib-object.h>



/**
 * Les définitions issues de <glib-2.80>/gobject/gtype.h fournissent des macros
 * facilitant la déclaration de types pour entêtes. Cependant :
 *
 *    - G_DECLARE_FINAL_TYPE impose une structure de classe fixe ;
 *    - G_DECLARE_DERIVABLE_TYPE impose une structure d'objet fixe.
 *
 * Ces deux macros ne peuvent donc pas être employées en l'état dans Chrysalide.
 *
 * Par ailleurs, la fonctionnalité g_autoptr() n'offre pas une séduction totale :
 * elle conduit à un code inconsistant, avec parfois des libérations explicites,
 * parfois des libérations invisible gérées par le compilateur. Cet aspect
 * fonctionnel offert par les macros GLib est donc inutile pour Chrysalide.
 *
 * Une nouvelle macro de déclaration est ainsi constituée ici.
 *
 * De fait de l'absence d'argument, l'inclusion de la définition suivante n'est
 * pas possible :
 *
 *    MOD##_TYPE_##NAME t_n##_get_type()
 *
 * La macro traditionnelle doit ainsi être fournie en parallèle à la déclaration
 * condensée ici. Elle y est d'ailleurs référencée, forçant une déclaration
 * préalable de manière globale et cohérente dans l'ensemble du code d'emploi.
 */

#define DECLARE_GTYPE(TN, t_n, MOD, NAME)                                               \
                                                                                        \
    GType t_n##_get_type(void) G_GNUC_CONST;                                            \
                                                                                        \
    typedef struct _##TN TN;                                                            \
    typedef struct _##TN##Class TN##Class;                                              \
                                                                                        \
    G_GNUC_UNUSED static inline TN *MOD##_##NAME(gconstpointer obj)                     \
    {                                                                                   \
        return G_TYPE_CHECK_INSTANCE_CAST(obj, MOD##_TYPE_##NAME, TN);                  \
    }                                                                                   \
                                                                                        \
    G_GNUC_UNUSED static inline TN##Class *MOD##_##NAME##_CLASS(gconstpointer klass)    \
    {                                                                                   \
        return G_TYPE_CHECK_CLASS_CAST(klass, MOD##_TYPE_##NAME, TN##Class);            \
    }                                                                                   \
                                                                                        \
    G_GNUC_UNUSED static inline gboolean MOD##_IS_##NAME(gconstpointer obj)             \
    {                                                                                   \
        return G_TYPE_CHECK_INSTANCE_TYPE(obj, MOD##_TYPE_##NAME);                      \
    }                                                                                   \
                                                                                        \
    G_GNUC_UNUSED static inline gboolean MOD##_IS_##NAME##_CLASS(gconstpointer klass)   \
    {                                                                                   \
        return G_TYPE_CHECK_CLASS_TYPE(klass, MOD##_TYPE_##NAME);                       \
    }                                                                                   \
                                                                                        \
    G_GNUC_UNUSED static inline TN##Class *MOD##_##NAME##_GET_CLASS(gconstpointer obj)  \
    {                                                                                   \
        return G_TYPE_INSTANCE_GET_CLASS(obj, MOD##_TYPE_##NAME, TN##Class);            \
    }


/**
 * Bis repetita pour les interfaces...
 */

#define DECLARE_INTERFACE(TN, t_n, MOD, NAME)                                               \
                                                                                            \
    GType t_n##_get_type(void) G_GNUC_CONST;                                                \
                                                                                            \
    /* Coquille vide */                                                                     \
    typedef struct _##TN TN;                                                                \
    /* Interface */                                                                         \
    typedef struct _##TN##Interface TN##Interface;                                          \
                                                                                            \
    G_GNUC_UNUSED static inline TN *MOD##_##NAME(gconstpointer obj)                         \
    {                                                                                       \
        return G_TYPE_CHECK_INSTANCE_CAST(obj, MOD##_TYPE_##NAME, TN);                      \
    }                                                                                       \
                                                                                            \
    G_GNUC_UNUSED static inline gboolean MOD##_IS_##NAME(gconstpointer obj)                 \
    {                                                                                       \
        return G_TYPE_CHECK_INSTANCE_TYPE(obj, MOD##_TYPE_##NAME);                          \
    }                                                                                       \
                                                                                            \
    G_GNUC_UNUSED static inline TN##Interface *MOD##_##NAME##_GET_IFACE(gconstpointer obj)  \
    {                                                                                       \
        return G_TYPE_INSTANCE_GET_INTERFACE(obj, MOD##_TYPE_##NAME, TN##Interface);        \
    }



/**
 * Les principales fonctions incrémentant ou réduisant le nombre de références
 * attachées à un objet acceptent de simples pointeurs génériques (cf. définitions
 * du fichier <glib-2.80>/gobject/gobject.h :
 *
 *    [...]
 *    void        g_object_notify_by_pspec          (GObject        *object,
 *                                                   GParamSpec     *pspec);
 *    void        g_object_thaw_notify              (GObject        *object);
 *    gboolean    g_object_is_floating              (gpointer        object);
 *    gpointer    g_object_ref_sink                 (gpointer        object);
 *    gpointer    g_object_take_ref                 (gpointer        object);
 *    gpointer    g_object_ref                      (gpointer        object);
 *    void        g_object_unref                    (gpointer        object);
 *    void        g_object_weak_ref                 (GObject        *object,
 *                                                   GWeakNotify     notify,
 *                                                   gpointer        data);
 *    [...]
 *
 * La fonction g_object_unref() débute bien par exemple par une validation
 * de l'instance, avec un appel à : g_return_if_fail (G_IS_OBJECT (object)).
 *
 * Cependant, cette vérification est désactivée en cas de compilation de GLib
 * avec G_DISABLE_CHECKS.
 *
 * Une conversion vers un type donné (par exemple avec G_OBJECT()) est par
 * ailleurs dépendante d'autres paramètres de compilation, comme le révèle
 * le fichier <glib-2.80>/gobject/gtype.h :
 *
 *    #if defined(G_DISABLE_CAST_CHECKS) || defined(__OPTIMIZE__)
 *    #  define _G_TYPE_CIC(ip, gt, ct)       ((ct*) (void *) ip)
 *    #  define _G_TYPE_CCC(cp, gt, ct)       ((ct*) (void *) cp)
 *    #else
 *    #  define _G_TYPE_CIC(ip, gt, ct) \
 *        ((ct*) (void *) g_type_check_instance_cast ((GTypeInstance*) ip, gt))
 *    #  define _G_TYPE_CCC(cp, gt, ct) \
 *        ((ct*) (void *) g_type_check_class_cast ((GTypeClass*) cp, gt))
 *    #endif
 *
 * Afin d'assurer des conditions de débogage/fuzzing optimales dans tous les
 * cas de figure, les fonctions de manipulation des références font l'objet
 * ici d'une couche intermédiaire pour les appels.
 */

#ifndef NDEBUG

#   define ref_object(ip)                                                           \
    do                                                                              \
    {                                                                               \
        GTypeInstance *__inst;                                                      \
        __inst = g_type_check_instance_cast((GTypeInstance *)ip, G_TYPE_OBJECT);    \
        assert(__inst != NULL);                                                     \
        g_object_ref(ip);                                                           \
    }                                                                               \
    while (0)

#   define unref_object(ip)                                                         \
    do                                                                              \
    {                                                                               \
        GTypeInstance *__inst;                                                      \
        __inst = g_type_check_instance_cast((GTypeInstance *)ip, G_TYPE_OBJECT);    \
        assert(__inst != NULL);                                                     \
        g_object_unref(ip);                                                         \
    }                                                                               \
    while (0)

#else

#   define ref_object(p) g_object_ref(p)
#   define unref_object(p) g_object_unref(p)

#endif



#endif  /* _GLIBEXT_HELPERS_H */