A type system for high-level interfaces



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.

Here is my first shot at it.  Some of it has already been implemented,
but the major work will probably be to go thru all of Gtk and Gdk to
make it conform to the conventions outlined below.


The Type System
---------------

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.

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.

Types can be arranged in a hierachy 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 types 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

- How to pass it by reference as an lvalue.

  Some functions might prefer to return a value by modifying some
  object that has been passed by reference.  We have to specify how
  this should work.

  Expressed as

    ref: ctype1, ctype2, ...

  Note that functions are generally only allowed to either have a
  normal return value, or exactly one of these lvalue parameters, but
  this is of no concern to the type system.  We come back to it later
  when describing the function descriptions.

- How to store it in a GtkArg structure.

  This involves listing the names and types of the union members that
  are used to store all relevant information.

  Expressed as

    store: ctype1 name1, ctype2 name2, ...

  This will probably always be the same information as the `pass:'
  line.

- How to store a reference in a GtkArg structure that allows for
  returning it from a function call constructed at runtime.

  Expressed as

    refstore: ctype1 name1, ctype2 name2, ...

- How to copy a value of this type.

  This includes reference counting issues, etc.  When nothing is
  mentioned about copying, this means that you can freely copy.

- Whether this type can be inherited.

  Some types can not be inherited, like "int"s, other must be inherited,
  like "int_enum"s to become meaningful, some can but don't have to,
  like "GtkObjects".

  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 following are all fundamental types.  You can't add new
fundamental types at runtime.

- GTK_ARG_INVALID

        pass: n.a.
      return: n.a.
         ref: n.a.
       store: n.a.
    refstore: n.a.
     inherit: no

    No valid type has this id. Use this only as the `base type' for
    new fundamental types.  Note that it is not recommended to
    introduce new fundamental types lightly.

- GTK_ARG_NONE

        pass: n.a.
      return: void
         ref: n.a.
       store: n.a.
    refstore: n.a.
     inherit: no
    
    The "void" type.  There is no data associated with, so there is
    nothing to store in a GtkArg.  It is illegal to use it for a
    function paramter.  If it is used as a return type, it means that
    the function does not return any useful value.  It is not
    meaningful to copy a GTK_ARG_NONE value.

- GTK_ARG_CHAR

        pass: gchar
      return: gchar
         ref: n.a.
       store: gchar c
    refstore: gchar *p
     inherit: no

    A character.  In good C tradition, this is represented as a
    "gchar".  [Maybe we can come up with something more
    international?]

- GTK_ARG_BOOL

        pass: gint [gbool?]
      return: gint
         ref: n.a.
       store: gint i [gbool b?]
    refstore: gint* p
     inherit: no

    An boolean value, zero is false, non-zero is true.  The canonical
    true value is 1.

- GTK_ARG_INTEGER

        pass: glong
      return: glong
         ref: n.a.
       store: glong l
    refstore: glong *p
     inherit: no

- GTK_ARG_UNSIGNED_INTEGER

        pass: gulong
      return: gulong
         ref: n.a.
       store: gulong ul
    refstore: gulong *p
     inherit: no

- GTK_ARG_FLOAT

        pass: gfloat
      return: gfloat
         ref: n.a.
       store: gfloat f
    refstore: gfloat *p
     inherit: no

- GTK_ARG_STRING

        pass: gchar*
      return: gchar*
         ref: n.a.
       store: gchar *s
    refstore: gchar **p
     inherit: no

    Read only nul-terminated string of "gchar"s.

    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 free(3)).

    When stored in a GtkArg, the pointer is only as long valid as the
    GtkArg itself.

    When storing a return value in a GtkArg, the pointer points to
    malloced memory that falls into the responsiblity of the creator
    of the GtkArg structure.

- GTK_ARG_STATIC_STRING

        pass: n.a.       (use GTK_ARG_STRING instead)
      return: gchar*
         ref: n.a.
       store: n.a.
    refstore: gchar **p
     inherit: no

    For returning a read-only string, that should not be freed by the
    caller.

    The caller is not responsible for freeing the memory of a static
    string that it gets from some function.  However 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.

