BTW though: I hadn't looked closely at the sources earlier, so I was conjecturing. Looked just now. If we take, say, "unpack" as an example of an object that typically has multiple outlets, and see how it responds to a list:
in m.connective.c:
static void unpack_list(t_unpack *x, t_symbol *s, int argc, t_atom *argv)
{
t_atom *ap;
t_unpackout *u;
int i;
if (argc > x->x_n) argc = (int)x->x_n;
for (i = argc, u = x->x_vec + i, ap = argv + i; u--, ap--, i--;)
{
t_atomtype type = u->u_type;
if (type != ap->a_type)
pd_error(x, "unpack: type mismatch");
else if (type == A_FLOAT)
outlet_float(u->u_outlet, ap->a_w.w_float);
else if (type == A_SYMBOL)
outlet_symbol(u->u_outlet, ap->a_w.w_symbol);
else outlet_pointer(u->u_outlet, ap->a_w.w_gpointer);
}
}
... where each output value calls either outlet_float() or outlet_symbol() or outlet_pointer(). It'll move to the next outlet when the outlet_ function returns.
m_obj.c:
void outlet_float(t_outlet *x, t_float f)
{
t_outconnect *oc;
if(!stackcount_add())
outlet_stackerror(x);
else
for (oc = x->o_connections; oc; oc = oc->oc_next)
pd_float(oc->oc_to, f);
stackcount_release();
}
OK, check for stack overflow and then call pd_float() for each connection from that outlet. (_bang, _symbol, _pointer are similar.)
m_pd.c:
void pd_float(t_pd *x, t_float f)
{
if (x == &pd_objectmaker)
((t_floatmethodr)(*(*x)->c_floatmethod))(x, f);
else
(*(*x)->c_floatmethod)(x, f);
}
Get a reference to the next object's floatmethod function, e.g. and call it: a method handler --> an iterator over connections --> a connection handler --> another object's method handler, at which point the cycle recurs.
So the way that it backtracks seems to be simply that e.g. outlet_float() returns and the method handler resumes where it left off: normal tree-traversal recursion using C's call stack (without data return, as these are all void
).
Gotos do appear in the Pd source but it looks like the vast majority of these are used for early-exit or for input validation before performing some operation -- goto error
, goto fail
, goto doit
, etc. I don't see them in the message-passing functions. (What I also don't see in the message-passing functions is an explicit stack of objects/connections -- stackcount_add()
is just a global counter, no real push/pop -- so if I guessed that pd keeps its own stack of objects visited, that was a wrong conjecture.)
hjh