Dictionary Passing API

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...

+ Collaboration diagram for Dictionary Passing API:

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_dictionarydictobj_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_dictionarydictobj_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_symboldictobj_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.

Detailed Description

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.

Registration and Access

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.

Dictionary Syntax

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.

Utilities

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.

Registration and Access

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.

Version:
6.0

Function Documentation

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.

Parameters:
aAn atom to check, and potentially modify, to ensure safety in the dictionary-passing system.
Returns:
If the atom was changed then 1 is returned. Otherwise 0 is returned.
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.

Parameters:
dThe address of a dictionary variable, which will hold a pointer to the new dictionary upon return. Should be initialized to NULL.
argcThe number of atoms in argv.
argvPointer to the first of an array of atoms to be interpreted as Dictionary Syntax .
Returns:
A Max error code.
See also:
dictobj_dictionarytoatoms()
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.

Parameters:
dThe address of a dictionary variable, which will hold a pointer to the new dictionary upon return. Should be initialized to NULL.
strA NULL-terminated C-string containing Dictionary Syntax .
str_is_already_json
errorstring
Returns:
A Max error code.
See also:
dictobj_dictionarytoatoms()
t_max_err dictobj_dictionarytoatoms ( const t_dictionary d,
long *  argc,
t_atom **  argv 
)

Serialize the contents of a t_dictionary into Dictionary Syntax .

Parameters:
dThe dictionary to serialize.
argcThe address of a variable to hold the number of atoms allocated upon return.
argvThe address of a t_atom pointer which will point to the first atom (of an array of argc atoms) upon return.
Returns:
A Max error code.
See also:
dictobj_dictionaryfromatoms()
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.

Parameters:
nameThe name associated with the dictionary for which you wish to obtain a copy.
Returns:
The dictionary cloned from the existing dictionary. Returns NULL if no dictionary is associated with name.
See also:
dictobj_findregistered_retain()
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.

Parameters:
nameThe name associated with the dictionary for which you wish to obtain a pointer.
Returns:
A pointer to the dictionary associated with name. Returns NULL if no dictionary is associated with name.
See also:
dictobj_release()
dictobj_findregistered_clone()
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.

Parameters:
jsonsizeThe address of a variable to be filled-in with the number of chars in json upon return.
jsonThe 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.
strA NULL-terminated C-string containing Dictionary Syntax .
Returns:
A Max error code.
See also:
dictobj_dictionarytoatoms()
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.

Parameters:
xYour calling object. If there is an error this will be used by the internal call to object_error().
dThe dictionary you are querying.
akeyThe complex key specifying the query.
createIf true, create the intermediate dictionaries in the hierarchy specified in akey.
targetdictReturns the t_dictionary that for the (sub)dictionary specified by akey.
targetkeyReturns the name of the key in targetdict that to which akey is referencing.
indexReturns the index requested if array-member access is specified. Pass NULL if you are not interested in this.
Returns:
A Max error code.
t_symbol* dictobj_namefromptr ( t_dictionary d)

Find the name associated with a given t_dictionary.

Parameters:
dA dictionary, whose name you wish to determine.
Returns:
The symbol associated with the dictionary, or NULL if the dictionary is not registered.
See also:
dictobj_register()
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);
Parameters:
outThe outlet through which the atoms should be sent.
argcThe count of atoms in argv.
argvPointer 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.

Parameters:
dA valid dictionary object.
nameThe 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.
Returns:
The dictionary mapped to the specified name.
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).

Parameters:
dA valid dictionary object retained by dictobj_findregistered_retain().
Returns:
A Max error code.
See also:
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.

Parameters:
dA valid dictionary object.
Returns:
A Max error code.
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.

Parameters:
schemaThe dictionary against which to validate candidate.
candidateA dictionary to test against the schema.
Returns:
Returns true if the candidate validates against the schema, otherwise returns false.
See also:
dictobj_dictionarytoatoms()