/* 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 .
*/
#ifndef _GLIBEXT_HELPERS_H
#define _GLIBEXT_HELPERS_H
#include
#include
/**
* Les définitions issues de /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 /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 /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 */