Max API  8.2.0
Atoms and Messages

When a Max object receives a message, it uses its class to look up the message selector ("int", "bang", "set" etc.) and invoke the associated C function (method).

This association is what you are creating when you use class_addmethod() in the initialization routine. If the lookup fails, you'll see an "object doesn't understand message" error in the Max window.

Message selectors are not character strings, but a special data structure called a symbol (t_symbol). A symbol holds a string and a value, but what is more important is that every symbol in Max is unique. This permits you to compare two symbols for equivalence by comparing pointers, rather than having to compare each character in two strings.

The "data" or argument part of a message, if it exists, is transmitted in the form of an array of atoms (t_atom). The atom is a structure that can hold integers, floats, symbols, or even pointers to other objects, identified by a tag. You'll use symbols and atoms both in sending messages and receiving them.

To illustrate the use of symbols and atoms, here is how you would send a message out an outlet. Let's say we want to send the message "green 43 crazy 8.34." This message consists of a selector "green" plus an array of three atoms.

First, we'll need to create a generic outlet with outlet_new in our new instance routine.

x->m_outlet = outlet_new((t_object *)x, NULL);
void * outlet_new(void *x, C74_CONST char *s)
Use outlet_new() to create an outlet that can send a specific non-standard message,...
The structure for the head of any object which wants to have inlets or outlets, or support attributes...
Definition: ext_mess.h:191

The second argument being NULL indicates that the outlet can be used to send any message. If the second argument had been a character string such as "int" or "set" only that specific message could be sent out the outlet. You'd be correct if you wondered whether intout() is actually just outlet_new(x, "int").

Now that we have our generic outlet, we'll call outlet_anything() on it in a method. The first step, however, is to assemble our message, with a selector "green" plus an array of atoms. Assigning ints and floats to an atom is relatively simple, but to assign a symbol, we need to transform a character string into a symbol using gensym(). The gensym() function returns a pointer to a symbol that is guaranteed to be unique for the string you supply. This means the string is compared with other symbols to ensure its uniqueness. If it already exists, gensym() will supply a pointer to the symbol. Otherwise it will create a new one and store it in a table so it can be found the next time someone asks for it.

void myobject_bang(t_object *x)
{
t_atom argv[3];
atom_setlong(argv, 43);
atom_setsym(argv + 1, gensym("crazy"));
atom_setfloat(argv + 2, 8.34);
outlet_anything(x->m_outlet, gensym("green"), 3, argv);
}
t_max_err atom_setsym(t_atom *a, t_symbol *b)
Inserts a t_symbol * into a t_atom and change the t_atom's type to A_SYM.
t_max_err atom_setlong(t_atom *a, t_atom_long b)
Inserts an integer into a t_atom and change the t_atom's type to A_LONG.
t_max_err atom_setfloat(t_atom *a, double b)
Inserts a floating point number into a t_atom and change the t_atom's type to A_FLOAT.
void * outlet_anything(t_outlet *x, t_symbol *s, short ac, t_atom *av)
Use outlet_anything() to send any message out an outlet.
t_symbol * gensym(C74_CONST char *s)
Given a C-string, fetch the matching t_symbol pointer from the symbol table, generating the symbol if...
An atom is a typed datum.
Definition: ext_mess.h:323

In the call to outlet_anything() above, gensym("green") represents the message selector. The outlet_anything() function will try to find a message "green" in each of the objects connected to the outlet. If outlet_anything() finds such a message, it will execute it, passing it the array of atoms it received.

If it cannot find a match for the symbol green, it does one more thing, which allows objects to handle messages generically. Your object can define a special method bound to the symbol "anything" that will be invoked if no other match is found for a selector. We'll discuss the anything method in a moment, but first, we need to return to class_addmethod() and explain the final arguments it accepts.

To access atoms, you can use the functions atom_setlong(), atom_getlong() etc. or you can access the t_atom structure directly. We recommend using the accessor functions, as they lead to both cleaner code and will permit your source to work without modifications when changes to the t_atom structure occur over time.

Argument Type Specifiers

In the simp example, you saw the int method defined as follows:

class_addmethod(c, (method)simp_int, "int", A_LONG, 0);
@ A_LONG
long integer
Definition: ext_mess.h:279
t_max_err class_addmethod(t_class *c, C74_CONST method m, C74_CONST char *name,...)
Adds a method to a previously defined object class.
BEGIN_USING_C_LINKAGE typedef void *(* method)(void *)
Function pointer type for generic methods.
Definition: ext_mess.h:25

