Max 5 API Reference
00001 /** 00002 @page chapter_atoms Atoms and Messages 00003 00004 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. 00005 00006 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. 00007 00008 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. 00009 00010 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. 00011 00012 First, we'll need to create a generic outlet with outlet_new in our new instance routine. 00013 @code 00014 x->m_outlet = outlet_new((t_object *)x, NULL); 00015 @endcode 00016 00017 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"). 00018 00019 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. 00020 00021 @code 00022 void myobject_bang(t_object *x) 00023 { 00024 t_atom argv[3]; 00025 00026 atom_setlong(argv, 43); 00027 atom_setsym(argv + 1, gensym("crazy")); 00028 atom_setfloat(argv + 2, 8.34); 00029 00030 outlet_anything(x->m_outlet, gensym("green"), 3, argv); 00031 } 00032 @endcode 00033 00034 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 inlet. If outlet_anything() finds such a message, it will execute it, passing it the array of atoms it received. 00035 00036 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. 00037 00038 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. 00039 00040 00041 @section chapter_atoms_types Argument Type Specifiers 00042 00043 In the simp example, you saw the int method defined as follows: 00044 @code 00045 class_addmethod(c, (method)simp_int, A_LONG, 0); 00046 @endcode 00047 00048 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. 00049 00050 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. 00051 00052 If the atoms cannot be coerced into the format of the argument type specifier, a bad arguments error is printed in the Max window. 00053 00054 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. 00055 00056 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. 00057 00058 00059 @section chapter_atoms_gimme_funcs Writing A_GIMME Functions 00060 00061 A method that uses #A_GIMME is declared as follows: 00062 @code 00063 void myobject_message(t_myobject *x, t_symbol *s, long argc, t_atom *argv); 00064 @endcode 00065 00066 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. 00067 00068 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. 00069 00070 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. 00071 00072 @code 00073 void myobject_printargs(t_myobject *x, t_symbol *s, long argc, t_atom *argv) 00074 { 00075 long i; 00076 t_atom *ap; 00077 00078 post("message selector is %s",s->s_name); 00079 post("there are %ld arguments",argc); 00080 for (i = 0, ap = argv; i < argc; i++, ap++) { // increment ap each time to get to the next atom 00081 switch (atom_gettype(ap)) { 00082 case A_LONG: 00083 post("%ld: %ld",i+1,atom_getlong(ap)); 00084 break; 00085 case A_FLOAT: 00086 post("%ld: %.2f",i+1,atom_getfloat(ap)); 00087 break; 00088 case A_SYM: 00089 post("%ld: %s",i+1, atom_getsym(ap)->s_name); 00090 break; 00091 default: 00092 post("%ld: unknown atom type (%ld)", i+1, atom_gettype(ap)); 00093 break; 00094 } 00095 } 00096 } 00097 @endcode 00098 00099 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. 00100 00101 */
Copyright © 2008, Cycling '74