The Dictionary Passing API defines a means by which t_dictionary instances may be passed between Max objects in a way similar to the way Jitter Matrices are passed between objects. More...
Functions | |
BEGIN_USING_C_LINKAGE t_dictionary * | dictobj_register (t_dictionary *d, t_symbol **name) |
Register a t_dictionary with the dictionary passing system and map it to a unique name. | |
t_max_err | dictobj_unregister (t_dictionary *d) |
Unregister a t_dictionary with the dictionary passing system. | |
t_dictionary * | dictobj_findregistered_clone (t_symbol *name) |
Find the t_dictionary for a given name, and return a copy of that dictionary When you are done, do not call dictobj_release() on the dictionary, because you are working on a copy rather than on a retained pointer. | |
t_dictionary * | dictobj_findregistered_retain (t_symbol *name) |
Find the t_dictionary for a given name, return a pointer to that t_dictionary, and increment its reference count. | |
t_max_err | dictobj_release (t_dictionary *d) |
For a t_dictionary/name that was previously retained with dictobj_findregistered_retain(), release it (decrement its reference count). | |
t_symbol * | dictobj_namefromptr (t_dictionary *d) |
Find the name associated with a given t_dictionary. | |
void | dictobj_outlet_atoms (void *out, long argc, t_atom *argv) |
Send atoms to an outlet in your Max object, handling complex datatypes that may be present in those atoms. | |
long | dictobj_atom_safety (t_atom *a) |
Ensure that an atom is safe for passing. | |
long | dictobj_validate (const t_dictionary *schema, const t_dictionary *candidate) |
Validate the contents of a t_dictionary against a second t_dictionary containing a schema. | |
t_max_err | dictobj_jsonfromstring (long *jsonsize, char **json, const char *str) |
Convert a C-string of Dictionary Syntax into a C-string of JSON. | |
t_max_err | dictobj_dictionaryfromstring (t_dictionary **d, const char *str, int str_is_already_json, char *errorstring) |
Create a new t_dictionary from Dictionary Syntax which is passed in as a C-string. | |
t_max_err | dictobj_dictionaryfromatoms (t_dictionary **d, const long argc, const t_atom *argv) |
Create a new t_dictionary from Dictionary Syntax which is passed in as an array of atoms. | |
t_max_err | dictobj_dictionarytoatoms (const t_dictionary *d, long *argc, t_atom **argv) |
Serialize the contents of a t_dictionary into Dictionary Syntax . | |
t_max_err | dictobj_key_parse (t_object *x, t_dictionary *d, t_atom *akey, t_bool create, t_dictionary **targetdict, t_symbol **targetkey, t_int32 *index) |
Given a complex key (one that includes potential heirarchy or array-member access), return the actual key and the dictionary in which the key should be referenced. |
The Dictionary Passing API defines a means by which t_dictionary instances may be passed between Max objects in a way similar to the way Jitter Matrices are passed between objects.
There are important differences, however, between Jitter matrix passing and dictionary passing. Many of these differences are documented in Max's documentation on dictionaries and structured data.
Every dictionary instance in this system is mapped to a unique name that identifies the dictionary. Dictionaries are passed between objects using the "dictionary" message with a single argument, which is the name of the dictionary.
The C-API for working with these dictionaries is composed of 5 primary registration/access methods:
It is useful to think of objects in the dictionary system as "nouns" and "verbs".
A "noun" is an object that possess or owns a dictionary. These objects are servers whose dictionary will accessed by other object that are clients. An example of a "noun" is the dict.pack object that creates a dictionary that is passed to other objects.
A "verb" is an object that does not maintain its own dictionary (it is not a thing) but merely does something to any dictionaries it receives. This object is a client rather than a server. An example of a "verb" is the dict.strip object, which removes entries from an existing dictionary but possesses no dictionary of its own.
Any object which is a dictionary "noun", can keep and rely on their dictionary pointer. Because of the way object_register() works, there should be no possiblity for this pointer to change behind the scenes. They each need to call object_free() on their respective object pointer, however. A call to object_free() also calls object_unregister() once, so there's technically not a need to unregister from the owner itself. They work like jit.matrix (and similar to buffer~), and use object_register() to increment a server reference count. If an object has already registered an object with the given name, the pointer passed in to register is freed and the existing one is returned from the registration function.
Dictionary "verbs" on the other hand should just call dict_findregistered_retain() and dict_release() when done. They are not incrementing the server reference count. They increment a reference count with regards to object freeing, which is compatible with and complementary to the server reference count.
Dictionaries may be represented in a variety of textual formats including JSON. Max also supports a compact YAML-like dictionary notation which is useful for proving data structure contents as lists of atoms in object boxes. This format is documented in Max's documentation of the dictionary features. The following functions are used for formatting and parsing the dictionary syntax.
There are several utility functions available to assist in coding objects that pass dictionaries.
The dictobj_validate() object is a utility routine for validating a dictionary against "schema" dictionary. This enables a behavior somewhat analogous to Objective-C or Smalltalk prototypes. Dictionary validation can be useful to implement a kind of dictionary polymorphism. For a multiple-inheritance behavior, simply validate a dictionary against multiple schemas to verify the presence of required keys and values.
The dict_outlet_atoms() function will not output A_OBJ atoms directly (nor should any other object) and as such it will also not output t_atomarray instances containing objects, thus atomarrays are not hierarchical in the dictionary passing implementation.
It will output an atom array if provided a single A_OBJ atom with class atomarray. If there is an array of atoms which contain A_OBJ atoms, they are converted to the *symbols* <dictionary-object>, <atomarray-object>, <string-object>, <other-object> respectively. Ideally such a case should never be reached if everything which inserts values into a dictionary is well behaved--i.e.
long dictobj_atom_safety | ( | t_atom * | a | ) |
Ensure that an atom is safe for passing.
Atoms are allowed to be A_LONG, A_FLOAT, or A_SYM, but not A_OBJ. If the atom is an A_OBJ, it will be converted into something that will be safe to pass.
a | An atom to check, and potentially modify, to ensure safety in the dictionary-passing system. |
t_max_err dictobj_dictionaryfromatoms | ( | t_dictionary ** | d, |
const long | argc, | ||
const t_atom * | argv | ||
) |
Create a new t_dictionary from Dictionary Syntax which is passed in as an array of atoms.
d | The address of a dictionary variable, which will hold a pointer to the new dictionary upon return. Should be initialized to NULL. |
argc | The number of atoms in argv. |
argv | Pointer to the first of an array of atoms to be interpreted as Dictionary Syntax . |
t_max_err dictobj_dictionaryfromstring | ( | t_dictionary ** | d, |
const char * | str, | ||
int | str_is_already_json, | ||
char * | errorstring | ||
) |
Create a new t_dictionary from Dictionary Syntax which is passed in as a C-string.
d | The address of a dictionary variable, which will hold a pointer to the new dictionary upon return. Should be initialized to NULL. |
str | A NULL-terminated C-string containing Dictionary Syntax . |
str_is_already_json | |
errorstring |
t_max_err dictobj_dictionarytoatoms | ( | const t_dictionary * | d, |
long * | argc, | ||
t_atom ** | argv | ||
) |
Serialize the contents of a t_dictionary into Dictionary Syntax .
d | The dictionary to serialize. |
argc | The address of a variable to hold the number of atoms allocated upon return. |
argv | The address of a t_atom pointer which will point to the first atom (of an array of argc atoms) upon return. |
t_dictionary* dictobj_findregistered_clone | ( | t_symbol * | name | ) |
Find the t_dictionary for a given name, and return a copy of that dictionary When you are done, do not call dictobj_release() on the dictionary, because you are working on a copy rather than on a retained pointer.
name | The name associated with the dictionary for which you wish to obtain a copy. |
t_dictionary* dictobj_findregistered_retain | ( | t_symbol * | name | ) |
Find the t_dictionary for a given name, return a pointer to that t_dictionary, and increment its reference count.
When you are done you should call dictobj_release() on the dictionary.
name | The name associated with the dictionary for which you wish to obtain a pointer. |
t_max_err dictobj_jsonfromstring | ( | long * | jsonsize, |
char ** | json, | ||
const char * | str | ||
) |
Convert a C-string of Dictionary Syntax into a C-string of JSON.
jsonsize | The address of a variable to be filled-in with the number of chars in json upon return. |
json | The address of a char pointer to point to the JSON C-string upon return. Should be initialized to NULL. You are responsible for freeing the string with sysmem_freeptr() when you are done with it. |
str | A NULL-terminated C-string containing Dictionary Syntax . |
t_max_err dictobj_key_parse | ( | t_object * | x, |
t_dictionary * | d, | ||
t_atom * | akey, | ||
t_bool | create, | ||
t_dictionary ** | targetdict, | ||
t_symbol ** | targetkey, | ||
t_int32 * | index | ||
) |
Given a complex key (one that includes potential heirarchy or array-member access), return the actual key and the dictionary in which the key should be referenced.
x | Your calling object. If there is an error this will be used by the internal call to object_error(). |
d | The dictionary you are querying. |
akey | The complex key specifying the query. |
create | If true, create the intermediate dictionaries in the hierarchy specified in akey. |
targetdict | Returns the t_dictionary that for the (sub)dictionary specified by akey. |
targetkey | Returns the name of the key in targetdict that to which akey is referencing. |
index | Returns the index requested if array-member access is specified. Pass NULL if you are not interested in this. |
t_symbol* dictobj_namefromptr | ( | t_dictionary * | d | ) |
Find the name associated with a given t_dictionary.
d | A dictionary, whose name you wish to determine. |
void dictobj_outlet_atoms | ( | void * | out, |
long | argc, | ||
t_atom * | argv | ||
) |
Send atoms to an outlet in your Max object, handling complex datatypes that may be present in those atoms.
This is particularly when sending the contents of a dictionary entry out of an outlet as in the following example code.
long ac = 0; t_atom *av = NULL; t_max_err err; err = dictionary_copyatoms(d, key, &ac, &av); if (!err && ac && av) { // handles singles, lists, symbols, atomarrays, dictionaries, etc. dictobj_outlet_atoms(x->outlets[i],ac,av); } if (av) sysmem_freeptr(av);
out | The outlet through which the atoms should be sent. |
argc | The count of atoms in argv. |
argv | Pointer to the first of an array of atoms to send to the outlet. |
BEGIN_USING_C_LINKAGE t_dictionary* dictobj_register | ( | t_dictionary * | d, |
t_symbol ** | name | ||
) |
Register a t_dictionary with the dictionary passing system and map it to a unique name.
d | A valid dictionary object. |
name | The address of a t_symbol pointer to the name you would like mapped to this dictionary. If the t_symbol pointer has a NULL value then a unique name will be generated and filled-in upon return. |
t_max_err dictobj_release | ( | t_dictionary * | d | ) |
For a t_dictionary/name that was previously retained with dictobj_findregistered_retain(), release it (decrement its reference count).
d | A valid dictionary object retained by dictobj_findregistered_retain(). |
t_max_err dictobj_unregister | ( | t_dictionary * | d | ) |
Unregister a t_dictionary with the dictionary passing system.
Generally speaking you should not need to call this method. Calling object_free() on the t_dictionary automatically unregisters it.
d | A valid dictionary object. |
long dictobj_validate | ( | const t_dictionary * | schema, |
const t_dictionary * | candidate | ||
) |
Validate the contents of a t_dictionary against a second t_dictionary containing a schema.
The schema dictionary contains keys and values, like any dictionary. dictobj_validate() checks to make sure that all keys in the schema dictionary are present in the candidate dictionary. If the keys are all present then the candidate passes and the function returns true. Otherwise the the candidate fails the validation and the function returns false.
Generally speaking, the schema dictionary with contain values with the symbol "*", indicating a wildcard, and thus only the key is used to validate the dictionary (all values match the wildcard). However, if the schema dictionary contains non-wildcard values for any of its keys, those keys in the candidate dictionary must also contain matching values in order for the candidate to successfully validate.
An example of this in action is the dict.route object in Max, which simply wraps this function.
schema | The dictionary against which to validate candidate. |
candidate | A dictionary to test against the schema. |