hi everybody, I just added this:
EXTERN void class_setfieldnames(t_class *x, const char *s);
It's intended to store the names of the fields. What counts as fields and what doesn't is another story. Each struct inheriting from t_gobj has a number of things in it, but I'm especially intending this for messages as found in *.pd files, that is, constructors of objects.
By convention I include *every* atom in the creation message, which includes pretty obvious things like "#X obj". What people type in an objectbox starts at argument number 5. (and argument numbering starts at 0).
example of use:
class_setfieldnames(bang_class, "foo bar x1 y1 class w hold break isa " "snd rcv lab ldx ldy fstyle fs bcol fcol lcol");
where the string is a space-separated list of fields, and in this case it's straight from running desire.tk and typing this in the tcl box:
global fields; list $fields(bng)
____________________________________________________________________ Mathieu Bouchard - tél:+1.514.383.3801 - http://artengine.ca/matju Freelance Digital Arts Engineer, Montréal QC Canada
Hi Mathieu, maybe i'm a bit slow today, but (probably since i don't use desire.tk) could you explain again in other words what that is used for? Currently, i have no idea what "fields" are and what one can do with them.
best greetings, Thomas
Am 06.10.2005 um 09:18 schrieb Mathieu Bouchard:
hi everybody, I just added this:
EXTERN void class_setfieldnames(t_class *x, const char *s);
It's intended to store the names of the fields. What counts as fields and what doesn't is another story. Each struct inheriting from t_gobj has a number of things in it, but I'm especially intending this for messages as found in *.pd files, that is, constructors of objects.
By convention I include *every* atom in the creation message, which includes pretty obvious things like "#X obj". What people type in an objectbox starts at argument number 5. (and argument numbering starts at 0).
example of use:
class_setfieldnames(bang_class, "foo bar x1 y1 class w hold break isa " "snd rcv lab ldx ldy fstyle fs bcol fcol lcol");
where the string is a space-separated list of fields, and in this case it's straight from running desire.tk and typing this in the tcl box:
global fields; list $fields(bng)
Mathieu Bouchard - tél:+1.514.383.3801 - http://artengine.ca/matju Freelance Digital Arts Engineer, Montréal QC Canada
PD-dev mailing list PD-dev@iem.at http://lists.puredata.info/listinfo/pd-dev
On Thu, 6 Oct 2005, Thomas Grill wrote:
maybe i'm a bit slow today, but (probably since i don't use desire.tk) could you explain again in other words what that is used for? Currently, i have no idea what "fields" are and what one can do with them.
Yeah, in the meanwhile I figured out that I hadn't given out enough info so another mail explains its role in observables and observers.
____________________________________________________________________ Mathieu Bouchard - tél:+1.514.383.3801 - http://artengine.ca/matju Freelance Digital Arts Engineer, Montréal QC Canada
On Thu, 6 Oct 2005, Mathieu Bouchard wrote:
hi everybody, I just added this: EXTERN void class_setfieldnames(t_class *x, const char *s);
Ok, now WHY I've added it: this is so that objects uploaded from server to client don't have to be completely uploaded each time. I number the fields so that I have a bitfield of which fields are "dirty".
This is very similar to how XSetWindowAttributes() sends data to an X11 server, except that it uses a bunch of #defines instead and it deals with only one struct type and is not extensible.
nota bene: the following is only enabled with "scons desire=1".
I am implementing an observer/observable interface in t_gobj. Because I want to keep ABI compatibility with most externals, I've taken out the one field I could, g_next, and replaced it with a pointer to t_observable called g_obs. The g_next field is now in that struct and so are the other infos we need:
typedef struct _observable { struct _gobj *master; /* pointer back to our t_gobj */ struct _gobj *next; /* formerly in t_gobj */ /* actual observable */ int dirty; /* bitset of fields */ int nobs; /* number of spies */ struct _gobj **obs; /* virtual func for observer */ void (*notice)(struct _gobj *x, struct _gobj *origin, int dirty); } t_observable;
And then there are a few functions for managing notifications.
EXTERN void observable_subscribe (t_observable *self, t_gobj *observer); EXTERN void observable_unsubscribe (t_observable *self, t_gobj *observer);
Those two manage the relationships so that the observables know where to forward their notifications.
EXTERN void observable_changed (t_observable *self, const char *k);
This records a change so that later a notification may be emitted.
EXTERN void observable_notify (t_observable *self);
This makes the observable notify its subscribers when asked to.
EXTERN void gobj_notice(t_gobj *x, t_gobj *origin, int dirty);
This is the function called on each subscribed object (observer). It must have had glist_add called on it and then g_obs->notify must be set to a valid function.
TODO:
* Pools of observables so that _notify may be batch-called on all of them at once during idle time (or something...)
* Support for notices on portions of array data (intervals of indices)
* Something that actually uses that API
____________________________________________________________________ Mathieu Bouchard - tél:+1.514.383.3801 - http://artengine.ca/matju Freelance Digital Arts Engineer, Montréal QC Canada
Ok, back from Norway, and even almost back from the cold I caught in the plane. Here's an update (no pun intended) on Observables.
On Thu, 6 Oct 2005, Mathieu Bouchard wrote:
I am implementing an observer/observable interface in t_gobj. typedef struct _observable { struct _gobj *master; /* pointer back to our t_gobj */ struct _gobj *next; /* formerly in t_gobj */ /* actual observable */ int dirty; /* bitset of fields */
I have removed this "dirty" thing, and then re-added it again.
First I figured out that t_observable was a misnomer, and instead should be called t_appendix. (it's separate from t_gobj for binary compatibility reasons, but isn't only about observables).
Then I figured out that whatever I had copied from Ruby's Observable had been copied from java.util.Observable which itself is not used by anybody at all, and for a reason.
I found out that the two-phase system of flagging an object as "changed" and then coming back later to notify-if-necessary is silly. There is a much bigger incentive to gather notices together in a central place so that we can figure out their chronology, save time, and manage things (uh) holistically. So instead,
EXTERN void observable_changed (t_observable *self, const char *k); EXTERN void observable_notify (t_observable *self);
is only one function now,
gobj_changed(t_gobj *self, const char *k);
/* virtual func for observer */ void (*notice)(struct _gobj *x, struct _gobj *origin, int dirty);
This has been moved to t_class, and you set it using the function:
typedef void (*t_notice)(struct _gobj *x, struct _gobj *origin, int argc, t_atom *argv); EXTERN void class_setnotice(t_class *c, t_notice notice);
Also,
EXTERN void observable_subscribe (t_observable *self, t_gobj *observer); EXTERN void observable_unsubscribe (t_observable *self, t_gobj *observer);
is now considered part of the t_gobj API, just like gobj_changed:
EXTERN void gobj_subscribe (t_gobj *self, t_gobj *observer); EXTERN void gobj_unsubscribe (t_gobj *self, t_gobj *observer);
Then Carmen (and others) asked me why i hadn't already added support for partial array updates. So then I added this (instead of adding partial array updates):
EXTERN void gobj_changed2 (void /*t_gobj*/ *self, int argc, t_atom *argv);
Which allows any list of atoms (but heap-allocated, unlike list-messages) to be a notice. I'm not too sure how we will really use it, but that should allow us not only to have partial array updates, but also partial updates of any kind imaginable.
Those two manage the relationships so that the observables know where to forward their notifications.
Then I found out that I didn't have a function that could take care of the forwarding, and so did I add:
EXTERN void gobj_changed3 (void /*t_gobj*/ *self, t_gobj *origin, int argc, t_atom *argv);
Where one specifies where the notice is coming from.
Then I figured out that I didn't have a hashtable class handy and didn't want to write one now so I added "dirty" fields back into the t_gobj's, except that now they belong to the t_manager, supposing each object has only one t_manager. This won't be true forever though, so later this will have to be replaced by hashtables.
The t_manager is the thing that manages a queue of objects that have to be updated (uploaded), in chronological order, without duplicates. When duplicates are encountered, the earliest one is kept. This establishes a priority for updating objects client-side. This allows to introduce a "bandwidth limiter" that gives equal chance to all objects that need to be updated.
When partial updates start to be supported for real, duplicate notices will be handled in smarter ways. They will be coalesced (merged). E.g. bitmasks of dirty fields will be OR'ed together, and extra notice info will be appended to each other or something.
____________________________________________________________________ Mathieu Bouchard - tél:+1.514.383.3801 - http://artengine.ca/matju Freelance Digital Arts Engineer, Montréal QC Canada