/* flext - C++ layer for Max/MSP and pd (pure data) externals Copyright (c) 2001-2006 Thomas Grill (gr@grrrr.org) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. */ /*! \file fllib.cpp \brief Code for handling of object (and library) creation functions. */ #include "flext.h" #include "flinternal.h" #include #include #include #include #define ALIASDEL ',' #define ALIASSLASHES ":/\\" #if FLEXT_OS == FLEXT_OS_MAC #define ALIASSLASH ':' #elif FLEXT_OS == FLEXT_OS_WIN #if FLEXT_SYS == FLEXT_SYS_PD #define ALIASSLASH '/' #elif FLEXT_SYS == FLEXT_SYS_MAX #define ALIASSLASH '/' #else #error "Undefined" #endif #else // default to "/" #define ALIASSLASH '/' #endif //! Extract space-delimited words from a string static const char *extract(const char *name,int ix = 0) { char tmp[1024]; const char *n = name; const char *del = strchr(name,ALIASDEL); if(del) { #if 0 char *t = tmp; while(n < del && (isspace(*n) || strchr(ALIASSLASHES,*n))) ++n; while(n < del && !isspace(*n)) { char c = *(n++); *(t++) = strchr(ALIASSLASHES,c)?ALIASSLASH:c; } while(*t == ALIASSLASH && t > tmp) --t; *t = 0; #endif if(ix < 0) { // eat white space in front of help definition ++del; while(*del && isspace(*del)) ++del; return del; } strncpy(tmp,name,del-name); tmp[del-name] = 0; n = tmp; } else if(ix < 0) return NULL; // no explicit help name while(*n && isspace(*n)) ++n; for(int i = 0; n && *n; ++i) { if(i == ix) { char *t = tmp; for(; *n && !isspace(*n); ++t,++n) *t = *n; *t = 0; return *tmp?tmp:NULL; } else { while(*n && !isspace(*n)) ++n; while(*n && isspace(*n)) ++n; } } return NULL; } //! Check if object's name ends with a tilde bool flext::chktilde(const char *objname) { // int stplen = strlen(setupfun); bool tilde = true; //!strncmp(setupfun,"_tilde",6); if((objname[strlen(objname)-1] == '~'?1:0)^(tilde?1:0)) { if(tilde) error("flext: %s (no trailing ~) is defined as a tilde object",objname); else error("flext::check_tilde: %s is no tilde object",objname); return true; } else return false; } // this class stands for one library of objects // there can be more if flext is a shared library class flext_library { public: flext_library(const t_symbol *nm) : name(nm) #if FLEXT_SYS == FLEXT_SYS_MAX , clss(NULL),dsp(false) #endif {} const t_symbol *name; #if FLEXT_SYS == FLEXT_SYS_MAX t_class *clss; bool dsp; #endif }; // this class stands for one registered object // it holds the class, type flags, constructor and destructor of the object and the creation arg types // it will never be destroyed class flext_class: public flext_root { public: flext_class(t_class *&cl,flext_obj *(*newf)(int,t_atom *),void (*freef)(flext_hdr *)); t_class *const &clss; flext_obj *(*newfun)(int,t_atom *); void (*freefun)(flext_hdr *c); int argc; int *argv; flext_library *lib; bool dsp:1,noi:1,attr:1,dist:1; flext_base::ItemCont meths,attrs; }; flext_class::flext_class(t_class *&cl,flext_obj *(*newf)(int,t_atom *),void (*freef)(flext_hdr *)): clss(cl), newfun(newf),freefun(freef), argc(0),argv(NULL) , dist(false) {} typedef TablePtrMap LibMap; // static initialization (with constructor) doesn't work for Codewarrior static LibMap *libnames = NULL; //! Store or retrieve registered classes static flext_class *FindName(const t_symbol *s,flext_class *o = NULL) { if(!libnames) libnames = new LibMap; flext_class *cl = libnames->find(s); if(!cl && o) libnames->insert(s,cl = o); return cl; } t_class *flext_obj::getClass(t_classid cl) { return cl->clss; } bool flext_obj::HasAttributes(t_classid cl) { return cl->attr; } bool flext_obj::IsDSP(t_classid cl) { return cl->dsp; } bool flext_obj::HasDSPIn(t_classid cl) { return !cl->noi; } bool flext_obj::IsLib(t_classid cl) { return cl->lib != NULL; } bool flext_obj::HasAttributes() const { return clss->attr; } bool flext_obj::IsDSP() const { return clss->dsp; } bool flext_obj::HasDSPIn() const { return !clss->noi; } bool flext_obj::IsLib() const { return clss->lib != NULL; } #if FLEXT_SYS == FLEXT_SYS_MAX bool flext_obj::NeedDSP() const { return clss->dsp || (clss->lib && clss->lib->dsp); } #endif static flext_library *curlib = NULL; void flext_obj::lib_init(const char *name,void setupfun()) { // make new library instance curlib = new flext_library(MakeSymbol(name)); flext::Setup(); // first register all classes try { setupfun(); } catch(std::exception &x) { error("%s - %s",name,x.what()); return; } catch(char *txt) { error("%s - %s",name,txt); return; } catch(...) { error("%s - Unknown exception at library setup",name); return; } #if FLEXT_SYS == FLEXT_SYS_MAX // then see if we got DSP classes // for Max/MSP, the library is represented by a special object (class) registered at startup // all objects in the library are clones of that library object - they share the same class ::setup( (t_messlist **)&curlib->clss, (t_newmethod)obj_new,(t_method)obj_free, sizeof(flext_hdr),NULL,A_GIMME,A_NULL); // for all classes in library add methods flext_base::AddMessageMethods(curlib->clss,curlib->dsp,true); #endif curlib = NULL; } void flext_obj::obj_add(bool lib,bool dsp,bool noi,bool attr,const char *idname,const char *names,void setupfun(t_classid),flext_obj *(*newfun)(int,t_atom *),void (*freefun)(flext_hdr *),int argtp1,...) { // get first possible object name const t_symbol *nsym = MakeSymbol(extract(names)); #ifdef FLEXT_DEBUG if(dsp) chktilde(GetString(nsym)); #endif if(lib) { FLEXT_ASSERT(curlib); #if FLEXT_SYS == FLEXT_SYS_MAX curlib->dsp |= dsp; #endif } else { FLEXT_ASSERT(!curlib); // process_attributes = attr; } // set dynamic class pointer t_class **cl = #if FLEXT_SYS == FLEXT_SYS_MAX lib?&curlib->clss: #endif new t_class *; // register object class #if FLEXT_SYS == FLEXT_SYS_PD *cl = ::class_new( (t_symbol *)nsym, (t_newmethod)obj_new,(t_method)obj_free, sizeof(flext_hdr),CLASS_DEFAULT,A_GIMME,A_NULL); #elif FLEXT_SYS == FLEXT_SYS_MAX if(!lib) { ::setup( (t_messlist **)cl, (t_newmethod)obj_new,(t_method)obj_free, sizeof(flext_hdr),NULL,A_GIMME,A_NULL); // attention: in Max/MSP the *cl variable is not initialized after that call. // just the address is stored, the initialization then occurs with the first object instance! } #else #error Platform not implemented #endif // make new dynamic object flext_class *lo = new flext_class(*cl,newfun,freefun); lo->lib = curlib; lo->dsp = dsp; lo->noi = noi; lo->attr = attr; // post("ADDCLASS %s,%s = %p -> LIBOBJ %p -> %p (lib=%i,dsp=%i)",idname,names,*cl,lo,lo->clss,lib?1:0,dsp?1:0); // parse the argument type list and store it with the object if(argtp1 == FLEXTTPN_VAR) lo->argc = -1; else { int argtp,i; va_list marker; // parse a first time and count only va_start(marker,argtp1); for(argtp = argtp1; argtp != FLEXTTPN_NULL; ++lo->argc) argtp = (int)va_arg(marker,int); va_end(marker); lo->argv = new int[lo->argc]; // now parse and store va_start(marker,argtp1); for(argtp = argtp1,i = 0; i < lo->argc; ++i) { lo->argv[i] = argtp; argtp = (int)va_arg(marker,int); } va_end(marker); } // get unique class id t_classid clid = lo; // make help reference const char *helptxt = extract(names,-1); if(helptxt) { const char *sl = strchr(helptxt,'/'); if(sl && !sl[1]) // helptxt is only the path (path with trailing /) flext_obj::DefineHelp(clid,idname,helptxt,dsp); else // helptxt is path and patch name flext_obj::DefineHelp(clid,helptxt,NULL,dsp); } for(int ix = 0; ; ++ix) { // in this loop register all the possible aliases of the object const char *c = ix?extract(names,ix):GetString(nsym); if(!c || !*c) break; // add to name list const t_symbol *lsym = MakeSymbol(c); FindName(lsym,lo); #if FLEXT_SYS == FLEXT_SYS_PD if(ix > 0) // in PD the first name is already registered with class creation ::class_addcreator((t_newmethod)obj_new,(t_symbol *)lsym,A_GIMME,A_NULL); #elif FLEXT_SYS == FLEXT_SYS_MAX if(ix > 0 || lib) // in Max/MSP the first alias gets its name from the name of the object file, // unless it is a library (then the name can be different) ::alias(const_cast(c)); #else #error #endif } try { // call class setup function setupfun(clid); } catch(std::exception &x) { error("%s: %s",idname,x.what()); } catch(char *txt) { error("%s: %s",idname,txt); } catch(...) { error("%s - Unknown exception while initializing class",idname); } } #define NEWARGS 256 // must be larger than FLEXT_NEWARGS = 5 typedef flext_obj *(*libfun)(int,t_atom *); #if FLEXT_SYS == FLEXT_SYS_MAX flext_hdr *flext_obj::obj_new(const t_symbol *s,short _argc_,t_atom *argv) #else flext_hdr *flext_obj::obj_new(const t_symbol *s,int _argc_,t_atom *argv) #endif { flext_hdr *obj = NULL; flext_class *lo = FindName(s); if(lo) { // post("NEWOBJ %s = %p -> %p",GetString(s),lo,lo->clss); bool ok = true; t_atom args[NEWARGS]; int argc = _argc_; if(lo->attr) { argc = flext_base::CheckAttrib(argc,argv); } if(lo->argc >= 0) { #ifdef FLEXT_DEBUG if(lo->argc > FLEXT_MAXNEWARGS) { ERRINTERNAL(); ok = false; } #endif int misnum = 0; if(argc > lo->argc) { ok = false; misnum = 1; } for(int i = 0; ok && i < lo->argc; ++i) { switch(lo->argv[i]) { #if FLEXT_SYS != FLEXT_SYS_PD case FLEXTTPN_INT: case FLEXTTPN_DEFINT: if(i >= argc) if(lo->argv[i] == FLEXTTPN_DEFINT) SetInt(args[i],0); else { misnum = -1,ok = false; break; } else if(IsInt(argv[i])) args[i] = argv[i]; else if(IsFloat(argv[i])) SetInt(args[i],(int)GetFloat(argv[i])); else ok = false; break; #endif case FLEXTTPN_FLOAT: case FLEXTTPN_DEFFLOAT: if(i >= argc) if(lo->argv[i] == FLEXTTPN_DEFFLOAT) SetFloat(args[i],0); else { misnum = -1,ok = false; break; } else if(IsInt(argv[i])) SetFloat(args[i],(float)GetInt(argv[i])); else if(IsFloat(argv[i])) args[i] = argv[i]; else ok = false; break; case FLEXTTPN_SYM: case FLEXTTPN_DEFSYM: // \todo shall we analyze the patcher args????... should already be done! if(i >= argc) if(lo->argv[i] == FLEXTTPN_DEFSYM) SetSymbol(args[i],sym__); else { misnum = -1,ok = false; break; } else if(IsSymbol(argv[i])) // SetSymbol(args[i],GetParamSym(GetSymbol(argv[i]),NULL)); args[i] = argv[i]; else ok = false; break; } } if(!ok) if(misnum) error("%s: %s creation arguments",GetString(s),misnum < 0?"Not enough":"Too many"); else error("%s: Creation arguments do not match",GetString(s)); } if(ok) { try { #if FLEXT_SYS == FLEXT_SYS_PD obj = (flext_hdr *)::pd_new(lo->clss); #elif FLEXT_SYS == FLEXT_SYS_MAX obj = (flext_hdr *)::newobject(lo->clss); #else #error #endif flext_obj::m_holder = obj; flext_obj::m_holdclass = lo; flext_obj::m_holdname = s; flext_obj::initing = true; flext_obj::init_ok = true; // get actual flext object (newfun calls "new flext_obj()") if(lo->argc >= 0) obj->data = lo->newfun(lo->argc,args); else obj->data = lo->newfun(argc,argv); flext_obj::m_holder = NULL; flext_obj::m_holdclass = NULL; flext_obj::m_holdname = NULL; ok = obj->data && // check constructor exit flag flext_obj::init_ok; if(ok) { if(lo->attr) { // DON'T convert eventual patcher args here... this is done by the actual attribute stuff // so that the initial $- or #- be preserved! // store creation args for attribute initialization (inside flext_base::Init()) flext_obj::m_holdaargc = _argc_-argc; flext_obj::m_holdaargv = argv+argc; } else { flext_obj::m_holdaargc = 0; flext_obj::m_holdaargv = NULL; } // call virtual init function // here, inlets, outlets, methods and attributes can be set up ok = obj->data->Init(); flext_obj::initing = false; // call another virtual init function if(ok) ok = obj->data->Finalize(); flext_obj::m_holdaargc = 0; flext_obj::m_holdaargv = NULL; } } //try catch(std::exception &x) { error("%s - Exception while creating object: %s",GetString(s),x.what()); ok = false; } catch(char *txt) { error("%s - Exception while creating object: %s",GetString(s),txt); ok = false; } catch(...) { error("%s - Unknown exception while creating object",GetString(s)); ok = false; } flext_obj::initing = false; if(!ok) { // there was some init error, free object lo->freefun(obj); obj = NULL; } } } #ifdef FLEXT_DEBUG else #if FLEXT_SYS == FLEXT_SYS_MAX // in Max/MSP an object with the name of the library exists, even if not explicitly declared! // if(!lo->lib || s != lo->lib->name) #endif error("Class %s not found in library!",s->s_name); #endif return obj; } void flext_obj::obj_free(flext_hdr *h) { flext_hdr *hdr = (flext_hdr *)h; const t_symbol *name = hdr->data->thisNameSym(); flext_class *lcl = FindName(name); if(lcl) { try { flext_obj::exiting = true; // call virtual exit function hdr->data->Exit(); // now call object destructor and deallocate lcl->freefun(hdr); } //try catch(std::exception &x) { error("%s - Exception while destroying object: %s",GetString(name),x.what()); } catch(char *txt) { error("%s - Exception while destroying object: %s",GetString(name),txt); } catch(...) { error("%s - Unknown exception while destroying object",GetString(name)); } flext_obj::exiting = false; } #ifdef FLEXT_DEBUG else #if FLEXT_SYS == FLEXT_SYS_MAX // in Max/MSP an object with the name of the library exists, even if not explicitely declared! // if(!lo->lib || s != lo->lib->name) #endif error("Class %s not found in library!",name); #endif } t_class *flext_obj::thisClass() const { FLEXT_ASSERT(x_obj); return thisClassId()->clss; } void flext_base::SetDist(t_classid c,bool d) { c->dist = d; } bool flext_base::DoDist() const { return thisClassId()->dist; } flext_base::ItemCont *flext_base::ClMeths(t_classid c) { return &c->meths; } flext_base::ItemCont *flext_base::ClAttrs(t_classid c) { return &c->attrs; }