fllib.cpp 11.7 KB
Newer Older
thomas's avatar
thomas committed
1
2
3
4
/* 

flext - C++ layer for Max/MSP and pd (pure data) externals

thomas's avatar
thomas committed
5
Copyright (c) 2001-2003 Thomas Grill (xovo@gmx.net)
thomas's avatar
thomas committed
6
7
8
9
10
For information on usage and redistribution, and for a DISCLAIMER OF ALL
WARRANTIES, see the file, "license.txt," in this distribution.  

*/

thomas's avatar
thomas committed
11
12
13
/*! \file fllib.cpp
    \brief Code for handling of object (and library) creation functions.
*/
thomas's avatar
thomas committed
14
15

#include "flext.h"
thomas's avatar
thomas committed
16
#include "flinternal.h"
thomas's avatar
thomas committed
17
18
19
20
21
22
23
24

#include <stdarg.h>
#include <string.h>
#include <ctype.h>

#define ALIASDEL ','

#define ALIASSLASHES ":/\\"
thomas's avatar
thomas committed
25
#if FLEXT_OS == FLEXT_OS_MAC
thomas's avatar
thomas committed
26
	#define ALIASSLASH ':'
thomas's avatar
thomas committed
27
28
29
#elif FLEXT_OS == FLEXT_OS_WIN
	#if FLEXT_SYS == FLEXT_SYS_PD
		#define ALIASSLASH '/'
thomas's avatar
thomas committed
30
31
	#elif FLEXT_SYS == FLEXT_SYS_MAX
		#define ALIASSLASH '/'
thomas's avatar
thomas committed
32
	#else
thomas's avatar
thomas committed
33
		#error "Undefined"
thomas's avatar
thomas committed
34
	#endif
thomas's avatar
thomas committed
35
#else
thomas's avatar
thomas committed
36
	// default to "/"
thomas's avatar
thomas committed
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
	#define ALIASSLASH '/'
#endif

