I'd like to propose the following type system for Gtk. It will be used at runtime to construct argument lists for signal handlers and for setting object slots (with gtk_type_set_arg). It will also be used to statically describe most of the functions that should be exported to an interpreter. Basically, it should reflect the needs and capabilities of the typical high-level language that wants to interface to Gtk. The primary issue here is robustness. Ideally, it should not be possible to crash your program from the high-level language. The second issue is to allow mostly automatic stub generation for the interpreter glue. I have evolved it from the current system by unifying the GTK_ARG_* types with the GtkObject types and then adding support for the new things. PLEASE NOTE: In the following I often say that something `will be done' or describe things as being already finished. This only means that I -- in the moment that I have written them down -- would like these things to be done that way. It does not imply any official commitment from the Gtk developers or likely further directions of Gtk development. The master plan is to: - find a set of fundamental types and their precise semantics so that they blend well with the implementation of high-level languages. - describe most `interesting' types that are defined by Gtk in terms of these fundamental types. - describe as much functions of Gtk as possible with these types. The set of the fundamental types will be fairly heterogenous and their semantics will be written down in prose. The remaining types will be derived from the fundamental types in a very restrictede way. That allows us to describe them in a highly stylized manner so that they can be automatically translated into whatever glue code is required by the high-level language. Most of the functions that can be exported to the high-level language will likewise be described in a stylized manner to allow automatic stub generation. This system will probably not cover all features of Gtk that we might want to export to the high-level language, but it could help avoid gratuitous definition of features that cannot be exported cleanly because they use the full `power' of C. The preceding paragraphs all address a static interface. When glue code is generated for a particular high-level language implementation, the description of the types and the functions is read from some files that have been installed alongside with the rest of Gtk. There will also be a run-time type system that mirrors the fundamental and derived types. This is needed by Gtk itself for the signal mechanism and the generic object interface (gtk_object_new, gtk_object_set, ...) and can also be used to advantage by the glue code. But the type information that is available from Gtk does only cover what Gtk needs for itself. If you need more information for your glue code (for example, a mapping between the actual names of enumeration literals and their values) you have to implement it yourself (by using the static description files, of course). The Fundamental Types --------------------- Whenever I say "interpreter" in this text, I mean the implementation of the particular high-level language. Only a few types are allowed to travel between Gtk land and interpreter land. Some of them can be converted (each in its own special way) and some need to be wrapped. There exist some strict conventions to ensure proper memory handling. Objects that escape into the interpreter must have a (potentially) unlimited life-time or must be able to inform the interpreter when they have been destroyed. When Gtk can not guarantee this (by not respecting a reference count for example), the object must be copied. At runtime, a type is identified by its numerical id. These ids are essentially determined dynamically, so you can't count on a certain type to always be represented by the same id. The fundamental types (see below), however, are assigned constant ids to blend well with the established usage of the GTK_ARG_* types. For the static function descriptions, a type is identified by its name. That name is also available at runtime, mostly for error reporting. Some types can be arranged in a hierarchy to express (single) inheritance relationships. Types without a base class are called `fundamental' types in this text. The set of fundamental types is fixed, because each fundamental type has its own very special semantics and they probably need to be handled individually by the interpreter glue. Adding a new fundamental type generally means writing new glue code for each interpreter. Each fundamental type has a precise description of its semantics. Every type that (ultimately) inherits from a certain fundamental type must exactly adhere to this semantics. The description of the semantics include: - How to pass it to a function. This includes stating the types and order of all `primitive' C types that carry the information as well as detailing the things that the called function is allowed to do with the values and how the associated memory is managed. This is expressed below as a line that looks like: pass: ctype1, ctype2, ... followed by the description. When it is not allowed to pass this type to functions, the line looks like this: pass: n.a. - How to get it back from a function as the return value. This means stating the C type of the return value, if it is at all possible to return the type from a function. [etc...] Expressed as return: ctype - Whether this type can be inherited. Some types can not be inherited (like "int"), other must be inherited to become meaningful (like "enum"), some can but don't have to (like "GtkObject"). Expressed as inherit: no inherit: must inherit: yes These descriptions are only written down in English and must be enforced by carefully implementing both the interpreter glue and the Gtk functions. Because of this, the number of fundamental types should be kept to a minimum. There is no way to add new fundamental types to the system short of hacking the Gtk sources (and the table below). If a prototype of a function can not be described with the fundamental types, it can still be exported to the interpreter, but requires more attention by the glue writers. Therefore, try to design your functions so that they follow these conventions. The first line of each type states the symbolic name of the run-time numerical type id and the textual name (in double quotes). - GTK_TYPE_INVALID, "invalid" pass: n.a. return: n.a. inherit: no No valid type has this id. Use this only as the `base type' for new fundamental types or for expressing other exceptional situations. - GTK_TYPE_NONE, "none" pass: n.a. return: void inherit: no The "void" type. There is no data associated with it. It is illegal to use it for a function parameter. If it is used as a return type, it means that the function does not return any useful value. - GTK_TYPE_CHAR, "char" pass: gchar return: gchar inherit: no A character. In good C tradition, this is represented as a "gchar". [Maybe we can come up with something more international?] - GTK_TYPE_BOOL, "bool" pass: gint [gbool?] return: gint inherit: no An boolean value, zero is false, non-zero is true. The canonical true value is 1. - GTK_TYPE_INT, "int" pass: gint return: gint inherit: no - GTK_TYPE_UINT, "uint" pass: guint return: guint inherit: no - GTK_TYPE_LONG, "long" pass: glong return: glong inherit: no - GTK_TYPE_ULONG, "ulong" pass: gulong return: gulong inherit: no - GTK_TYPE_FLOAT, "double" pass: gdouble return: gdouble inherit: no - GTK_TYPE_STRING, "string" pass: gchar* return: gchar* inherit: no For passing read only nul-terminated strings of "gchar"s and returning newly allocated strings. When passed to a function, the string is only valid for the duration of the call. The callee is not allowed to retain the pointer or to modify the string. It has to copy the whole string to new storage. When returned from a function, the string sits in malloced memory that must eventually be freed by the caller (with g_free). - GTK_TYPE_STATIC_STRING, "static_string" pass: n.a. (use GTK_TYPE_STRING instead) return: gchar* inherit: no For returning a read-only string that should not be freed by the caller. The caller does not assume ownership of the memory of the string The returned pointer is only valid until the next Gtk or Gdk function is called, so the whole string should be immediately copied into new storage if needs be. - GTK_TYPE_OBJECT, "GtkObject" pass: GtkObject* (or derived) return: GtkObject* (or derived) inherit: yes A pointer to a GtkObject or some derived type. There are two possible mechanism to manage the lifetime of GtkObjects. First, they maintain a reference count that can be controlled with gtk_object_ref and gtk_object_unref. The GtkObject is not deleted as long as the reference count is greater than 0. The second possibility is to receive a notification when a GtkObject is about to be deleted. Install a signal handler for the "destroy" signal. Upon receiving the notification, you must invalidate all references you have stored to the GtkObject. The second method is preferable because it does not run the risk of creating cyclic references which would completely prevent the GtkObject from being destroyed. When a GtkObject is returned from a function the returned pointer is *not* reflected in the reference count. That is, a widget returned by gtk_new_button, for example, has a reference count of zero. [A cyclic reference is very easy to produce from Scheme. Just register a callback that has the object in its closure. Happens all the time.] - GTK_TYPE_ENUM, "enum" pass: gint return: gint inherit: must An enumeration that represents separate cases, like GtkWindowType. - GTK_TYPE_FLAGS, "flags" pass: gint return: gint inherit: must An enumeration that represent options that can be combined by oring them together. The values of such an enumeration are not required to be single bits. They can also contain common combinations with their own names. Such combinations are required to be listed after all the single bit `fundamental' values in the enumeration info (see below [but not yet]). - GTK_TYPE_BOXED, "boxed" pass: gpointer return: gpointer inherit: must A pointer to some structure. You have to explicitely manage the lifetime of those structures by invoking some functions at the right times. Boxed values that are passed to a function are only valid for the duration of that function call. If the function wants to retain a reference to the boxed value, it needs to invoke the "copy" function (see below). If the copy is no longer needed, the "destroy" function has to be invoked. When a boxed value is returned from a function, the caller of the function assumes ownership of the value. When it is no longer needed, the "destroy" function has to be invoked. The "copy" and "destroy" functions are listed in the static description file. [I have not completely made my mind up about thie reference counting issues and the implementation is probably very inconsistent (but conservative) right now.] - GTK_TYPE_FOREIGN, "foreign" pass: gpointer, GtkDestroyNotify notify return: gpointer inherit: no An arbitrary value that fits into a "gpointer" plus a function that has to be called when the value is no longer needed. This allows the interpreter to protect the value from its garbage collector. - GTK_TYPE_CALLBACK, "callback" pass: GtkCallbackMarshal, gpointer, GtkDestroyNotify notify return: n.a. inherit: no An arbitrary value from the interpreter that can be somehow invoked plus a destroy notification function. [The GtkCallbackMarshal function is called with enough information to invoke the real callback. This includes a GtkArg array, etc. Details follow.] - GTK_TYPE_VALUES pass: gint, GtkArg* return: n.a. inherit: no An array of GtkArg structures plus its size. Memory is managed just like for a GTK_TYPE_STRING. - GTK_TYPE_POINTER pass: gpointer return: gpointer inherit: no A untyped "gpointer". Probably not useful to the interpreter. - GTK_TYPE_C_CALLBACK pass: GtkFunction, gpointer data return: n.a. To handle the GtkContainer::foreach signal. Tough, tough.