I've attached the reasoning behind my string patch, probably there is a lot wrong with it.

But I can't see how to do this kind of thing otherwise without modifying Pd.

Martin


Hans-Christoph Steiner wrote:
I don't know the details, but I believe it was just choosing an unused number to represent the type. Then also not using a keyword. Ultimately, there could be something like /etc/services where we can register these numbers. Or maybe they could just be included in the Pd headers.

http://lists.puredata.info/pipermail/pd-dev/2008-04/011257.html

.hc

On May 8, 2008, at 10:33 PM, Martin Peach wrote:

Could someone point me to IOhannes' technique? If it makes sense I'll give it a go during the next week.

Martin


From: Hans-Christoph Steiner <[EMAIL PROTECTED]>
To: pd-dev List <pd-dev@iem.at>
Subject: [PD-dev] removing string types from pd-extended release
Date: Thu, 8 May 2008 20:44:09 +0200


Hey,

We talked about this in the recent past.  Could someone who knows the
details remove the declarations of the string type from branches/pd-
extended/0.40?  Or shall I just remove the whole patch?  I want to
include string support, but using the technique that IOhannes laid
out, which should be possible without patching Pd (AFAIK).

.hc

--------------------------------------------------------------------- ---
----

Using ReBirth is like trying to play an 808 with a long stick.    -
David Zicarelli



_______________________________________________
PD-dev mailing list
PD-dev@iem.at
http://lists.puredata.info/listinfo/pd-dev



------------------------------------------------------------------------ ----

All information should be free.  - the hacker ethic





_______________________________________________
PD-dev mailing list
PD-dev@iem.at
http://lists.puredata.info/listinfo/pd-dev


I think it would be most efficient to have a string type be a length followed 
by that many unsigned chars,
similar to a Pascal string but with the length being something like a 32-bit 
integer.
It would not be added to pd's symbol list. The atom whose type was string would 
have
to contain a pointer to the first byte of the string, and a length. Multibyte 
characters would just
be counted as multiple characters when calculating the length, so the length 
would be the number of bytes
in the string, not the number of characters.
It looks too easy to me...In m_pd.h, add:
A_STRING
to t_atomtype at line 139 of m_pd.h.
Add
t_string * w_string;
to  t_word at line 122 of m_pd.h.
Add the typedef:
typedef struct _string /* pointer to a string */
{
   unsigned long s_length; /* length of string in bytes */
   unsigned char *s_data; /* pointer to 1st byte of string */
} t_string;
at line 106 of m_pd.h
...so a string atom would have a_type = A_STRING and a_w = a_w.w_string, which 
points to a t_string containing
the length and a pointer to the string.

This we did first and now we are compiling pd on linux to see if it breaks 
anything...not so far.

If pd is otherwise able to handle atom types it doesn't know about (?), all the 
string manipulation objects could
be built as externals.

As I see it the atom types are defined in m_pd.h and registered in m_class.c, 
which would need to have functions added
 to it to handle string, like adding:
t_symbol  s_string =    {"string", 0, 0};
to the list of t_symbols around line 555, and changing:
static t_symbol *symlist[] = { &s_pointer, &s_float, &s_symbol, &s_bang,
   &s_list, &s_anything, &s_signal, &s__N, &s__X, &s_x, &s_y, &s_};
to:
static t_symbol *symlist[] = { &s_pointer, &s_float, &s_symbol, &s_bang,
   &s_list, &s_anything, &s_signal, &s__N, &s__X, &s_x, &s_y, &s_, &s_string};
...and pd_typedmess needs a handler for string messages around line 658:
    if (s == &s_string)
    {
        if (argc == 1) (*c->c_stringmethod)(x, argv->a_w.w_string);
        else goto badarg;
        return;
    }
...and a handler for messages with string arguments with other names, just 
befor case A_SYMBOL, line 728:
            case A_STRING:/* MP 20070106 string type */
                post("pd_typedmess A_STRING");
                if (!argc) goto badarg;
                if (argv->a_type == A_STRING)
                {
                    post("argv->a_type == A_STRING, argc = %d, narg= %d", argc, 
narg);
                    *ap = (t_int)(argv->a_w.w_string);
                    mp = 1;
                }
                argc--;
                argv++;
                narg++;
                ap++;
                break;