//! Extract space-delimited words from a string
static const char *extract(const char *name,int ix = 0)
{
	static char tmp[1024];
	const char *n = name;
	
	const char *del = strchr(name,ALIASDEL);

	if(del) {
		if(ix < 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;

			return tmp;
		}

		n = del+1;
	}

	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
thomas's avatar
thomas committed
86
bool flext::chktilde(const char *objname)
thomas's avatar
thomas committed
87
88
89
90
91
92
{
//	int stplen = strlen(setupfun);
	bool tilde = true; //!strncmp(setupfun,"_tilde",6);

	if((objname[strlen(objname)-1] == '~'?1:0)^(tilde?1:0)) {
		if(tilde) 
thomas's avatar
thomas committed
93
			error("flext: %s (no trailing ~) is defined as a tilde object",objname);
thomas's avatar
thomas committed
94
		else
thomas's avatar
thomas committed
95
			error("flext::check_tilde: %s is no tilde object",objname);
thomas's avatar
thomas committed
96
97
98
99
100
101
102
103
104
105
106
		return true;
	} 
	else
		return false;
}



// 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
thomas's avatar
thomas committed
107
class libclass {
thomas's avatar
thomas committed
108
public:
thomas's avatar
thomas committed
109
	libclass(t_class *&cl,flext_obj *(*newf)(int,t_atom *),void (*freef)(flext_hdr *)); 
thomas's avatar
thomas committed
110
111
112
113
114
115
116
117
118
119
	
	flext_obj *(*newfun)(int,t_atom *);
	void (*freefun)(flext_hdr *c);

	t_class *const &clss;
	bool lib,dsp,attr;
	int argc;
	int *argv;
};

thomas's avatar
thomas committed
120
libclass::libclass(t_class *&cl,flext_obj *(*newf)(int,t_atom *),void (*freef)(flext_hdr *)): 
thomas's avatar
thomas committed
121
122
123
	newfun(newf),freefun(freef),
	clss(cl),
	argc(0),argv(NULL) 
thomas's avatar
thomas committed
124
125
126
127
128
129
130
131
{}
	
// this class stands for one registered object name
// it holds a pointer to the respective object
// it will never be destroyed
class libname {
public:
	const t_symbol *name;
thomas's avatar
thomas committed
132
	libclass *obj;
thomas's avatar
thomas committed
133
134

	static void add(libname *n);
thomas's avatar
thomas committed
135
	static libname *Find(const t_symbol *s,libclass *o = NULL);
thomas's avatar
thomas committed
136
137
	
protected:
thomas's avatar
thomas committed
138
	libname(const t_symbol *n,libclass *o): name(n),obj(o),nxt(NULL) {}	
thomas's avatar
thomas committed
139
140
141
142
143
144

	static int Hash(const t_symbol *s);
	int Hash() const { return Hash(name); }

	enum { HASHBITS=7, HASHSIZE=1<<HASHBITS };

thomas's avatar
thomas committed
145
	libname *nxt;
thomas's avatar
thomas committed
146
147
148
	void Add(libname *n);

	static libname **root;
thomas's avatar
thomas committed
149
150
};

thomas's avatar
thomas committed
151
void libname::Add(libname *n) { if(nxt) nxt->Add(n); else nxt = n; }
thomas's avatar
thomas committed
152

thomas's avatar
thomas committed
153
154
155
int libname::Hash(const t_symbol *s) 
{
	return flext::FoldBits(reinterpret_cast<unsigned long>(s),HASHBITS);
thomas's avatar
thomas committed
156
157
}

thomas's avatar
thomas committed
158
libname *libname::Find(const t_symbol *s,libclass *o) 
thomas's avatar
thomas committed
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
{
	if(!root) {
		root = new libname *[HASHSIZE];
		memset(root,0,HASHSIZE*sizeof(*root));
	}

	int hash = Hash(s);
	libname *a = root[hash];
	libname *pa = NULL;
	while(a && a->name != s) pa = a,a = a->nxt;

	if(!a && o) {
		a = new libname(s,o);
		if(pa) 
			// previous entry... extend
			a->nxt = pa->nxt,pa->nxt = a;
		else 
			// new singular entry
			root[hash] = a;
	}

	return a;
thomas's avatar
thomas committed
181
182
}

thomas's avatar
thomas committed
183
184
185
libname **libname::root = NULL;


thomas's avatar
thomas committed
186

thomas's avatar
thomas committed
187
// for Max/MSP, the library is represented by a special object (class) registered at startup
thomas's avatar
thomas committed
188
// all objects in the library are clones of that library object - they share the same class
thomas's avatar
thomas committed
189
#if FLEXT_SYS == FLEXT_SYS_MAX
thomas's avatar
thomas committed
190
191
static t_class *lib_class = NULL;
static const t_symbol *lib_name = NULL;
thomas's avatar
thomas committed
192
193

flext_obj::t_classid flext_obj::thisClassId() const { return libname::Find(thisNameSym())->obj; }
thomas's avatar
thomas committed
194
t_class *flext_obj::getClass(t_classid id) { return reinterpret_cast<libclass *>(id)->clss; }
thomas's avatar
thomas committed
195
196
197
198
#endif

void flext_obj::lib_init(const char *name,void setupfun(),bool attr)
{
199
    flext::Setup();
thomas's avatar
thomas committed
200

thomas's avatar
thomas committed
201
#if FLEXT_SYS == FLEXT_SYS_MAX
thomas's avatar
thomas committed
202
203
204
205
206
207
208
209
210
211
	lib_name = MakeSymbol(name);
	::setup(
		(t_messlist **)&lib_class,
		(t_newmethod)obj_new,(t_method)obj_free,
		sizeof(flext_hdr),NULL,A_GIMME,A_NULL);
#endif
	process_attributes = attr;
	setupfun();
}

thomas's avatar
thomas committed
212
213
214
215
216
217
218
#if FLEXT_SYS == FLEXT_SYS_JMAX
static void jmax_class_inst(t_class *cl) 
{
	fts_class_init(cl, sizeof(flext_hdr),flext_obj::obj_new,flext_obj::obj_free);
}
#endif

thomas's avatar
thomas committed
219
void flext_obj::obj_add(bool lib,bool dsp,bool attr,const char *idname,const char *names,void setupfun(t_classid),flext_obj *(*newfun)(int,t_atom *),void (*freefun)(flext_hdr *),int argtp1,...)
thomas's avatar
thomas committed
220
221
222
223
{
	// get first possible object name
	const t_symbol *nsym = MakeSymbol(extract(names));
	
thomas's avatar
thomas committed
224
#ifdef FLEXT_DEBUG
thomas's avatar
thomas committed
225
226
227
228
229
230
231
	if(dsp) chktilde(GetString(nsym));
#endif

	if(!lib) process_attributes = attr;

	// set dynamic class pointer
	t_class **cl = 
thomas's avatar
thomas committed
232
#if FLEXT_SYS == FLEXT_SYS_MAX
thomas's avatar
thomas committed
233
234
235
236
237
		lib?&lib_class:
#endif
		new t_class *;

	// register object class
thomas's avatar
thomas committed
238
#if FLEXT_SYS == FLEXT_SYS_PD
thomas's avatar
thomas committed
239
240
241
    *cl = ::class_new(
		(t_symbol *)nsym,
    	(t_newmethod)obj_new,(t_method)obj_free,
thomas's avatar
thomas committed
242
     	sizeof(flext_hdr),CLASS_DEFAULT,A_GIMME,A_NULL);
thomas's avatar
thomas committed
243
#elif FLEXT_SYS == FLEXT_SYS_MAX
thomas's avatar
thomas committed
244
245
246
247
248
	if(!lib) {
		::setup(
			(t_messlist **)cl,
    		(t_newmethod)obj_new,(t_method)obj_free,
     		sizeof(flext_hdr),NULL,A_GIMME,A_NULL);
thomas's avatar
thomas committed
249
250
     	// 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!
thomas's avatar
thomas committed
251
	}
thomas's avatar
thomas committed
252
253
#elif FLEXT_SYS == FLEXT_SYS_JMAX
	*cl = fts_class_install(nsym, jmax_class_inst);
thomas's avatar
thomas committed
254
255
#else
#error
thomas's avatar
thomas committed
256
257
258
#endif

	// make new dynamic object
thomas's avatar
thomas committed
259
	libclass *lo = new libclass(*cl,newfun,freefun);
thomas's avatar
thomas committed
260
261
262
263
	lo->lib = lib;
	lo->dsp = dsp;
	lo->attr = process_attributes;

thomas's avatar
thomas committed
264
265
//	post("ADDCLASS %p -> LIBOBJ %p -> %p",*cl,lo,lo->clss);

thomas's avatar
thomas committed
266
	// parse the argument type list and store it with the object
thomas's avatar
thomas committed
267
	if(argtp1 == FLEXTTPN_VAR)
thomas's avatar
thomas committed
268
269
270
271
272
273
274
		lo->argc = -1;
	else {
		int argtp,i;
		va_list marker;
		
		// parse a first time and count only
		va_start(marker,argtp1);
thomas's avatar
thomas committed
275
		for(argtp = argtp1; argtp != FLEXTTPN_NULL; ++lo->argc) argtp = (int)va_arg(marker,int); 
thomas's avatar
thomas committed
276
277
278
279
280
281
282
283
284
285
286
287
288
		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);
	}

thomas's avatar
thomas committed
289
	// get unique class id
thomas's avatar
thomas committed
290
#if FLEXT_SYS == FLEXT_SYS_PD || FLEXT_SYS == FLEXT_SYS_JMAX
thomas's avatar
thomas committed
291
292
293
294
295
296
	t_classid clid = lo->clss;
#else
	// in Max/MSP the t_class *value can't be used because it's possible that's it's not yet set!!
	t_classid clid = lo;
#endif

thomas's avatar
thomas committed
297
	// make help reference
thomas's avatar
thomas committed
298
	flext_obj::DefineHelp(clid,idname,extract(names,-1),dsp);
thomas's avatar
thomas committed
299
300
301
302
303
304
305
306

	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
thomas's avatar
thomas committed
307
		libname *l = libname::Find(MakeSymbol(c),lo);
thomas's avatar
thomas committed
308
	
thomas's avatar
thomas committed
309
#if FLEXT_SYS == FLEXT_SYS_PD
thomas's avatar
thomas committed
310
311
312
		if(ix > 0) 
			// in PD the first name is already registered with class creation
			::class_addcreator((t_newmethod)obj_new,(t_symbol *)l->name,A_GIMME,A_NULL);
thomas's avatar
thomas committed
313
#elif FLEXT_SYS == FLEXT_SYS_MAX
thomas's avatar
thomas committed
314
		if(ix > 0 || lib) 
thomas's avatar
thomas committed
315
			// in Max/MSP the first alias gets its name from the name of the object file,
thomas's avatar
thomas committed
316
317
			// unless it is a library (then the name can be different)
			::alias(const_cast<char *>(c));  
thomas's avatar
thomas committed
318
319
#elif FLEXT_SYS == FLEXT_SYS_JMAX
		if(ix > 0)  fts_class_alias(lo->clss,l->name);
thomas's avatar
thomas committed
320
321
#else
#error
thomas's avatar
thomas committed
322
323
324
325
#endif	
	}

	// call class setup function
