Problèmes dans l'API C

Vous allez vraisemblablement rencontrer des problèmes dans la bibliothèque en cours d'habillage, en particulier s'il s'agit d'un nouveau projet. Voici quelques problèmes courants et leurs solutions.

G.VI.I. Impossibilité de pré-déclarer des structures

Par convention, les structures sont déclarées comme suit dans les en-têtes de style glib/GTK+ :

typedef struct _ExampleWidget ExampleWidget;

struct _ExampleWidget
{
  ...
};

Le typedef préliminaire permet l'utilisation de cette structure dans un en-tête sans inclure sa définition complète, simplement en la pré-déclarant, par répétition de la définition de type. Cela signifie que vous n'avez pas à inclure les en-têtes de la bibliothèque C dans la bibliothèque C++ ; vous la gardez ainsi hors de votre API publique. gmmproc suppose qu'on utilise cette technique ; vous verrez donc des messages d'erreurs si ce n'est pas le cas.

L'erreur de compilation ressemble à quelque chose comme :

example-widget.h:56: error: using typedef-name 'ExampleWidget' after 'struct'
../../libexample/libexamplemm/example-widget.h:34: error: 
           'ExampleWidget' has a previous declaration here
make[4]: *** [example-widget.lo] Error 1
ou bien encore :
example-widget.h:60: error: '_ExampleWidget ExampleWidget' 
     redeclared as different kind of symbol
../../libexample/libexamplemm/example-widget.h:34: error: 
previous declaration of 'typedef struct _ExampleWidget ExampleWidget'

C'est facile à corriger dans la bibliothèque C ; n'envoyez pas de correctif au mainteneur concerné.

G.VI.II. Perte de propriétés

Par convention, les objets de style glib/GTK+ possèdent des fonctions *_new(), comme par exemple example_widget_new(), qui ne font rien de plus qu'appeler g_object_new() et renvoyer le résultat. Les paramètres en entrée sont fournis à la fonction g_object_new() en même temps que les noms des propriétés dont elles sont les valeurs. Par exemple,

GtkWidget* example_widget_new(int something, const char* thing)
{
        return g_object_new (EXAMPLE_TYPE_WIDGET,
                             "something", something, 
                             "thing", thing, NULL);
}

Cette façon de procéder permet des liaisons de langage pour implémenter leurs propres équivalents (comme les constructeurs C++) sans utiliser les fonctions *_new(). Ceci est souvent nécessaire ; elles peuvent donc instancier en réalité un GType dérivé pour ajouter leurs propres accroches pour les gestionnaires de signal et les fonctions virtuelles.

Pour le moins, la fonction _new() ne doit pas utiliser une quelconque API privée (fonctions uniquement dans un fichier .c). Même s'il n'y a pas de fonctions, nous pouvons ré-implémenter 2 ou 3 lignes de code dans une fonction _new() pour autant que ces lignes de code utilisent une API disponible pour nous.

Une autre manière de contourner ce problème consiste à ajouter une fonction *_construct() que le constructeur C++ peut appeler après avoir instancié son propre type. Par exemple,

GtkWidget* example_widget_new(int something, const char* thing)
{
        ExampleWidget* widget;
        widget = g_object_new (EXAMPLE_TYPE_WIDGET, NULL);
        example_widget_construct(widget, 
                                 "something", something,
                                 "thing", thing);
}

void example_widget_construct(ExampleWidget* widget,
                              int something,
                              const char* thing)
{
        //faire quelque chose qui utilise l'API privée:
        widget->priv->thing = thing;
        do_something(something);
}

Ajouter des propriétés en s'assurant qu'elles interagissent proprement entre elles est relativement difficile à corriger dans une bibliothèque C, mais c'est possible ; faites remonter un rapport d'anomalie avec si possible un correctif au mainteneur concerné.