Still in m_class.c, around line 344 we add:
void class_addstring(t_class *c, t_method fn)
{
    c->c_stringmethod = (t_stringmethod)fn;
}
This is used to register a string handler with pd so that incoming strings will 
be sent to fn.
We added:
    else if (sel == &s_string) /* MP 20070106 string type */
    {
        post("class_addmethod: %p", fn);
        if (argtype != A_STRING || va_arg(ap, t_atomtype)) goto phooey;
        class_addstring(c, fn);
    }
around line 300 of m_class.c in case someone calls class_addmethod instead of 
class_addstring with a string selector.

Then in m_pd.h, line 418:
EXTERN void class_addstring(t_class *c, t_method fn);
and at line 447:
#define class_addstring(x, y) class_addstring((x), (t_method)(y))
Also add:
EXTERN t_symbol s_string;
at line 226 of m_pd.h.

In  m_imp.h we need to define the stringmethod function at line 28:
typedef void (*t_stringmethod)(t_pd *x, t_string *st);
and, inside struct _class we add a place to store a pointer to the stringmethod 
at line 45:
    t_stringmethod c_stringmethod;
. As long as no externals are accessing struct _class directly, this should be 
safe.

Add to m_pd.c, line 287 the function to be called when a string arrives at an 
inlet:
void pd_string(t_pd *x, t_string *st)
{
    (*(*x)->c_stringmethod)(x, st);
}
We need to add a prototype for pd_string in m_pd.h at line 340ish:
EXTERN void pd_string(t_pd *x, t_string *st);


Along the lines of pd_defaultlist() in m_class.c, which handles list messages 
for objects that don't have list methods,
one could add a pd_defaultstring(), which attempts to convert strings into 
symbols/floats/lists, instead of calling
pd_defaultanything(), which would print "no method for string". But it needs to 
be understood that it might not do
it correctly, which is Not A Good Thing, but no worse than comments getting 
mangled. Maybe a [string unpack] object
would be better: it could attempt to unpack a string into specified types, so 
the user could decide if a string like
"123" is meant to represent a float or a symbol.

In class_new() in m_class.c we add:
static void pd_defaultstring(t_pd *x, t_string *st);
around line 22,
    c->c_stringmethod = pd_defaultstring;
at line 208.
Add
static void pd_defaultstring(t_pd *x, t_string *st)
{ /* for now just reject it, later convert to symbol/float/list */
    pd_error(x, "%s: no method for string so far...", (*x)->c_name->s_name);
}
around line 40.

And now outlets:
Add to m_pd.h, line 364ish:
EXTERN void outlet_string(t_outlet *x, t_string *st);
This will send a string through an outlet, by calling the pd_string method on 
all the inlets connected to the outlet.
It's defined in m_obj.c, line 369ish:
void outlet_string(t_outlet *x, t_string *st)
{
    t_outconnect *oc;
    if(++stackcount >= STACKITER)
        outlet_stackerror(x);
    else
    for (oc = x->o_connections; oc; oc = oc->oc_next)
        pd_string(oc->oc_to, st);
    --stackcount;
}
We defined pd_string in m_pd.c.
To add a string-capable outlet to an object, we already have outlet_new() in 
m_obj.c.

And inlets:
static void inlet_string(t_inlet *x, t_string *st)
{
    if (x->i_symfrom == &s_string)
        pd_vmess(x->i_dest, x->i_symto, "t", st);
    else if (!x->i_symfrom) pd_string(x->i_dest, st);
    else inlet_wrong(x, &s_string);
}
should be added around line 113 of m_obj.c.
What is x->i_symfrom? It is equal to s_string if the inlet is string-capable. 
See stringinlet_new, below.
When is it equal to zero?

Also in m_obj.c, obj_init needs a line around 255:
    class_addstring(inlet_class, inlet_string);
In m_class.c, line 794ish we add the line:
        case 't': SETSTRING(at, va_arg(ap, t_string *)); break;