thomas's avatar
thomas committed
326
    setupfun(clid);
thomas's avatar
thomas committed
327
328
329
330
331
}
	

typedef flext_obj *(*libfun)(int,t_atom *);

thomas's avatar
thomas committed
332
333
334
335
336
#if FLEXT_SYS == FLEXT_SYS_JMAX
void flext_obj::obj_new(fts_object_t *o, int, fts_symbol_t s, int _argc_, const fts_atom_t *argv)
{
	flext_hdr *obj = (flext_hdr *)o;
#else
thomas's avatar
thomas committed
337
338
339
#if FLEXT_SYS == FLEXT_SYS_MAX
flext_hdr *flext_obj::obj_new(const t_symbol *s,short _argc_,t_atom *argv)
#else
thomas's avatar
thomas committed
340
flext_hdr *flext_obj::obj_new(const t_symbol *s,int _argc_,t_atom *argv)
thomas's avatar
thomas committed
341
#endif
thomas's avatar
thomas committed
342
343
{
	flext_hdr *obj = NULL;
thomas's avatar
thomas committed
344
#endif
thomas's avatar
thomas committed
345
	libname *l = libname::Find(s);
thomas's avatar
thomas committed
346
347
348
	if(l) {
		bool ok = true;
		t_atom args[FLEXT_MAXNEWARGS]; 
thomas's avatar
thomas committed
349
		libclass *lo = l->obj;
thomas's avatar
thomas committed
350
351
352
353
354
355
356

		int argc = _argc_;
		if(lo->attr) {
			argc = flext_base::CheckAttrib(argc,argv);
		}

		if(lo->argc >= 0) {
thomas's avatar
thomas committed
357
#ifdef FLEXT_DEBUG
thomas's avatar
thomas committed
358
359
360
361
362
363
			if(lo->argc > FLEXT_MAXNEWARGS) { ERRINTERNAL(); ok = false; }
#endif

			if(argc == lo->argc) {
				for(int i = 0; /*ok &&*/ i < lo->argc; ++i) {
					switch(lo->argv[i]) {
thomas's avatar
thomas committed
364
365
#if FLEXT_SYS != FLEXT_SYS_PD
					case FLEXTTPN_INT:
thomas's avatar
thomas committed
366
367
368
369
370
						if(flext::IsInt(argv[i])) args[i] = argv[i];
						else if(flext::IsFloat(argv[i])) flext::SetInt(args[i],(int)flext::GetFloat(argv[i]));
						else ok = false;
						break;
#endif
thomas's avatar
thomas committed
371
					case FLEXTTPN_FLOAT:
thomas's avatar
thomas committed
372
373
374
375
						if(flext::IsInt(argv[i])) flext::SetFloat(args[i],(float)flext::GetInt(argv[i]));
						else if(flext::IsFloat(argv[i])) args[i] = argv[i];
						else ok = false;
						break;
thomas's avatar
thomas committed
376
					case FLEXTTPN_SYM:
thomas's avatar
thomas committed
377
378
379
380
381
382
383
						if(flext::IsSymbol(argv[i])) args[i] = argv[i];
						else ok = false;
						break;
					}
				}
			
				if(!ok)
thomas's avatar
thomas committed
384
					post("%s: Creation arguments do not match",GetString(s));
thomas's avatar
thomas committed
385
386
			}
			else {
thomas's avatar
thomas committed
387
				error("%s: %s creation arguments",GetString(s),argc < lo->argc?"Not enough":"Too many");
thomas's avatar
thomas committed
388
389
390
391
392
				ok = false;
			}
		}

		if(ok) {
thomas's avatar
thomas committed
393
394
			t_classid clid;

thomas's avatar
thomas committed
395
#if FLEXT_SYS == FLEXT_SYS_PD
thomas's avatar
thomas committed
396
			clid = lo->clss;
thomas's avatar
thomas committed
397
			obj = (flext_hdr *)::pd_new(lo->clss);
thomas's avatar
thomas committed
398
#elif FLEXT_SYS == FLEXT_SYS_MAX
thomas's avatar
thomas committed
399
			clid = lo;
thomas's avatar
thomas committed
400
			obj = (flext_hdr *)::newobject(lo->clss);
thomas's avatar
thomas committed
401
402
#elif FLEXT_SYS == FLEXT_SYS_JMAX
			clid = lo->clss;
thomas's avatar
thomas committed
403
404
#else
#error
thomas's avatar
thomas committed
405
#endif
thomas's avatar
thomas committed
406
407
//			post("NEWINST CLID %p",clid);

thomas's avatar
thomas committed
408
409
410
411
412
413
414
415
416
		    flext_obj::m_holder = obj;
			flext_obj::m_holdname = l->name;
			flext_obj::m_holdattr = lo->attr;

			// get actual flext object (newfun calls "new flext_obj()")
			if(lo->argc >= 0)
				// for interpreted arguments
				obj->data = lo->newfun(lo->argc,args); 
			else
thomas's avatar
thomas committed
417
				obj->data = lo->newfun(argc,(t_atom *)argv); 
thomas's avatar
thomas committed
418
419
420
421
422
	
			flext_obj::m_holder = NULL;
			flext_obj::m_holdname = NULL;
			flext_obj::m_holdattr = false;

thomas's avatar
thomas committed
423
			ok = obj->data &&
thomas's avatar
thomas committed
424
425
426
427
428
429
430
431
432
				// check constructor exit flag
				obj->data->InitOk();

			if(ok) {
				// store creation args for attribute initialization (inside flext_base::Init())
				flext_obj::m_holdaargc = _argc_-argc;
				flext_obj::m_holdaargv = argv+argc;

				// call virtual init function 
thomas's avatar
thomas committed
433
				// here, inlets, outlets, methods and attributes can be set up
thomas's avatar
thomas committed
434
435
				ok = obj->data->Init();

thomas's avatar
thomas committed
436
437
438
				// call another virtual init function 
				if(ok) ok = obj->data->Finalize();

thomas's avatar
thomas committed
439
440
441
442
443
444
445
446
447
448
449
				flext_obj::m_holdaargc = 0;
				flext_obj::m_holdaargv = NULL;
			}

			if(!ok) { 
				// there was some init error, free object
				lo->freefun(obj); 
				obj = NULL; 
			}
		}
	}
thomas's avatar
thomas committed
450
#ifdef FLEXT_DEBUG
thomas's avatar
thomas committed
451
	else
thomas's avatar
thomas committed
452
453
#if FLEXT_SYS == FLEXT_SYS_MAX
		// in Max/MSP an object with the name of the library exists, even if not explicitely declared!
thomas's avatar
thomas committed
454
455
456
457
458
		if(s != lib_name) 
#endif
		error("Class %s not found in library!",s->s_name);
#endif

thomas's avatar
thomas committed
459
#if FLEXT_SYS != FLEXT_SYS_JMAX
thomas's avatar
thomas committed
460
	return obj;
thomas's avatar
thomas committed
461
#endif
thomas's avatar
thomas committed
462
463
}

thomas's avatar
thomas committed
464
465
466
467
468
#if FLEXT_SYS == FLEXT_SYS_JMAX
void flext_obj::obj_free(fts_object_t *h, int winlet, fts_symbol_t s, int ac, const fts_atom_t *at)
#else
void flext_obj::obj_free(flext_hdr *h)
#endif
thomas's avatar
thomas committed
469
{
thomas's avatar
thomas committed
470
	flext_hdr *hdr = (flext_hdr *)h;
thomas's avatar
thomas committed
471
	const t_symbol *name = hdr->data->thisNameSym();
thomas's avatar
thomas committed
472
	libname *l = libname::Find(name);
thomas's avatar
thomas committed
473
474
475
476
477
478
479
480

	if(l) {
		// call virtual exit function
		hdr->data->Exit();

		// now call object destructor and deallocate
		l->obj->freefun(hdr);
	}
thomas's avatar
thomas committed
481
#ifdef FLEXT_DEBUG
thomas's avatar
thomas committed
482
	else 
thomas's avatar
thomas committed
483
#if FLEXT_SYS == FLEXT_SYS_MAX
thomas's avatar
thomas committed
484
		// in Max/MSP an object with the name of the library exists, even if not explicitely declared!
thomas's avatar
thomas committed
485
486
487
488
489
490
491
		if(name != lib_name) 
#endif
		error("Class %s not found in library!",name);
#endif
}