GLib parameters
- From: Tim Janik <timj gtk org>
- To: Gtk+ Developers <gtk-devel-list redhat com>
- Subject: GLib parameters
- Date: Mon, 6 Mar 2000 04:52:53 +0100 (CET)
hi all,
as requested by owen, here are some details about the new parameter
system to be used in GLib, along with GLib's new type and
object system.
for the moment, lets say we have the following fundamentals,
and later concentrate on G_TYPE_PARAM:
typedef enum
{
G_TYPE_INVALID,
G_TYPE_NONE,
G_TYPE_PARAM,
G_TYPE_ENUM,
G_TYPE_FLAGS,
G_TYPE_OBJECT
};
G_TYPE_ENUM, G_TYPE_FLAGS and G_TYPE_OBJECT are all classesd types,
that means they have *one* class structure per type id, much like
things work in Gtk already, i.e.:
class structure (one instance per program):
+----------------------------------------+
| GtkWidgetClass (GTK_TYPE_WIDGET) |
| void (*delete_event) (GtkWidget*,...); |
| etc... |
+----------------------------------------+
^ ^
| |
object structure | |
(multiple instances | |
per program): | |
| |
+------------------------+ | | +------------------------+
| GtkWidget (Widget1) | | | | GtkWidget (Widget2) |
| GtkWidgetClass *class;-+-+ +-+-GtkWidgetClass *class; |
| GtkStateType state; | | GtkStateType state; |
| etc... | | etc... |
+------------------------+ +------------------------+
parameters, in contrast, will have multiple specification
structures per type id (thus the type system doesn't really
treat them as "classed" types):
typedef struct _GParamSpec GParamSpec;
typedef struct _GParamSpec GParamSpecInt;
typedef struct _GParamSpecRichInt GParamSpecRichInt;
typedef enum
{
G_PARAM_READABLE = 1 << 0,
G_PARAM_WRITABLE = 1 << 1,
} GParamFlags;
struct _GParamSpec /* base specification */
{
GType gtype;
const gchar *name;
GParamFlags flags;
};
struct _GParamSpecRichInt
{
/* from GParamSpec */
GType gtype;
const gchar *name;
GParamFlags flags;
/* rich int specific members */
gint minimum, maximum, default_value;
};
parameter specification structures (multiple instances per type id):
+-------------------------------------+
| GParamSpecRichInt (G_TYPE_RICH_INT) |
| param_name="balance"; |
| minumum=-127; |
| maximum=127; |
| default_value=0; |
+-------------------------------------+
^ ^
+----------------------------+ +-------------------------------------+ | |
| GParamSpecInt (G_TYPE_INT) | | GParamSpecRichInt (G_TYPE_RICH_INT) | | |
| param_name="pixel_size"; | | param_name="person_age"; | | |
+----------------------------+ | minumum=1; | | |
^ ^ | maximum=120; | | |
| | | default_value=18; | | |
| | +-------------------------------------+ | |
| | ^ ^ | |
parameter structures | | | | | |
(multiple instances | | | | | |
per GParamSpec): | | | | | |
| | | | | |
+-------------------+ | | +-------------------+ | | +-------------------+ | |
| GParam (Param1) | | | | GParam (Param3) | | | | GParam (Param5) | | |
| GParamSpec *spec;-+-+ | | GParamSpec *spec;-+-+ | | GParamSpec *spec;-+-+ |
| value=999; | | | value=17; | | | value=-127; | |
+-------------------+ | +-------------------+ | +-------------------+ |
| | |
+-------------------+ | +-------------------+ | +-------------------+ |
| GParam (Param2) | | | GParam (Param4) | | | GParam (Param6) | |
| GParamSpec *spec;-+---+ | GParamSpec *spec;-+---+ | GParamSpec *spec;-+---+
| value=-34223; | | value=102; | | value=12; |
+-------------------+ +-------------------+ +-------------------+
so here we have two parameter types, G_TYPE_RICH_INT, and
G_TYPE_INT, both deriving from the type G_TYPE_PARAM.
comparing Param1, a "pixel_size" parameter, with a
"pixel_size" GtkArg (which is what we'd current have in gtk):
struct _GtkArg
{
GtkType type; =GTK_TYPE_INT
gchar *name; ="pixel_size"
union {
gint int_data; =999
} d;
};
struct _GParamSpecInt <------------------+
{ |
GType gtype; =G_TYPE_INT |
const gchar *name; ="pixel_size" |
}; |
struct _GParam |
{ |
GParamSpec *spec; -----------------+
union {
gint v_int; =999
} value;
};
we basically just moved the `type' and `name' parameters
into a seperate structure.
so basically GParam parameters are not identified by an inlined
type id and name, but by the parameter specification they point
to. this provides a number of benefits over the old system:
- memory savings; e.g. for all the properties that a GtkWidget
currently exports, there will be *one* GParamSpec alike
structure, held in the GtkObjectClass (GObjectClass?) structure.
all parameters of such a type will point to this param spec
and don't come with their own name strings.
- unification; GtkArgs currently may be named in different ways,
e.g. "GtkWidget::visible" and "visible" refer to the same
argument for a GtkFrame.
- complexity reduction over the current GtkArg system, basically
the GtkArgInfo cruft will be absorbed by GParamSpec
* customizability; custom parameter types can be created, like
the above GParamSpecRichInt which comes with maximum, minimum
and default values - are often needed features for e.g.
GUI builders. many variations are imaginable, e.g. BSE currently
has a custom string type that defines character sets of which
a string parameter may consist.
* scalability; parameter specifications can be as simple or complex as
an application wants them to be. glib comes with the base types:
ints, floats, strings...
applications (library foundations) can extend parameter types to
rightfully fit their needs while keeping the API reasonably simple:
only two public functions have to be supported, one to create
the parameter specification and one to set the parameter value
(a getter can also be provided where convenient, to hide GParam
details)
- benefits i forgot because i became too familiar with the concept already ;)
furthermore, since parameter types need to be "officially"
derived from the G_TYPE_PARAM fundamental type, appropriate
functions can be setup to support various _generic_ operations
on parameters. tentative API:
typedef gchar* (*GTypeParamCollector) (GParam *param,
guint n_bytes,
guint8 *bytes);
struct _GTypeParamInfo
{
guint sizeof_param_spec;
void (*param_spec_free_fields) (BseParamSpec *pspec);
void (*param_init) (BseParam *param,
BseParamSpec *pspec,
gboolean zeroed);
void (*param_free_value) (BseParam *param);
void (*param_copy_value) (const BseParam *param_src,
BseParam *param_dest);
/* optional, can be NULL for plain memcmp(3) */
gint (*param_values_cmp) (const BseParam *param1,
const BseParam *param2);
/* optional, for rich types (e.g. for value = CLAMP (value, min, max);) */
gboolean (*param_validate) (BseParam *param);
/* optional, required for vararg getters */
void (*move_value) (BseParam *param,
gpointer value_p);
/* how to collect parameter values from vararg setters */
guint n_collect_bytes; /* optional (0) */
GTypeParamCollector param_collector; /* optional (NULL) */
};
GType g_type_register_param_type (GType parent_type,
const gchar *type_name,
const GTypeParamInfo *info);
typedef void (*GParamExchangeValueFunc) (BseParam *param1,
BseParam *param2);
/* for possible parameter conversions (optional) */
void g_param_type_register_exchange (GType param_type1,
GType param_type2,
GParamExchangeValueFunc func);
the standard set of parameters that glib should support out of the box
is probably:
G_TYPE_PARAM_BOOL
G_TYPE_PARAM_INT
G_TYPE_PARAM_UINT
G_TYPE_PARAM_FLOAT
G_TYPE_PARAM_DOUBLE
G_TYPE_PARAM_STRING
G_TYPE_PARAM_POINTER
G_TYPE_PARAM_GOBJECT
none of these should be rich types (i.e. come with min/max or default),
since as owen pointed out rightfully to me, glib should supply the
mechanism, not define semantics.
however, i think it'd be very convenient to supply rich types for Gtk,
i.e. floats and ints with min/max and default, since that's one of the
things that could easily be supplied by widgets and GUI builders are
desperatedly lacking.
so in parallel to the above GParamSpecRichInt example, we could have
such a type for gtk:
/* standard (generic) GLib parameter functions */
void g_param_init (GParam *param,
GParamSpec *pspec);
void g_param_init_default (GParam *param,
GParamSpec *pspec);
gboolean g_param_validate (GParam *param);
gboolean g_param_defaults (const GParam *param);
gint g_param_values_cmp (const GParam *param1,
const GParam *param2);
void g_param_copy_value (const GParam *param_src,
GParam *param_dest);
void g_param_move_value (GParam *param,
gpointer value_p);
void g_param_free_value (GParam *param);
void g_param_reset_value (GParam *param);
gboolean g_param_value_convert (const BseParam *param_src,
BseParam *param_dest);
gboolean g_param_values_exchange (BseParam *param1,
BseParam *param2);
[gtk, upon startup, registers GTK_TYPE_PARAM_INT_RANGE and conversion
functions like range_int<->int, range_int<->float]
typedef struct _GtkParamSpecRangeInt GtkParamSpecRangeInt;
struct _GtkParamSpecRangeInt
{
/* from GParamSpec */
GType gtype;
const gchar *name;
GParamFlags flags;
/* standard GtkParamSpec* members */
gchar *nick;
gchar *blurb;
/* RangeInt specifics */
gint minimum;
gint maximum;
gint stepping_rate;
gint default_value;
};
/* create a parameter specification for a range_int */
GParamSpec* gtk_param_spec_range_int (const gchar *name,
const gchar *nick,
const gchar *blurb,
gint minimum,
gint maximum,
gint stepping_rate,
gint default_value,
GtkParamFlags flags);
/* returns whether v_int is valid within range or had to be CLAMP()ed */
gboolean gtk_param_set_range_int (GParam *param,
gint v_int);
with that, the current GtkContainer::border_width argument would
e.g. be implemented as (in gtk_container_class_init):
gtk_object_class_add_param (container_class,
ARG_BORDER_WIDTH,
gtk_param_spec_range_int ("border_width",
"Border Width",
"Padding border in pixels"
" around the container",
0, /* minimum */
1024, /* maximum */
5, /* stepping_rate */
0, /* default_value */
GTK_PARAM_READWRITE));
example use:
gint border_width = 0;
gtk_object_set (frame,
"border_width", 15,
NULL);
gtk_object_get (frame,
"border_width", &border_width,
NULL);
/* border_width == 15 at this point */
a GUI builder can present this property with a spin button
that steps upwards/downwards by 5 pixels and a tooltip:
Border Width: | 15| X
/
/
+-----------------------------------------------+
| Padding border in pixels around the container |
+-----------------------------------------------+
and the builder could omit saving the border_width property to
its GUI description file, if border_width==0 (since that's the default).
ok i guess its time for some rationale on this lengthy mail, GtkArg
has some severe limitations (and also an awkwardly complex implementation
to be found in gtkarg.c, gtkargcollector.c and various places in
gtksignal.c and gtkobject.c), most of which mentioned above already.
GParam is meant to solve that by providing the flexibility required in
some places and be extensible beyond the needs of a toolkit object system.
the above suggested g_param_*() API is not something i invented while
composing this mail, rather, it evolved in BSE and is basically already
implemented there (what's missing is the type registration part; in BSE
the different parameters are still fundamental types).
a complete set of rich parameters can be found in the module beast on
GNOME CVS under beast/bse/bseparam.[hc], and the various objects in
beast/bse/ and beast/plugins/ make exclusive use of this
mechansim to export properties (much like GnomeCanvasItems provide a
GtkArg API only). some older textual description is also available
in beast/docs/bse-typesystem.txt.
90% of BEASTs GUI is generated from the parameter specifications, so i'm
pretty sure this mechanism will be feasible and extensible enough for other
parameterized applications as well.
the parameter types are not only usefull to export object properties, in fact,
this is what GSignal (and for the ones that've heared of it: GClosure) will
build upon as well, and within BSE they also serve as arguments to
procedure types and build the foundation to serialize objects (read/write
objects to/from a text stream).
DISCLAIMER:
since i haven't actually performed the transition of the BSE parameter
system to GLib yet, the above outline is still subject to change, but
the general mechanism is pretty clear already.
it's pretty late already, so please be prepared to forgive minor and/or
major mistakes i made ;)
things that are as of yet not completely resolved:
* since objects, object classes, enum/flags classes, interfaces etc. are
going to be runtime destructable, so they can be implemented from within
dynamic modules, i wonder whether this makes sense for parameter types as
well. on the API side this is just:
GType g_type_register_param_type (GType parent_type,
const gchar *type_name,
- const GTypeParamInfo *info);
+ GPlugin *plugin);
on the other hand, parameter types provide the most fundamental means
of comunication and should therefore probably be provided by static
program components.
* GtkArg/GtkObject have some general-use problems with constructor arguments,
though this is independant of the GtkArg->GParam transition, a satisfying
solution still has to be worked out.
---
ciaoTJ
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]