The main concepts of FTS message system are:
typedef struct { fts_object_t o; /* MUST BE FIRST STRUCTURE MEMBER */ long n; /* the state of a integer object */ } integer_t;Attention !!! A FTS object must always have a first structure member of type
fts_object_t
. This implements in C the fact that all FTS objects
inherits from the fts_object_t
type.
The only "system" member of the structure which is made mandatory by the FTS object
system is the first member of type fts_object_t
. The other members of the structure
are "user" members and are not handled by the system.
An number of functions or macros are provided to get some of the object characteristic:
int fts_object_get_outlets_number(fts_object_t *obj);Return the number of outlets the object obj have.
fts_symbol_t *fts_object_get_outlet_typethe (fts_object_t *obj, int outlet);Return the type of the outlet outlet for obj (see Outlet Typing).
int fts_object_get_inlets_number(fts_object_t *obj);Return the number of inlets the object obj have.
const char *fts_object_get_class_name(fts_object_t *obj);Return as a C string the name of the object class; to be used only for user messages.
Future releases will include more functions and macros of this family.
void method( fts_object_t *object, int winlet, const fts_symbol_t *selector, int ac, const fts_atom_t *at)
The arguments of the method have the following meaning :
fts_symbol_t
data structure is discussed in details in FTS Kernel Reference Manual.
fts_atom_t
. The fts_atom_t
data structure is discussed in details in FTS Kernel Reference Manual. Attention !!! The arguments are passed by the message system as an array of constant atoms. This means that a method cannot modify its arguments. Modifying the arguments of a method in the body of the method can lead to unpredictable side effects. If this is needed for convenience, the arguments must first be copied.
Accessing Arguments
A method access its arguments via access macros, which handle both
accessing the atoms and giving a default value if corresponding
argument is optional.
Attention !!! It is strongly recommended to use the arguments access
macros. Accessing directly the members of the fts_atom_t
structure
is garanteed not to work across different releases of FTS, because fts_atom_t
implementation may change.
This is an example of a simple method, taking one argument of type long
,
with a default value of 0, which stores the value of this argument in the state
of the object :
void integer_in1( fts_object_t *object, int winlet, const fts_symbol_t *selector, int ac, const fts_atom_t *at) { integer_t *this = (integer_t *)object; this->n = fts_get_long_arg( at, ac, 0, 0); }In this method, the object argument, typed as an
fts_object_t *
is in fact a pointer to an object of type integer_t
(defined earlier),
which first
structure member is of type fts_object_t
. This explains the cast
made at beginning of method.
List of Access Macros
Below is the list of available access macros :
fts_get_long_arg( AT, AC, N, DEFAULT)
long
fts_get_float_arg( AT, AC, N, DEFAULT)
float
fts_get_symbol_arg( AT, AC, N, DEFAULT)
fts_symbol_t *
fts_get_string_arg( AT, AC, N, DEFAULT)
const char *
fts_get_obj_arg( AT, AC, N, DEFAULT)
void *
One special macro is available:
fts_get_float_long_arg( AT, AC, N, DEFAULT)
long
or float
.
long
or float
value,
depending on the type of the passed atom; this macro is usefull to write code
that work with generic numeric values; callers of this macros should always cast
the result to the desired numeric type.
fts_outlet_send
function.
fts_outlet_send
Functionfts_status_t fts_outlet_send( fts_object_t *object, int woutlet, fts_symbol_t *selector, int argc, const fts_atom_t *args)
The arguments of fts_outlet_send
have the following meaning :
void integer_bang( fts_object_t *object, int winlet, const fts_symbol_t *selector, int ac, const fts_atom_t *at) { integer_t *this = (integer_t *)object; fts_atom_t a; fts_set_long( &a, this->n); fts_outlet_send( object, 0, fts_s_int, 1, &a); }
fts_atom_t
data structure access macros, documented in
FTS Kernel Reference Manual
fts_outlet_send
function, passing it the arguments
array
fts_atom_t
structure
is garanteed not to work across different releases of FTS, because fts_atom_t
implementation may change.
fts_symbol_t
. The fts_symbol_t
data structure is discussed in details in FTS Kernel Reference Manual. There is a number of predefined selectors which are defined in the header files for the message system. Below is the list of already defined selectors for release 1.3.x together with associated symbol name :
fts_s_int
fts_s_float
fts_s_bang
fts_s_list
fts_s_symbol
fts_s_set
fts_s_sig
fts_s_put
fts_new_symbol
function generates a new symbol.
fts_method_define
function,
which is usually called when initializing the class. This function defines
to which message the method will be associated, and which kind of
arguments it expects.
fts_method_define
Functionfts_status_t fts_method_define( fts_class_t *class, int winlet, fts_symbol_t *selector, fts_method_t method, int argc, fts_atom_type_t *argtypes )
The arguments of fts_method_define
have the following meaning :
integer_in1
already defined for the integer
object. This method is defined for
inlet number 1 and takes one argument of type FTS_LONG
.
fts_status_t integer_instantiate( fts_class_t *class, int ac, fts_atom_t *at) { fts_atom_type_t a; a = fts_Long; fts_method_define( class, 1, fts_s_int, integer_in1, 1, &a); }
This type definition is done with
the argtypes argument of function fts_method_define
.
This argument is an array of elements of type fts_atom_type_t
.
Each element defines the expected type of the argument having
same index in the array of atoms which is the arguments of the
received message.
The elements of the array argtypes can take the following values (or an bit-or of some of them) :
fts_Long
FTS_LONG
, initialized with fts_set_long
fts_Float
FTS_FLOAT
, initialized with fts_set_float
fts_Symbol
FTS_SYM
, initialized with fts_set_symbol
fts_String
FTS_STRING
, initialized with fts_set_string
fts_Object
FTS_OBJ
, initialized with fts_set_obj
fts_Any
fts_OptArg
fts_VarArgs
All the system methods are associated to messages received on the system inlet, which is a symbolic constant defined by the message system. Except this, system methods are strictly identical to standard methods.
init
Method
init
Method arguments
delete
Method
connect
Method
disconnect
Method
init
Method
The init
method arguments, are the object creation arguments, including
the class name; i.e. using FTS with the standard Max editor, the arguments of the
init arguments are the content of the object box.
The declaration of the types of these so-called creation arguments must take
into account this feature.
Below is the example of the init method for the integer
object
already defined. This method just initialize the state of the object
with the value of its argument.
void integer_init( fts_object_t *object, int winlet, const fts_symbol_t *selector, int ac, const fts_atom_t *at) { integer_t *this = (integer_t *)object; post( "initializing an object of class %s", fts_symbol_name(fts_get_symbol_arg( at, ac, 0, 0))); this->n = fts_get_long_arg( at, ac, 1, 0); }This method is installed by the following code :
fts_status_t integer_instantiate( fts_class_t *class, int ac, fts_atom_t *at) { fts_atom_type_t a[2]; a[0] = fts_Symbol; a[1] = fts_Long; fts_method_define( class, fts_SystemInlet, fts_s_init, integer_init, 2, a); }
delete
Method
A delete method can be installed for the integer
object by the following code :
fts_status_t integer_instantiate( fts_class_t *class, int ac, fts_atom_t *at) { fts_method_define( class, fts_SystemInlet, fts_s_delete, integer_delete, 0, 0); }
connect
Methodconnect
method is not documented in this release.
The
The disconnect
Methoddisconnect
method is not documented in this release.
System Messages Selectors
There is a number of predefined system messages selectors which are defined in the
header
files for the message system. Below is the list of already defined selectors
for release 1.3.x together with associated symbol name :
fts_s_init
fts_s_delete
fts_s_connect_inlet
fts_s_connect_outlet
fts_s_disconnect_inlet
fts_s_disconnect_outlet
fts_outlet_type_define
function.
fts_outlet_type_define
Functionfts_status_t fts_outlet_type_define( fts_class_t *class, int woutlet, fts_symbol_t *selector,int argc, fts_atom_type_t *argtypes)
The arguments of fts_outlet_type_define
have the following meaning :
fts_method_define
. The type of an outlet will be used by the system to do a type checking, at connection-time and at run-time (when needed).
Outlet typing do not affect the efficency of method calling; the method dispatching
use a entry dynamic cache that optimize methods calls for all objects,
not only statically type ones.
The type of an outlet is unique :
if an outlet is supposed to send different kinds of messages, then
it must not be typed; this means that the symbol fts_s_anything
is not accepted as argument to this function.
Outlet Typing Example
The following code defines the type of the outlet for the integer
object. The outlet number 1 is defined to send only one type of message,
with selector int
and one argument of type FTS_LONG
.
fts_status_t integer_instantiate( fts_class_t *class, int ac, fts_atom_t *at) { fts_atom_type_t a; a = fts_Long; fts_outlet_type_define(class, 0, fts_s_int, 1, &a); }
The function that defines a class is called the instantiation function.
fts_status_t my_class_instantiate( fts_class_t *class, int ac, fts_atom_t *at)
The arguments of the class instantiation function have the following meaning :
fts_class_init
function.
fts_method_define
previously described
fts_outlet_type_define
previously
described
fts_class_init
Functionfts_status_t fts_class_init( fts_class_t *class, unsigned int size, int ninlets, int noutlets, void *user_data)
The arguments of fts_class_init
have the following meaning :
sizeof
of the object
structure
integer
object
already defined. This instantiation function first initialize the class, then
defines all the methods of the class, then defines the type of the outlet.
The objects of this class have 2 inlets and 1 outlet :
fts_status_t integer_instantiate( fts_class_t *class, int ac, fts_atom_t *at) { fts_atom_type_t a; fts_class_init( class, sizeof( integer_t), 2, 1, 0); a = fts_Long; /* init method */ fts_method_define( class, fts_SystemInlet, fts_s_init, integer_init, 1, &a); /* message "int" on inlet 0 method */ fts_method_define( class, 0, fts_s_int, integer_int, 1, &a); /* message "int" on inlet 1 method */ fts_method_define( class, 1, fts_s_int, integer_in1, 1, &a); /* message "bang" on inlet 0 method */ fts_method_define( class, 0, fts_s_bang, integer_bang, 0, 0); /* outlet type is int */ fts_outlet_type_define(class, 0, fts_s_int, 1, &a); return fts_Success; }
A class is instanciated on demand, when an object is created, through the following steps :
for all classes of the base { if ( equivalence_function( creation_arguments( current_class), creation_arguments( object_to_be_created)) { /* object to be created is of the current class */ return current_class; } }
$init
method is called for this object
int a_equivalence_function( int ac0, const fts_atom_t *at0, int ac1, const fts_atom_t *at1)
The arguments of a equivalence_function
have the following meaning :
bangbang
class : the bangbang
object has 1 inlet,
receiving
a bang, and a certain number of outlets. The number of outlets is given by the creation
argument, which is of type FTS_LONG. When receiving a bang
message on its inlet,
the bangbang
object outputs a bang
message on all its outlets, starting
from the last.
The bangbang
class is a metaclass, with a simple equivalence function : it
simply compares the identity of the arguments, which must be of type FTS_LONG.
static int bangbang_equiv( int ac0, const fts_atom_t *at0, int ac1, const fts_atom_t *at1) { if (ac0 == 1 && ac1 == 1 && fts_is_long(at0) && fts_is_long(at1) && fts_get_long(at0) == fts_get_long(at1)) return 1; else return 0; }
typedef struct { fts_object_t o; int noutlets; } bangbang_t; static void bangbang_bang( fts_object_t *object, int winlet, fts_symbol_t *selector, int ac, const fts_atom_t *at) { bangbang_t *this; int i; this = (bangbang_t *)object; for (i = this->noutlets-1; i >= 0; i--) fts_outlet_send(object, i, fts_s_bang, 0, 0); } static void bangbang_init( fts_object_t *object, int winlet, fts_symbol_t *selector, int ac, const fts_atom_t *at) { bangbang_t *this; this->noutlets = fts_get_long_arg( at, ac, 0, 2); } static fts_status_t bangbang_instantiate(fts_class_t *class, int ac, const fts_atom_t *at) { int i, noutlets; fts_atom_type_t a; if ((ac >= 1) && fts_is_long( at)) noutlets = fts_get_long(at); else noutlets = 1; fts_class_init(class, sizeof(bangbang_t), 1, noutlets, 0); fts_method_define(class, 0, fts_s_bang, bangbang_bang, 0, 0); a = fts_Long; fts_method_define( class, fts_SystemInlet, fts_s_init, bangbang_init, 1, &a); for (i = 0; i < noutlets; i++) fts_outlet_type_define( class, i, fts_s_bang, 0, 0); return fts_Success; }
fts_metaclass_t * fts_metaclass_create(fts_symbol_t *name, fts_method_instantiate_t instantiate_function, fts_method_equiv_t equiv_function);The metaclass is named name; instantiate_function will be used to instantiate new classes, and equiv_function will be its equivalence function. Different metaclasses can have the same name; see Generic Metaclasses. For simplicity, it can be convenient to have multiple names (usually abbreviation) for the same kind of object; the standard way to do this in FTS is to declare an alias to a name, with the following function:
void fts_metaclass_alias(fts_symbol_t *new_name, fts_symbol_t *old_name)After the call to
fts_metaclass_alias
, the name
new_name, when used as a metaclass name, will be always
automatically substituted by old_name. Since more metaclasses
can have the same name, this is not equivalent to say that a
meta-class can have multiple names; i.e. all the metaclass with the
same name will share the same set of aliases.
void integer_config(void) { /* create the class 'integer' */ fts_metaclass_create(fts_new_symbol("int"),integer_instantiate, fts_always_equiv); /* ... and register 2 aliases for the "int" name: "i" and "integer" */ fts_metaclass_alias(fts_new_symbol("integer"), fts_new_symbol("int")); fts_metaclass_alias(fts_new_symbol("i"), fts_new_symbol("int")); }
integer
object
The FTS message system provide a way to view different metaclasses as the implementation of a single metaclass, the generic metaclass.
The term "generic" derive from the generic programming technique; if we see FTS objects as the operator and statements of a program represented by the FTS patches, then a generic operator (in the object oriented programming sense) can be implemented with a generic metaclass; for example, the "+" operator on control message means correspond to two actual operations depending on the argument being an int or a float; so "+ 1" and "+ 1.0" correspond to two different operations, but to the same, generic, operator.
Generic operators can be (and are, for object like "+") implemented using generic metclasses.
The principle is very simple: if more than one class with the same
name exists, the class instatiation algoritm explained in Metaclasses and Class
Instantiation is followed with the first class found; in case
of failure of the class instation function, i.e. in the case the
class instantiation function do not return fts_Success
,
the next metaclass with the same name is tryed, and so on.
If you design a metaclass so to be extensible or modularly integrable in a generic metaclass, you should be carefull in testing the argument in the instation function and to return an error in case the metaclass cannot implement the requested class; FTS do not provide a declarative method to discriminate between different metaclasses in order to leave the most generic flexibility in generic metaclasses design; for example, a metaclass can overide the behaviour of an existing metaclass for a specific value of its argument, for example to provide an optimized implementation of a specific special case.
The fts_metaclass_create
function always add the new
metaclass at the end of the class list; in order to add a new
behaviour on top of an existing class, the
fts_metaclass_create_override
function should be used;
this function behave exactly like fts_metaclass_create
,
but add the new metaclass at the beginning of the list, so that it is
allowed to override the behaviour of existing classes.
Also, the property system is extended with standard AI like constraint progagation and daemon techniques: putting a property or getting a property from an object can transparently activate a number of functions that propagate the properties in the object network.
This subsystem is currently used in FTS to implement some experimental optimization and features in the DSP compiler.
Its API is not currently documented.
Authors : Maurizio De Cecco, François Déchelle
Copyright © 1995 IRCAM.