aboutsummaryrefslogtreecommitdiff
path: root/externals/grill/flext/source/flext.cpp
diff options
context:
space:
mode:
authorThomas Grill <xovo@users.sourceforge.net>2002-10-22 23:07:10 +0000
committerThomas Grill <xovo@users.sourceforge.net>2002-10-22 23:07:10 +0000
commitd62e56f4df9594f72ce501f5e19c974fd18e7295 (patch)
tree635d4af7a7c2425098e60ca277086ec436b617f7 /externals/grill/flext/source/flext.cpp
parentc6f373c281ecb5cd1f4aa7a070e15cc61ab8793c (diff)
This commit was generated by cvs2svn to compensate for changes in r186,
which included commits to RCS files with non-trunk default branches. svn path=/trunk/; revision=187
Diffstat (limited to 'externals/grill/flext/source/flext.cpp')
-rw-r--r--externals/grill/flext/source/flext.cpp748
1 files changed, 748 insertions, 0 deletions
diff --git a/externals/grill/flext/source/flext.cpp b/externals/grill/flext/source/flext.cpp
new file mode 100644
index 00000000..5ed852c3
--- /dev/null
+++ b/externals/grill/flext/source/flext.cpp
@@ -0,0 +1,748 @@
+/*
+
+flext - C++ layer for Max/MSP and pd (pure data) externals
+
+Copyright (c) 2001,2002 Thomas Grill (xovo@gmx.net)
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see the file, "license.txt," in this distribution.
+
+*/
+
+/*! \file flext.cpp
+ \brief Implementation of the flext base class.
+*/
+
+#include "flext.h"
+#include "flinternal.h"
+#include <string.h>
+#include <stdarg.h>
+
+
+// === proxy class for flext_base ============================
+
+#ifdef PD
+
+static t_class *px_class;
+
+struct flext_base::px_object // no virtual table!
+{
+ t_object obj; // MUST reside at memory offset 0
+ flext_base *base;
+ int index;
+
+ void init(flext_base *b,int ix) { base = b; index = ix; }
+ static void px_method(px_object *c,const t_symbol *s,int argc,t_atom *argv);
+};
+
+
+void flext_base::px_object::px_method(px_object *obj,const t_symbol *s,int argc,t_atom *argv)
+{
+ obj->base->m_methodmain(obj->index,s,argc,argv);
+}
+
+void flext_base::cb_px_anything(t_class *c,const t_symbol *s,int argc,t_atom *argv)
+{
+ thisObject(c)->m_methodmain(0,s,argc,argv);
+}
+
+#define DEF_IN_FT(IX) \
+void flext_base::cb_px_ft ## IX(t_class *c,float v) { \
+ t_atom atom; SETFLOAT(&atom,v); \
+ thisObject(c)->m_methodmain(IX,&s_float,1,&atom); \
+}
+
+#define ADD_IN_FT(IX) \
+add_method1(c,cb_px_ft ## IX,"ft" #IX,A_FLOAT)
+
+#elif defined(MAXMSP)
+
+void flext_base::cb_px_anything(t_class *c,const t_symbol *s,int argc,t_atom *argv)
+{
+ // check if inlet allows anything (or list)
+
+ flext_base *o = thisObject(c);
+ int ci = ((flext_hdr *)o->x_obj)->curinlet;
+
+ o->m_methodmain(ci,s,argc,argv);
+}
+
+void flext_base::cb_px_int(t_class *c,int v)
+{
+ // check if inlet allows int type
+ t_atom atom;
+ SETINT(&atom,v);
+ cb_px_anything(c,sym_int,1,&atom);
+}
+
+void flext_base::cb_px_float(t_class *c,float v)
+{
+ // check if inlet allows float type
+ t_atom atom;
+ SETFLOAT(&atom,v);
+ cb_px_anything(c,sym_float,1,&atom);
+}
+
+void flext_base::cb_px_bang(t_class *c)
+{
+ // check if inlet allows bang
+ cb_px_anything(c,sym_bang,0,NULL);
+}
+
+
+#define DEF_IN_FT(IX) \
+void flext_base::cb_px_in ## IX(t_class *c,int v) { long &ci = ((flext_hdr *)thisObject(c)->x_obj)->curinlet; ci = IX; cb_px_int(c,v); ci = 0; } \
+void flext_base::cb_px_ft ## IX(t_class *c,float v) { long &ci = ((flext_hdr *)thisObject(c)->x_obj)->curinlet; ci = IX; cb_px_float(c,v); ci = 0; }
+
+#define ADD_IN_FT(IX) \
+add_method1(c,cb_px_in ## IX,"in" #IX,A_INT); \
+add_method1(c,cb_px_ft ## IX,"ft" #IX,A_FLOAT)
+
+#endif // MAXMSP
+
+
+DEF_IN_FT(1)
+DEF_IN_FT(2)
+DEF_IN_FT(3)
+DEF_IN_FT(4)
+DEF_IN_FT(5)
+DEF_IN_FT(6)
+DEF_IN_FT(7)
+DEF_IN_FT(8)
+DEF_IN_FT(9)
+
+
+
+// === flext_base ============================================
+
+bool flext_base::compatibility = true;
+
+flext_base::flext_base():
+ inlist(NULL),outlist(NULL),
+ incnt(0),outcnt(0),
+ insigs(0),outsigs(0),
+ curtag(NULL),
+ outlets(NULL),inlets(NULL),outattr(NULL),
+ methhead(NULL),attrhead(NULL),attrcnt(0),
+ distmsgs(false)
+{
+ LOG1("%s - flext logging is on",thisName());
+
+#ifdef FLEXT_THREADS
+ thrid = pthread_self();
+
+ shouldexit = false;
+ thrhead = thrtail = NULL;
+#endif
+ qhead = qtail = NULL;
+ qclk = (t_qelem *)(qelem_new(this,(t_method)QTick));
+#ifdef MAXMSP
+ yclk = (t_clock *)(clock_new(this,(t_method)YTick));
+#endif
+
+ AddMethod(0,"getattributes",(methfun)cb_ListAttrib);
+}
+
+flext_base::~flext_base()
+{
+#ifdef FLEXT_THREADS
+ // wait for thread termination
+ shouldexit = true;
+ for(int wi = 0; thrhead && wi < 100; ++wi) Sleep(0.01f);
+
+#ifdef PD
+ qmutex.Lock(); // Lock message queue
+ tlmutex.Lock();
+ // timeout -> hard termination
+ while(thrhead) {
+ thr_entry *t = thrhead;
+ if(pthread_cancel(t->thrid)) post("%s - Thread could not be terminated!",thisName());
+ thrhead = t->nxt;
+ t->nxt = NULL; delete t;
+ }
+ tlmutex.Unlock();
+ qmutex.Unlock();
+#else
+#pragma message ("No tread cancelling")
+#endif
+
+#endif
+
+ // send remaining pending messages
+ while(qhead) QTick(this);
+ qelem_free((t_qelem *)qclk);
+#ifdef MAXMSP
+ clock_free((object *)yclk);
+#endif
+
+ if(inlist) delete inlist;
+ if(outlist) delete outlist;
+ if(outlets) delete[] outlets;
+
+ if(inlets) {
+ for(int ix = 0; ix < incnt; ++ix)
+ if(inlets[ix]) {
+#ifdef PD
+ pd_free(&inlets[ix]->obj.ob_pd);
+#elif defined(MAXMSP)
+ freeobject((object *)inlets[ix]);
+#endif
+ }
+ delete[] inlets;
+ }
+
+#ifdef MAXMSP
+// if(insigs) dsp_free(thisHdr());
+ if(insigs) dsp_freebox(thisHdr());
+#endif
+
+ if(methhead) delete methhead;
+ if(attrhead) delete attrhead;
+}
+
+
+bool flext_base::Init()
+{
+// if(!flext_obj::Init()) return false;
+
+ bool ok = true;
+
+ incnt = insigs = 0;
+
+ if(inlets) {
+ for(int ix = 0; ix < incnt; ++ix)
+ if(inlets[ix]) {
+#ifdef PD
+ pd_free(&inlets[ix]->obj.ob_pd);
+#elif defined(MAXMSP)
+ freeobject(inlets[ix]);
+#endif
+ }
+ delete[] inlets;
+ inlets = NULL;
+ }
+
+ if(inlist) {
+ xlet *xi;
+ incnt = 0;
+ for(xi = inlist; xi; xi = xi->nxt) ++incnt;
+ xlet::type *list = new xlet::type[incnt];
+ int i;
+ for(xi = inlist,i = 0; xi; xi = xi->nxt,++i) list[i] = xi->tp;
+ delete inlist; inlist = NULL;
+
+ inlets = new px_object *[incnt];
+ for(i = 0; i < incnt; ++i) inlets[i] = NULL;
+
+ // type info is now in list array
+#ifdef PD
+ {
+ int cnt = 0;
+
+ if(incnt >= 1) {
+ switch(list[0]) {
+ case xlet::tp_sig:
+ CLASS_MAINSIGNALIN(thisClass(),flext_hdr,defsig);
+ ++insigs;
+ break;
+ default:
+ // leftmost inlet is already there...
+ break;
+ }
+ ++cnt;
+ }
+
+ for(int ix = 1; ix < incnt; ++ix,++cnt) {
+ switch(list[ix]) {
+ case xlet::tp_float:
+ case xlet::tp_int: {
+ char sym[] = "ft??";
+ if(ix >= 10) {
+ if(compatibility) {
+ // Max allows max. 9 inlets
+ post("%s: Only 9 float/int inlets allowed in compatibility mode",thisName());
+ ok = false;
+ }
+ else {
+ if(ix > 99)
+ post("%s: Inlet index > 99 not allowed for float/int inlets",thisName());
+ sym[2] = '0'+ix/10,sym[3] = '0'+ix%10;
+ }
+ }
+ else
+ sym[2] = '0'+ix,sym[3] = 0;
+ if(ok) inlet_new(&x_obj->obj, &x_obj->obj.ob_pd, &s_float, gensym(sym));
+ break;
+ }
+ case xlet::tp_sym:
+ (inlets[ix] = (px_object *)pd_new(px_class))->init(this,ix); // proxy for 2nd inlet messages
+ inlet_new(&x_obj->obj,&inlets[ix]->obj.ob_pd, &s_symbol, &s_symbol);
+ break;
+ case xlet::tp_list:
+ (inlets[ix] = (px_object *)pd_new(px_class))->init(this,ix); // proxy for 2nd inlet messages
+ inlet_new(&x_obj->obj,&inlets[ix]->obj.ob_pd, &s_list, &s_list);
+ break;
+ case xlet::tp_any:
+ (inlets[ix] = (px_object *)pd_new(px_class))->init(this,ix); // proxy for 2nd inlet messages
+ inlet_new(&x_obj->obj,&inlets[ix]->obj.ob_pd, 0, 0);
+ break;
+ case xlet::tp_sig:
+ if(compatibility && list[ix-1] != xlet::tp_sig) {
+ post("%s: All signal inlets must be left-aligned in compatibility mode",thisName());
+ ok = false;
+ }
+ else {
+ // pd doesn't seem to be able to handle signals and messages into the same inlet...
+
+ inlet_new(&x_obj->obj, &x_obj->obj.ob_pd, &s_signal, &s_signal);
+ ++insigs;
+ }
+ break;
+ default:
+ error("%s: Wrong type for inlet #%i: %i",thisName(),ix,(int)list[ix]);
+ ok = false;
+ }
+ }
+
+ incnt = cnt;
+ }
+#elif defined(MAXMSP)
+ {
+ int ix,cnt;
+ // count leftmost signal inlets
+ while(insigs < incnt && list[insigs] == xlet::tp_sig) ++insigs;
+
+ for(cnt = 0,ix = incnt-1; ix >= insigs; --ix,++cnt) {
+ if(ix == 0) {
+ if(list[ix] != xlet::tp_any) {
+ error("%s: Leftmost inlet must be of type signal or default",thisName());
+ ok = false;
+ }
+ }
+ else {
+ switch(list[ix]) {
+ case xlet::tp_sig:
+ error("%s: All signal inlets must be at the left side",thisName());
+ ok = false;
+ break;
+ case xlet::tp_float:
+ if(ix >= 10) {
+ post("%s: Only 9 float inlets possible",thisName());
+ ok = false;
+ }
+ else
+ floatin(x_obj,ix);
+ break;
+ case xlet::tp_int:
+ if(ix >= 10) {
+ post("%s: Only 9 int inlets possible",thisName());
+ ok = false;
+ }
+ else
+ intin(x_obj,ix);
+ break;
+ case xlet::tp_any: // non-leftmost
+ case xlet::tp_sym:
+ case xlet::tp_list:
+ inlets[ix] = (px_object *)proxy_new(x_obj,ix,&((flext_hdr *)x_obj)->curinlet);
+ break;
+ default:
+ error("%s: Wrong type for inlet #%i: %i",thisName(),ix,(int)list[ix]);
+ ok = false;
+ }
+ }
+ }
+
+ incnt = cnt;
+
+ if(insigs)
+// dsp_setup(thisHdr(),insigs); // signal inlets
+ dsp_setupbox(thisHdr(),insigs); // signal inlets
+ }
+#endif
+
+ delete[] list;
+ }
+
+ if(outlets) { delete[] outlets; outlets = NULL; }
+ outcnt = outsigs = 0;
+
+ if(outlist) {
+ xlet *xi;
+
+ // count outlets
+ outcnt = 0;
+ for(xi = outlist; xi; xi = xi->nxt) ++outcnt;
+
+ xlet::type *list = new xlet::type[outcnt];
+ int i;
+ for(xi = outlist,i = 0; xi; xi = xi->nxt,++i) list[i] = xi->tp;
+ delete outlist; outlist = NULL;
+
+ outlets = new outlet *[outcnt];
+
+ // type info is now in list array
+#ifdef PD
+ for(int ix = 0; ix < outcnt; ++ix)
+#elif defined(MAXMSP)
+ for(int ix = outcnt-1; ix >= 0; --ix)
+#endif
+ {
+ switch(list[ix]) {
+ case xlet::tp_float:
+ outlets[ix] = (outlet *)newout_float(&x_obj->obj);
+ break;
+ case xlet::tp_int:
+ outlets[ix] = (outlet *)newout_flint(&x_obj->obj);
+ break;
+ case xlet::tp_sig:
+ outlets[ix] = (outlet *)newout_signal(&x_obj->obj);
+ ++outsigs;
+ break;
+ case xlet::tp_sym:
+ outlets[ix] = (outlet *)newout_symbol(&x_obj->obj);
+ break;
+ case xlet::tp_list:
+ outlets[ix] = (outlet *)newout_list(&x_obj->obj);
+ break;
+ case xlet::tp_any:
+ outlets[ix] = (outlet *)newout_anything(&x_obj->obj);
+ break;
+#ifdef _DEBUG
+ default:
+ ERRINTERNAL();
+ ok = false;
+#endif
+ }
+ }
+
+ delete[] list;
+ }
+
+ if(procattr)
+ // attribute dump outlet is the last one
+ outattr = (outlet *)newout_anything(&x_obj->obj);
+
+ return ok;
+}
+
+void flext_base::Setup(t_class *c)
+{
+ add_method(c,cb_help,"help");
+ add_loadbang(c,cb_loadbang);
+#ifdef MAXMSP
+ add_assist(c,cb_assist);
+#endif
+
+ // proxy for extra inlets
+#ifdef PD
+ add_anything(c,cb_px_anything); // for leftmost inlet
+ px_class = class_new(gensym("flext_base proxy"),NULL,NULL,sizeof(px_object),CLASS_PD|CLASS_NOINLET, A_NULL);
+ add_anything(px_class,px_object::px_method); // for other inlets
+#elif defined(MAXMSP)
+ add_bang(c,cb_px_bang);
+ add_method1(c,cb_px_int,"int",A_INT);
+ add_method1(c,cb_px_float,"float",A_FLOAT);
+ add_methodG(c,cb_px_anything,"list");
+ add_anything(c,cb_px_anything);
+#endif
+
+ // setup non-leftmost ints and floats
+ ADD_IN_FT(1);
+ ADD_IN_FT(2);
+ ADD_IN_FT(3);
+ ADD_IN_FT(4);
+ ADD_IN_FT(5);
+ ADD_IN_FT(6);
+ ADD_IN_FT(7);
+ ADD_IN_FT(8);
+ ADD_IN_FT(9);
+}
+
+void flext_base::cb_help(t_class *c) { thisObject(c)->m_help(); }
+
+void flext_base::cb_loadbang(t_class *c) { thisObject(c)->m_loadbang(); }
+#ifdef MAXMSP
+void flext_base::cb_assist(t_class *c,void * /*b*/,long msg,long arg,char *s) { thisObject(c)->m_assist(msg,arg,s); }
+#endif
+
+void flext_base::m_help()
+{
+ // This should better be overloaded
+ post("%s (using flext " FLEXT_VERSTR ") - compiled on %s %s",thisName(),__DATE__,__TIME__);
+}
+
+
+bool flext_base::m_methodmain(int inlet,const t_symbol *s,int argc,t_atom *argv)
+{
+ static bool trap = false;
+ bool ret = false;
+
+ curtag = s;
+
+ LOG3("methodmain inlet:%i args:%i symbol:%s",inlet,argc,s?s->s_name:"");
+
+ for(const methitem *m = methhead; m && !ret; m = m->nxt) {
+ if(m->tag == s && (inlet == m->inlet || m->inlet < 0 )) {
+ // tag fits
+ LOG4("found method tag %s: inlet=%i, symbol=%s, argc=%i",m->tag->s_name,inlet,s->s_name,argc);
+
+ if(m->argc == 1 && m->args[0] == a_list) {
+ ret = ((methfun_V)m->fun)(this,argc,argv);
+ }
+ else if(m->argc == 1 && m->args[0] == a_any) {
+ ret = ((methfun_A)m->fun)(this,s,argc,argv);
+ }
+ else if(argc == m->argc) {
+ int ix;
+ t_any aargs[FLEXT_MAXMETHARGS];
+ bool ok = true;
+ for(ix = 0; ix < argc && ok; ++ix) {
+ switch(m->args[ix]) {
+ case a_float: {
+ if(IsFloat(argv[ix])) aargs[ix].ft = GetFloat(argv[ix]);
+ else if(IsInt(argv[ix])) aargs[ix].ft = (float)GetInt(argv[ix]);
+ else ok = false;
+
+ if(ok) LOG2("int arg %i = %f",ix,aargs[ix].ft);
+ break;
+ }
+ case a_int: {
+ if(IsFloat(argv[ix])) aargs[ix].it = (int)GetFloat(argv[ix]);
+ else if(IsInt(argv[ix])) aargs[ix].it = GetInt(argv[ix]);
+ else ok = false;
+
+ if(ok) LOG2("float arg %i = %i",ix,aargs[ix].it);
+ break;
+ }
+ case a_symbol: {
+ if(IsSymbol(argv[ix])) aargs[ix].st = GetSymbol(argv[ix]);
+ else ok = false;
+
+ if(ok) LOG2("symbol arg %i = %s",ix,GetString(aargs[ix].st));
+ break;
+ }
+#ifdef PD
+ case a_pointer: {
+ if(IsPointer(argv[ix])) aargs[ix].pt = GetPointer(argv[ix]);
+ else ok = false;
+ break;
+ }
+#endif
+ default:
+ error("Argument type illegal");
+ ok = false;
+ }
+ }
+
+ if(ok && ix == argc) {
+ switch(argc) {
+ case 0: ret = ((methfun_0)m->fun)(this); break;
+ case 1: ret = ((methfun_1)m->fun)(this,aargs[0]); break;
+ case 2: ret = ((methfun_2)m->fun)(this,aargs[0],aargs[1]); break;
+ case 3: ret = ((methfun_3)m->fun)(this,aargs[0],aargs[1],aargs[2]); break;
+ case 4: ret = ((methfun_4)m->fun)(this,aargs[0],aargs[1],aargs[2],aargs[3]); break;
+ case 5: ret = ((methfun_5)m->fun)(this,aargs[0],aargs[1],aargs[2],aargs[3],aargs[4]); break;
+ }
+ }
+ }
+ }
+ else if(m->tag == sym_symbol && !argc && (inlet == m->inlet || m->inlet < 0 )) {
+ // symbol
+ LOG3("found symbol method for %s: inlet=%i, symbol=%s",m->tag->s_name,inlet,s->s_name);
+
+ t_any sym; sym.st = const_cast<t_symbol *>(s);
+ ret = ((methfun_1)m->fun)(this,sym);
+ }
+ else if(m->tag == sym_anything && (inlet == m->inlet || m->inlet < 0) && m->argc == 1 && m->args[0] == a_any) {
+ // any
+ LOG4("found any method for %s: inlet=%i, symbol=%s, argc=%i",m->tag->s_name,inlet,s->s_name,argc);
+
+ ret = ((methfun_A)m->fun)(this,s,argc,argv);
+ }
+ }
+
+#ifdef MAXMSP
+ // If float message is not explicitly handled: try int handler instead
+ if(!ret && argc == 1 && s == sym_float && !trap) {
+ t_atom fl;
+ SetInt(fl,GetAInt(argv[0]));
+ trap = true;
+ ret = m_methodmain(inlet,sym_int,1,&fl);
+ trap = false;
+ }
+
+ // If int message is not explicitly handled: try float handler instead
+ if(!ret && argc == 1 && s == sym_int && !trap) {
+ t_atom fl;
+ SetFloat(fl,GetAFloat(argv[0]));
+ trap = true;
+ ret = m_methodmain(inlet,sym_float,1,&fl);
+ trap = false;
+ }
+#endif
+
+ // If float or int message is not explicitly handled: try list handler instead
+ if(!ret && !trap && argc == 1 && (s == sym_float
+#ifdef MAXMSP
+ || s == sym_int
+#endif
+ )) {
+ t_atom list;
+ if(s == sym_float)
+ SetFloat(list,GetFloat(argv[0]));
+#ifdef MAXMSP
+ else if(s == sym_int)
+ SetInt(list,GetInt(argv[0]));
+#endif
+
+ trap = true;
+ ret = m_methodmain(inlet,sym_list,1,&list);
+ trap = false;
+ }
+
+ // If symbol message (pure anything without args) is not explicitly handled: try list handler instead
+ if(!ret && !trap && argc == 0) {
+ t_atom list;
+ SetSymbol(list,s);
+ trap = true;
+ ret = m_methodmain(inlet,sym_list,1,&list);
+ trap = false;
+ }
+
+ // if distmsgs is switched on then distribute list elements over inlets (Max/MSP behavior)
+ if(!ret && distmsgs && !trap && inlet == 0 && s == sym_list && insigs <= 1) {
+ int i = incnt;
+ if(i > argc) i = argc;
+ for(--i; i >= 0; --i) { // right to left distribution
+ const t_symbol *sym = NULL;
+ if(IsFloat(argv[i])) sym = sym_float;
+ else if(IsInt(argv[i])) sym = sym_int;
+ else if(IsSymbol(argv[i])) sym = sym_symbol;
+#ifdef PD
+ else if(IsPointer(argv[i])) sym = sym_pointer; // can pointer atoms occur here?
+#endif
+ if(sym) {
+ trap = true;
+ m_methodmain(i,sym,1,argv+i);
+ trap = false;
+ }
+ }
+
+ ret = true;
+ }
+
+ if(!ret && !trap) ret = m_method_(inlet,s,argc,argv);
+
+ curtag = NULL;
+
+ return ret; // true if appropriate handler was found and called
+}
+
+bool flext_base::m_method_(int inlet,const t_symbol *s,int argc,t_atom *argv)
+{
+//#ifdef _DEBUG
+ post("%s: message unhandled - inlet:%i args:%i symbol:%s",thisName(),inlet,argc,s?s->s_name:"");
+//#endif
+ return false;
+}
+
+
+flext_base::methitem::methitem(int in,const t_symbol *t):
+ inlet(in),tag(t),
+ fun(NULL),
+ argc(0),args(NULL),
+ nxt(NULL)
+{}
+
+flext_base::methitem::~methitem()
+{
+ if(nxt) delete nxt;
+ if(args) delete[] args;
+}
+
+void flext_base::methitem::SetArgs(methfun _fun,int _argc,metharg *_args)
+{
+ fun = _fun;
+ if(args) delete[] args;
+ argc = _argc,args = _args;
+}
+
+
+
+void flext_base::AddMethItem(methitem *m)
+{
+ if(methhead) {
+ methitem *mi;
+ for(mi = methhead; mi->nxt; mi = mi->nxt) {}
+ mi->nxt = m;
+ }
+ else
+ methhead = m;
+}
+/*
+const flext_base::methitem *flext_base::FindMethItem(int inlet,const t_symbol *tag,const methitem *st)
+{
+ const methitem *mi = st?st:mlst;
+ if(inlet < 0) {
+ for(; mi; mi = mi->nxt)
+ if(mi->tag == tag) break;
+ }
+ else {
+ for(; mi; mi = mi->nxt)
+ if(mi->inlet == inlet && mi->tag == tag) break;
+ }
+ return mi;
+}
+*/
+
+void flext_base::AddMethodDef(int inlet,const char *tag)
+{
+ AddMethItem(new methitem(inlet,tag?MakeSymbol(tag):NULL));
+}
+
+void flext_base::AddMethod(int inlet,const char *tag,methfun fun,metharg tp,...)
+{
+ methitem *mi = new methitem(inlet,MakeSymbol(tag));
+
+ va_list marker;
+
+ // at first just count the arg type list (in argc)
+ int argc = 0;
+ va_start(marker,tp);
+ metharg *args = NULL,arg = tp;
+ for(; arg != a_null; ++argc) arg = (metharg)va_arg(marker,int); //metharg);
+ va_end(marker);
+
+ if(argc > 0) {
+ if(argc > FLEXT_MAXMETHARGS) {
+ error("%s - method %s: only %i arguments are type-checkable: use variable argument list for more",thisName(),tag?tag:"?",FLEXT_MAXMETHARGS);
+ argc = FLEXT_MAXMETHARGS;
+ }
+
+ args = new metharg[argc];
+
+ va_start(marker,tp);
+ metharg a = tp;
+ for(int ix = 0; ix < argc; ++ix) {
+#ifdef _DEBUG
+ if(a == a_list && ix > 0) {
+ ERRINTERNAL();
+ }
+#endif
+#ifdef PD
+ if(a == a_pointer && flext_base::compatibility) {
+ post("Pointer arguments are not allowed in compatibility mode");
+ }
+#endif
+ args[ix] = a;
+ a = (metharg)va_arg(marker,int); //metharg);
+ }
+ va_end(marker);
+ }
+
+ mi->SetArgs(fun,argc,args);
+
+ AddMethItem(mi);
+}
+
+