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 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
|
=====================
Writing Bindable APIs
=====================
Things to do
------------
Type and function naming
~~~~~~~~~~~~~~~~~~~~~~~~
Follow the [GObject naming conventions](https://docs.gtk.org/gobject/concepts.html#conventions).
This allows the introspection tooling to group C functions together as methods
on types, and to group types and methods together into namespaces.
If these conventions are not followed, C functions may be incorrectly
introspected as functions rather than methods; methods may end up on the
incorrect class; class names may end up incorrectly including part of the
namespace in their name; amongst other problems.
Some of these problems can be worked around by passing additional arguments to
the introspection tooling to explicitly specify namespacing, but the tools work
better if C naming avoids that ambiguity.
Things to avoid
---------------
Structures with custom memory management
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Avoid creating C structures with custom memory management unless they are
registered as a `boxed type <https://docs.gtk.org/gobject/boxed.html>`__.
If you don't register them as a boxed type bindings will fall back to
simple memory copying, which might not be what you want.
Also consider using a full `GObject <https://docs.gtk.org/gobject/class.Object.html>`__
as that allows bindings to better integrate those objects with the binding
language, like for example preserve user defined state across language
boundaries.
Example to avoid:
.. code-block:: c
struct _GstMiniObject {
GTypeInstance instance;
/*< public >*/ /* with COW */
gint refcount;
guint flags;
Functionality only accessible through a C macro or inline function
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The scanner does not support C macros as API. Solution - add a function
accessor rather than a macro. This also has the side effect of making
debugging in C code easier.
Example:
.. code-block:: c
#define GTK_WIDGET_FLAGS(wid) (GTK_OBJECT_FLAGS (wid))
GtkWidgetFlags gtk_widget_get_flags (GtkWidget *widget); /* Actually, see http://bugzilla.gnome.org/show_bug.cgi?id=69872 */
Likewise, inline functions cannot be loaded from a dynamic library. Make sure to
provide a non-inline equivalent.
Direct C structure access for objects
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Having GObjects also have fields can be difficult to bind. Create accessor
functions.
Example:
.. code-block:: c
struct _SoupMessage {
GObject parent;
/*< public >*/
const char *method;
guint status_code;
...
}
const char * soup_message_get_method (SoupMessage *message); /* Or use a GObject property */
va_list
~~~~~~~
Using varargs can be convenient for C, but they are difficult to bind.
Solution: Keep the C function for the convenience of C programmers, but add an
another function which takes an array (either zero terminated or with a length
parameter).
**Good** example:
.. code-block:: c
GtkListStore *gtk_list_store_new (gint n_columns,
...);
GtkListStore *gtk_list_store_newv (gint n_columns,
GType *types);
You can also expose the array variant under the name of the varargs variant
using the ``rename-to`` annotation:
``gtk_list_store_newv: (rename-to gtk_list_store_new)``
Also consider using C99's compound literals and designated initializers to avoid
``va_list`` even in the C API, which is more type-safe.
Multiple out parameters
~~~~~~~~~~~~~~~~~~~~~~~
Multiple out parameters are supported by introspection, but not all languages
have an obvious mapping for multiple out values. A boxed structure could serve
as an alternative.
Example to think about (here, there could be a boxed ``struct GtkCoordinate {
gint x; gint y; }`` structure).
.. code-block:: c
void gtk_widget_get_pointer (GtkWidget *widget,
gint *x,
gint *y);
In-out parameters
~~~~~~~~~~~~~~~~~
Don't use in-out arguments, especially not for non-scalar values. It's difficult
to enforce or validate the conventions for in-out arguments, which can easily
lead to crashes.
Instead, pass the input as an in argument, and receive the output as either a
return value or an out argument.
.. code-block:: c
FooBoxed *foo_bar_scale_boxed(FooBar *self,
FooBoxed *boxed);
void foo_bar_scale_boxed(FooBar *self,
FooBoxed *boxed_in,
FooBoxed **boxed_out);
In particular, do not require the caller to pass an initialized ``GValue`` to
avoid the in-out annotation; instead, pass a ``GValue`` as an out argument, and
have the function initialize it.
Arrays
~~~~~~
For reference types, zero-terminated arrays are the easiest to work with.
Arrays of primitive type such as "int" will require length metadata.
In a general-purpose library, it's best not to expose GLib array and hash types
such as ``GArray``, ``GPtrArray``, ``GByteArray``, ``GList``, ``GSList``,
``GQueue``, and ``GHashTable`` in the public API. They are fine for internal
libraries, but difficult in general for consumers of introspected libraries to
deal with.
Strings
~~~~~~~
C treats strings as zero-terminated arrays of bytes, but many other languages do not. So don't
write APIs that treat ``const char *`` parameters as arrays that need an
``array length`` annotation.
Treat all ``const char *`` parameters as zero-terminated strings. Don't use the
same entry point for zero-terminated strings as for byte arrays which may
contain embedded zeroes.
.. code-block:: c
void foo_bar_snarf_string(FooBar *self,
const char *str);
void foo_bar_snarf_bytes(FooBar *self,
const uint8_t *bytes,
size_t length);
In particular, avoid functions taking a ``const char *`` with a signed length
that can be set to a negative value to let the function compute the string
length in bytes. These functions are hard to bind, and require manual overrides.
Callbacks
~~~~~~~~~
Callbacks are hard to support for introspection bindings because of their
complex life-cycle. Try to avoid having more than one callback in the same
function, and consider using GClosure when you need more.
Using a different name for error domain quarks from the enum name
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Error domain quarks should always be named in the form
<namespace>_<module>_error_quark() for an error enum called
<Namespace><Module>Error. Example to avoid:
.. code-block:: c
typedef enum FooBarError {
FOO_BAR_ERROR_MOO,
FOO_BAR_ERROR_BLEAT
};
GQuark foo_bar_errors_quark();
Don't have properties and methods with the same name
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Some bindings for dynamic languages expose GObject properties and methods in the
same way, as properties on an object instance. So don't make a GObject property
with the same name as a method, e.g. a property named ``add-feature`` on a class
named ``SoupSession`` which also has a method ``soup_session_add_feature()``.
Custom code in constructors
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Creating an object via ``foo_bar_new()`` shouldn't execute any code
differently than creating the same object via ``g_object_new()``, since many
bindings (and also GtkBuilder/Glade) create objects using ``g_object_new()``.
That is, don't do this:
.. code-block:: c
FooBar *
foo_bar_new (void)
{
FooBar *retval = FOO_BAR (g_object_new (FOO_TYPE_BAR, NULL));
retval->priv->some_variable = 5; /* Don't do this! */
return retval;
}
Instead, put initialization code in the ``foo_bar_init()`` function or the
``foo_bar_constructed()`` virtual function.
Transfer-none return values from the binding
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If your library expects to call a function from C which may be implemented in
another language and exposed through the binding (for example, a signal handler,
or a GObject vfunc), it's best not to return transfer-none values, because what
you assume about storage lifetime in C may not apply in other languages.
For example,
.. code-block:: c
typedef struct {
GTypeInterface iface;
const char * (*my_vfunc) (FooBaz *self); /* Don't do this! */
char * (*my_better_vfunc) (FooBaz *self); /* Do this instead! */
} FooBazIface;
A class that implements ``FooBazIface`` in another programming language may not
be able to return a static string here, because the language may not have a
concept of static storage lifetime, or it may not store strings as
zero-terminated UTF-8 bytes as C code would expect. This can cause memory leaks.
Instead, duplicate the string before returning it, and use transfer-full. This
recommendation applies to any data type with an ownership, including boxed and
object types.
|