to pd_vmess.
Now we need to add SETSTRING to m_pd.h, line 267ish:
#define SETSTRING(atom, st) ((atom)->a_type = A_STRING, (atom)->a_w.w_string = 
(st))

To be able to add the inlet to an object we need to add this to m_obj.c around 
line 200:
t_inlet *stringinlet_new(t_object *owner, t_string **stp)
{
    t_inlet *x = (t_inlet *)pd_new(stringinlet_class), *y, *y2;
    x->i_owner = owner;
    x->i_dest = 0;
    x->i_symfrom = &s_string;
    x->i_stringslot = stp;
    x->i_next = 0;
    if (y = owner->ob_inlet)
    {
        while (y2 = y->i_next) y = y2;
        y->i_next = x;
    }
    else owner->ob_inlet = x;
    return (x);
}

We need to add stringinlet_class at line 37 of m_obj.c:
static t_class *inlet_class, *pointerinlet_class, *floatinlet_class,
    *symbolinlet_class, *stringinlet_class;
Also define a stringslot and add it to the union inletunion at line 12 of 
m_obj.c:
union inletunion
{
    t_symbol *iu_symto;
    t_gpointer *iu_pointerslot;
    t_float *iu_floatslot;
    t_symbol **iu_symslot;
    t_string **iu_stringslot;
    t_sample iu_floatsignalvalue;
};
and at line 35ish:
#define i_stringslot i_un.iu_stringslot

Now the only thing that doesn't work properly is messages with strings 
replacing their $ arguments.
For instance, sending [append $1( to a [str join] should set the appended 
string (also set by the
 creation argument or the second inlet), but instead gives "no method for 
string" with a string,
  and crashes pd with non-string values.
Hop on over to g_text.c, where message lives.
There's a t_message class:
typedef struct _message
{
    t_text m_text;
    t_messresponder m_messresponder;
    t_glist *m_glist;
    t_clock *m_clock;
} t_message;
...and a t_messresponder which keeps the message's outlet:
typedef struct _messresponder
{
    t_pd mr_pd;
    t_outlet *mr_outlet;
} t_messresponder;
The message object is set up in g_text_setup, not message_setup as you might 
expect.
 I guess this is to stop a patcher from adding one of these as a [message] 
object.(?)
g_text_setup also sets up messresponder and gatom.
Less naively we add:
             case A_STRING:
                if (nargs == 1) pd_string(target, stackwas->a_w.w_string);
                else pd_list(target, 0, nargs, stackwas);
after gotmess in m_binbuf.c, line 678ish. This results in pd_string being 
called when a message receives a
 string, but because message's string method is default_string, the string 
doesn't get through.
So what we need to do now is set message's string method...
At line 1337 of g_text.c add:
    class_addstring(message_class, message_string);
and
static void message_string(t_message *x, t_string *st)
{
    t_atom at;
    SETSTRING(&at, st);
    binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, 1, &at);
}
at line 310.

Now we can send a string to a [set $1( message to [string join] and it will set 
the append string just as if the
 string went directly to the second inlet.
Unfortunately, other kinds of message will usually crash pd. Probably we need a 
way to be sure that an
 [append $1( or [compare $1( message really has a string attached. Or is the 
fact that we never
  specified a m_messresponder for string types to blame?
We add:
static void messresponder_string(t_messresponder *x, t_string *st)
{ /* MP 20070107 string type */
    outlet_string(x->mr_outlet, st);
}
at line 279 of g_text.c. Same thing happens.
Add
EXTERN t_string *atom_getstring(t_atom *a);
to m_pd.h line 280ish
and
t_string *atom_getstring(t_atom *a)  /* MP 20070108 */
{
    static unsigned char c = 0;/* a default string to avoid null pointers. This 
should be somewhere else...? */
    static t_string st = {1L, &c};
    if (a->a_type == A_STRING) return (a->a_w.w_string);
    else return (&st);
}
around line 36 of m_atom.c





All these additions should work without breaking anything else as long as 
externals access pd only via the
 functions exposed in m_pd.h. Probably we'll need to add somthing in g_io.c as 
well...


_______________________________________________
PD-dev mailing list
PD-dev@iem.at
http://lists.puredata.info/listinfo/pd-dev

Reply via email to