The A_LONG, 0 arguments to class_addmethod() specify the type of arguments expected by the C function you have written. A_LONG means that the C function accepts a long integer argument. The 0 terminates the argument specifier list, so for the int message, there is a single long integer argument.

The other options are A_FLOAT for doubles, A_SYM for symbols, and A_GIMME, which passes the raw list of atoms that were originally used to send the Max message in the first place. These argument type specifiers define what are known as "typed" methods in Max. Typed methods are those where Max checks the type of each atom in a message to ensure it is consistent with what the receiving object has said it expects for a given selector.

If the atoms cannot be coerced into the format of the argument type specifier, a bad arguments error is printed in the Max window.

There is a limit to the number of specifiers you can use, and in general, multiple A_FLOAT specifiers should be avoided due to the historically unpredictable nature of compiler implementations when passing floating-point values on the stack. Use A_GIMME for more than four arguments or with multiple floating-point arguments.

You can also specify that missing arguments to a message be filled in with default values before your C function receives them. A_DEFLONG will put a 0 in place of a missing long argument, A_DEFFLOAT will put 0.0 in place of a missing float argument, and A_DEFSYM will put the empty symbol (equal to gensym("")) in place of a missing symbol argument.

Writing A_GIMME Functions

A method that uses A_GIMME is declared as follows:

void myobject_message(t_myobject *x, t_symbol *s, long argc, t_atom *argv);
The symbol.
Definition: ext_mess.h:102

The symbol argument s is the message selector. Ordinarily this might seem redundant, but it is useful for the "anything" method as we'll discuss below.

argc is the number of atoms in the argv array. It could be 0 if the message was sent without arguments. argv is the array of atoms holding the arguments.

For typed messages, the atoms will be of type A_SYM, A_FLOAT, or A_LONG. Here is an example of a method that merely prints all of the arguments.

void myobject_printargs(t_myobject *x, t_symbol *s, long argc, t_atom *argv)
{
long i;
t_atom *ap;
post("message selector is %s",s->s_name);
post("there are %ld arguments",argc);
// increment ap each time to get to the next atom
for (i = 0, ap = argv; i < argc; i++, ap++) {
switch (atom_gettype(ap)) {
case A_LONG:
post("%ld: %ld",i+1,atom_getlong(ap));
break;
case A_FLOAT:
post("%ld: %.2f",i+1,atom_getfloat(ap));
break;
case A_SYM:
post("%ld: %s",i+1, atom_getsym(ap)->s_name);
break;
default:
post("%ld: unknown atom type (%ld)", i+1, atom_gettype(ap));
break;
}
}
}
t_atom_float atom_getfloat(const t_atom *a)
Retrieves a floating point value from a t_atom.
t_symbol * atom_getsym(const t_atom *a)
Retrieves a t_symbol * value from a t_atom.
t_atom_long atom_getlong(const t_atom *a)
Retrieves a long integer value from a t_atom.
long atom_gettype(const t_atom *a)
Retrieves type from a t_atom.
@ A_FLOAT
32-bit float
Definition: ext_mess.h:280
@ A_SYM
t_symbol pointer
Definition: ext_mess.h:281
BEGIN_USING_C_LINKAGE void post(C74_CONST char *fmt,...)
Print text to the Max window.
char * s_name
name: a c-string
Definition: ext_mess.h:103

You can interpret the arguments in whatever manner you wish. You cannot, however, modify the arguments as they may be about to be passed to another object.

Writing "Anything" Methods

As previously mentioned, your object can define a special method bound to the symbol "anything" that will be invoked if no other match is found for a selector. For example:

class_addmethod(c, (method)myobject_anything, "anything", A_GIMME, 0);
@ A_GIMME
request that args be passed as an array, the routine will check the types itself.
Definition: ext_mess.h:286

Your function definition for an anything method follows the same pattern as for all other A_GIMME methods:

void myobject_anything(t_myobject *x, t_symbol *s, long argc, t_atom *argv)
{
"This method was invoked by sending the '%s' message to this object.",
s->s_name);
// argc and argv are the arguments, as described in above.
}
void object_post(t_object *x, C74_CONST char *s,...)
Print text to the Max window, linked to an instance of your object.