- GTK_ARG_OBJECT

        pass: GtkObject*
      return: GtkObject*
         ref: n.a.
       store: GtkObject *o
    refstore: GtkObject **p
     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 recieve a notification when a
    GtkObject is about to be deleted.  Install a signal handler for
    the "destroy" signal.  Upon recieving 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 -- either directly or
    inside a GtkArg -- the returned pointer is reflected in the
    reference count.  When you want to use the notification method
    from above, you should first install your handler and then unref
    the GtkObject.

    [A cyclic refererence is very easy to produce from Scheme.  Just
    register a callback that has the object in its closure.  Happens
    all the time.]

- GTK_ARG_INT_ENUM

        pass: gint
      return: gint
         ref: n.a.
       store: gint i
    refstore: gint *p
     inherit: must

    An enumeration that represents separate cases, like GtkArgType
    itself.

    You can get information about a particular enumeration with
    `gtk_type_get_enum_info'.  It contains a list of all enumeration
    literals, each with its name and value.

- GTK_ARG_BIT_ENUM

        pass: gint
      return: gint
         ref: n.a.
       store: gint i
    refstore: gint *p
     inherit: no

    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, but 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.

    The enumeration info is obtained as for GTK_ARG_INT_ENUM.

- GTK_ARG_STRUCT_REF

        pass: gpointer
      return: gpointer
         ref: n.a.
       store: gpointer p
    refstore: gpointer *p
     inherit: must

    A pointer to some structure.  The structure maintains a reference
    count that needs to be updated appropriately.

    When a struct ref is returned from a function -- either directly or
    inside a GtkArg -- the returned pointer is reflected in the
    reference count.

    To maintain the reference count, you have to find the appropriate
    GtkStructInfo with gtk_type_get_struct_info.  It contains two
    functions, ref_func and unref_func.

- GTK_ARG_POINT

        pass: GdkPoint*
      return: n.a.
         ref: GdkPoint*
       store: GdkPoint point
    refstore: GdkPoint *p
     inherit: no

    The caller is responsible for allocating the memory for returned
    values.  Always copy the whole structure.

- GTK_ARG_COLOR

        pass: GdkColor*
      return: n.a.
         ref: GdkColor*
       store: GdkColor color
    refstore: GdkColor *p
     inherit: no

    Like GTK_ARG_POINT.

- GTK_ARG_RECTANGLE

        pass: GdkRectangle*
      return: n.a.
         ref: GdkRectangle
       store: GdkRectangle rectangle
    refstore: GdkRectangle *p
     inherit: no

    Like GTK_ARG_POINT.

- GTK_ARG_SEGMENT

        pass: GdkSegment*
      return: n.a.
         ref: GdkSegment*
       store: GdkSegment segment
    refstore: GdkSegment *p
     inherit: no

    Like GTK_ARG_POINT.

- GTK_ARG_EVENT

        pass: GdkEvent*
      return: GdkEvent*
         ref: n.a.
       store: GdkEvent *event
    refstore: GdkEvent **event
     inherit: no

    [Still need to sort this out.  Probably too big to store inline
    and needs special treatment for copying because it contains
    pointers.]

- GTK_ARG_FOREIGN

        pass: gpointer, void (*destroy_notify)(gpointer)
      return: n.a.
         ref: n.a.
       store: gpointer foreign.data,
              void (*foreign.destroy_notify)(gpointer data)
    refstore: n.a.
     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_ARG_FOREIGN_CALLBACK

        pass: GtkSignalMarshal, gpointer, void (*dnotify)(gpointer)
      return: n.a.
         ref: n.a.
       store: GtkSignalMarshal callback.marshal, gpointer callback.data,
              void (*callback.destroy_notify)(gpointer data)
    refstore: n.a.
     inherit: no

    An arbitrary value from the interpreter that can be somehow
    invoked plus a destroy notification function.

- GTK_ARG_ARGS

        pass: gint, GtkArg*
      return: n.a.
         ref: n.a.
       store: gint args.n_args, GtkArg *args.args
    refstore: n.a.
     inherit: no

    An array of GtkArgs structures plus its size.
    Memory is managed just like for a GTK_ARG_STRING.


Describing Exported Functions
-----------------------------

[under construction...]



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]