SYNOPSIS

#include <agar/core.h>

DESCRIPTION

The Agar object system provides object-oriented programming capabilities
including inheritance and virtual functions, as well as high-level features
such as serialization, timers, VFS and abstracted data types.
It is implemented in C and provides bindings to other languages.

Agar objects can be organized into a tree or virtual filesystem (VFS).
Any
AG_Object can become the root of a VFS.
A VFS can be made persistent to the degree required by the application.
Object data is serialized to a machine-independent binary format (using
AG_DataSource(3) calls in their
load() and
save() operations).
While an object's metadata (including the
name field) must always remain in memory, class-specific data can be serialized
to storage and deserialized on demand.

The
AG_ObjectNew() function allocates and initializes a new object instance of the given class.
The object is attached to
parent, unless the argument is NULL.
If
name is NULL then a unique name (e.g., "object0") will be generated.
If both
parent and
name are specified and the parent object already has a child of the given name,
AG_ObjectNew() fails and returns NULL.

The
AG_ObjectInit() function initializes an object of a given class.
It invokes
init() for every class in the inheritance hierarchy.
name is an optional name for the object instance relative to its parent (maximum
AG_OBJECT_NAME_MAX characters and no
/). classInfo should point to an initialized
AG_ObjectClass structure (see
CLASSES ) . The
flags argument specifies a default set of flags (see
FLAGS ) .

AG_ObjectAttach() attaches an object
child to another object
newParent, raising an
attached event.
It is a no-op if
newParent is NULL.

AG_ObjectMoveUp(), AG_ObjectMoveDown(), AG_ObjectMoveToHead() and
AG_ObjectMoveToTail() move the object in the parent object's list of child objects.
This is useful when the ordering of objects is important.

AG_ObjectDelete() is a shorthand for
AG_ObjectDetach() followed by
AG_ObjectDestroy().

AG_ObjectRoot() returns a pointer to the root of the VFS which the given object is attached to.
AG_ObjectParent() returns the object's parent.

The
AG_ObjectFind() function returns the object corresponding to the specified path name.
If there is no such object it returns NULL.

AG_ObjectFindParent() returns the first ancestor of the object matching either the name of the
object (if
name is non-NULL), or the inheritance hierarchy string of the parent (if
type is non-NULL).

AG_ObjectFindChild() searches the child objects directly under
obj for an object called
name. It returns a pointer to the object if found or NULL.

AG_ObjectGetName() returns an autoallocated string containing the full pathname of an object
(relative to the root of its VFS).
If insufficient memory is available to construct the path, it fails and
returns NULL.

AG_ObjectCopyName() copies the object's full pathname (relative to its VFS root) to a
fixed-size buffer
buf of size
bufSize in bytes.
It returns 0 on success or -1 if the buffer is too small for the full path.

AG_ObjectLock() and
AG_ObjectUnlock() acquire or release the locking device associated with the given object.
This is a mutex protecting all read/write members of the
AG_Object structure, except
parent, root and the list of child objects
cobjs which are all considered part of the virtual filesystem and are instead
protected by
AG_LockVFS().

The
AG_ObjectLock() mutex can be used as a general-purpose locking device which is guaranteed to
be held during processing of all events posted to the object as well as
during object operations such as
load() and
save().

AG_LockVFS() and
AG_UnlockVFS() acquire or release the lock which protect the layout of the entire VFS
which
obj is a part of.

Note: If Agar is compiled with
--disable-threads then
AG_ObjectLock(), AG_ObjectUnlock(), AG_LockVFS() and
AG_UnlockVFS() become no-ops.

AG_ObjectSetName() updates the name of the given object.
If the object is attached to a VFS then the VFS must be locked.

AG_ObjectGenName() generates a unique name for a child object of
obj. The class name in lowercase is used as prefix, followed by a number.
The generatedstring is copied to the fixed-size buffer
name of size
nameSize in bytes.
The
AG_ObjectGenNamePfx() variant generates a name using the specified prefix instead of the class name.

The
AGOBJECT_FOREACH_CHILD() macro iterates
child over every child object of
parent. The
child pointer is cast to the given structure
type, without type checking.
Example:

The first field
hier is the inheritance hierarchy string.
For example, "AG_Widget:AG_Button" says that
AG_Button is a direct subclass of
AG_Widget (and
AG_Widget is implicitely a subclass of
AG_Object).

Alternatively, if a namespace called "Agar" exists and is mapped to the
"AG_" prefix then the inheritance hierarchy can be also written as
"Agar(Widget:Button)".
If implementing the class requires specific libraries available as dynamically
loaded modules via
AG_DSO(3), this can be indicated in the
hier string by a terminating "@" followed by one or more library names, separated
by commas.
For example:

"AG_Widget:MY_Widget@myLib,myOtherLib"

The
size member specifies the size in bytes of the object instance structure.
ver is an optional datafile version number (see
AG_Version(3)).

init() initializes a new object instance.
It is called after successful allocation of a new object by
AG_ObjectNew() or
AG_ObjectInit().

reset() restores the state of the object to an initial state.
It is invoked by
AG_ObjectLoad() prior to
load(), and also by
AG_ObjectDestroy() prior to
destroy().

destroy() frees all resources allocated by
init() (excluding any which were freed previously by
reset()).

load() reads the serialized state of object
obj from data source
ds. save() saves the state of
obj to data source
ds. load() and
save() must both return 0 on success or -1 on failure.
See
AG_DataSource(3) and the
SERIALIZATION section.

edit() is an application-specific method.
In a typical Agar GUI application
edit() may generate and return an
AG_Window(3) or an
AG_Box(3).

AG_UnregisterClass() removes the specified object class.

AG_CreateClass() offers an alternative to passing a statically-initialized
AG_ObjectClass to
AG_RegisterClass(). The
AG_CreateClass() function allocates and initializes an
AG_ObjectClass structure (or derivative thereof).
AG_ClassSetInit(), AG_ClassSetReset(), AG_ClassSetDestroy(), AG_ClassSetLoad(), AG_ClassSetSave() and
AG_ClassSetEdit() can be used to subsequently set the function pointers for the individual
operations.
They return a pointer to the previous operation.
AG_DestroyClass() unregisters and frees an auto-allocated
AG_ObjectClass (or derivative thereof).

AG_RegisterNamespace() registers a new namespace with the specified name, prefix and informational
URL.
For example, Agar registers its own using:

Once the namespace is registered, it is possible to specify inheritance
hierarchies using the partitioned
namespace format:

Agar(Widget:Button):MyLib(MyButton)

which is equivalent to the conventional format:

AG_Widget:AG_Button:MY_Button

The
AG_UnregisterNamespace() function removes all information about the specified namespace.

The
AG_LookupClass() function looks up the
AG_ObjectClass structure describing the specified class (in namespace or expanded format).
If there is no currently registered class matching the specification,
AG_LookupClass() returns NULL.

AG_LoadClass() ensures that the object class specified in
classSpec (see
AG_RegisterClass() for details on the format) is registered, possibly loading one or more
dynamic library files if they are specified in the string.
Dynamic library dependencies are given in the form of a terminating
@lib1,lib2,... string.
AG_LoadClass() scans the registered module directories (see
AG_RegisterModuleDirectory()) for the libraries specified in the string.
Bare library names are given (the actual filenames are platform-dependent).
Libraries that are found (and not already in memory) are loaded via
AG_DSO(3). The first library must define a
myFooClass symbol (where
myFoo is the name of the class transformed from
MY_Foo), for an
AG_ObjectClass structure describing the class (i.e., the same structure that is passed to
AG_RegisterClass()).

AG_UnloadClass() unregisters the specified class and also decrements the reference count of
any dynamically-located module associated with it.
If this reference count reaches zero, the module is removed from the current
process's address space.

The
AG_RegisterModuleDirectory() function adds the specified directory to the module search path.
AG_UnregisterModuleDirectory() removes the specified directory from the search path.

Given an inheritance hierarchy string (with wildcards),
AG_OfClass() evaluates whether
obj is an instance of the
specified class and returns a boolean (0 = False, 1 = True) indicating
whether the object is an instance of a matching class.
For example:

AG_Button *btn = AG_ButtonNew( ... );
if (AG_OfClass(btn, "AG_Widget:AG_Button")) {
/*
* btn is an instance of AG_Button, and *not* a subclass of it.
*/
}
if (AG_OfClass(btn, "AG_Widget:AG_Button:*")) {
/*
* btn is an instance of AG_Button, or a subclass of AG_Button.
*/
}

Fast paths are provided for patterns such as "Super:Sub:*" and "Super:Sub",
but patterns such as "Super:*:Sub:*" are also supported.

AG_ObjectGetClassName() returns a newly-allocated string containing the name of the class of an
object
obj. If
full is 1, return the complete inheritance hierarchy (e.g., "AG_Widget:AG_Button").
Otherwise, return only the subclass (e.g., "AG_Button").

AG_ObjectSuperclass() returns a pointer to the
AG_ObjectClass structure describing the superclass of
obj. If
obj is an instance of the base class (AG_Object), then a pointer to the
AG_Object class is returned.

The
AG_ObjectGetInheritHier() function returns into
pHier an array of
AG_ObjectClass pointers describing the inheritance hierarchy of an object.
The size of the array is returned into
nHier. If the returned item count is > 0, the returned array should be freed when
no longer in use.
AG_ObjectGetInheritHier() returns 0 on success or -1 if there is insufficient memory.

The
AGOBJECT_FOREACH_CLASS() macro iterates
child over every child object of
parent which is an instance of the class specified by
pattern. child is cast to the given structure
type. Example:

RELEASING RESOURCES

voidAG_ObjectDestroy (AG_Object *obj)

voidAG_ObjectReset (AG_Object *obj)

voidAG_ObjectFreeEvents (AG_Object *obj)

voidAG_ObjectFreeVariables (AG_Object *obj)

voidAG_ObjectFreeChildren (AG_Object *obj)

AG_ObjectReset() restores the state of an object to some initial state.
It invokes the object's
reset(), which is expected to bring the object to a consistent state prior to
deserialization (before
load()).

AG_ObjectDestroy() frees all resources allocated by an object.
It invokes the
reset() and
destroy() methods over each class in the inheritance hierarchy.
AG_ObjectDestroy() also cancels any scheduled
AG_Timer(3) expiration.
AG_ObjectDestroy() implies
AG_ObjectFreeEvents(), AG_ObjectFreeVariables() and
AG_ObjectFreeChildren(). Unless
AG_OBJECT_STATIC is set,
AG_ObjectDestroy() also implies
free(3).

AG_ObjectFreeVariables() clears the property table (i.e., the table of
AG_Variable(3)) associated with the object.

AG_ObjectFreeChildren() invokes
AG_ObjectDetach() and
AG_ObjectDestroy() on all child objects of
parent.

SERIALIZATION

intAG_ObjectLoad (AG_Object *obj)

intAG_ObjectLoadFromFile (AG_Object *obj, const char *file)

intAG_ObjectLoadFromDB (AG_Object *obj, AG_Db *db, const AG_Dbt *key)

intAG_ObjectLoadData (AG_Object *obj)

intAG_ObjectLoadDataFromFile (AG_Object *obj, const char *file)

intAG_ObjectLoadGeneric (AG_Object *obj)

intAG_ObjectLoadGenericFromFile (AG_Object *obj, const char *file)

intAG_ObjectSave (AG_Object *obj)

intAG_ObjectSaveAll (AG_Object *obj)

intAG_ObjectSaveToFile (AG_Object *obj, const char *path)

intAG_ObjectSaveToDB (AG_Object *obj, AG_Db *db, const AG_Dbt *key)

intAG_ObjectSerialize (AG_Object *obj, AG_DataSource *ds)

intAG_ObjectUnserialize (AG_Object *obj, AG_DataSource *ds)

intAG_ObjectReadHeader (AG_DataSource *ds, AG_ObjectHeader *header)

intAG_ObjectPageIn (AG_Object *obj)

intAG_ObjectPageOut (AG_Object *obj)

These functions implement serialization, or archiving of the state of an
AG_Object to a flat, machine-independent binary format.

The
AG_ObjectLoad*() family of functions load the state of an Agar object from some binary data
source.
The generic
AG_Object state is loaded first, followed by the object's serialized data (which is read
by invoking the
load() function of every class in the inheritance hierarchy).
The
AG_ObjectLoad(), AG_ObjectLoadGeneric() and
AG_ObjectLoadData() functions look for an archive file in the default search path (using the
load-path setting of
AG_Config(3)). The
AG_ObjectLoadFromFile(), AG_ObjectLoadGenericFromFile() and
AG_ObjectLoadDataFromFile() variants attempt to load the object state from a specific file.
The
AG_ObjectLoadFromDB() variant loads the object state from the given
AG_Db(3) database entry.

The
AG_ObjectSave*() family of functions serialize and save the state of the given object.
The generic
AG_Object state is written first, followed by the object's serialized data
(which is written by invoking the
save() function of every class in the inheritance hierarchy).
The
AG_ObjectSave() function creates an archive of the given object in the default location
(i.e., the
save-path setting of
AG_Config(3)). The
AG_ObjectSaveAll() variant saves the object's children as well as the object itself.
AG_ObjectSaveToFile() archives the object to the specified file.
AG_ObjectSaveToDB() archives the object to the given
AG_Db(3) entry.

The
AG_ObjectSerialize() function writes an archive of the given object to the specified
AG_DataSource(3), and
AG_ObjectUnserialize() reads an archive of the given object.

The
AG_ObjectReadHeader() routine attempts to read the header of a serialized Agar object from a
AG_DataSource(3) and returns 0 on success or -1 if no valid header could be read.
On success, header information is returned into the
header structure:

The
AG_ObjectPageIn() function loads an object's data into memory and sets the
AG_OBJECT_RESIDENT flag.
AG_ObjectPageOut() checks whether an object is referenced by another object and if that is
not the case, the data is serialized to permanent storage, freed from
memory and
AG_OBJECT_RESIDENT is cleared.
Both functions return 0 on success or -1 if an error has occurred.

FLAGS

The following public
AG_Object flags are defined:

AG_OBJECT_FLOATING_VARS

Remove all entries of the
AG_Variable(3) table in
AG_ObjectLoad(). By default, the existing table is preserved and entries are created or
replaced by items found in the archive.

AG_OBJECT_NON_PERSISTENT

Disables archiving of the object and its children.
If set,
AG_ObjectSave() becomes a no-op and
AG_ObjectLoad() calls will fail.

AG_OBJECT_INDESTRUCTIBLE

Application-specific advisory flag.

AG_OBJECT_RESIDENT

The object's data exists in memory.
Set by
AG_ObjectPageIn() and
AG_ObjectPageOut().

AG_OBJECT_STATIC

Object is statically allocated (or allocated via a facility other than
malloc(3)). Disable use of
free(3) by
AG_ObjectDestroy().

AG_OBJECT_READONLY

Application-specific advisory flag.

AG_OBJECT_REOPEN_ONLOAD

If an
edit() operation is defined, indicate that elements associated with its return
value (such as GUI windows or elements in the case of a GUI application)
should be recreated whenever
AG_ObjectLoad() is used.

AG_OBJECT_REMAIN_DATA

Prevent the object's data from being freed by
AG_ObjectReset() when a
AG_ObjectPageOut() call is made and the reference count reaches zero.

AG_OBJECT_DEBUG

Application-specific debugging flag.

AG_OBJECT_NAME_ONATTACH

Automatically generate a unique name for the object as soon as
AG_ObjectAttach() occurs.

AG_OBJECT_CHLD_AUTOSAVE

Serialize the object's children in
AG_ObjectSerialize().

EVENTS

The
AG_Object mechanism generates the following events:

attached (AG_Object *parent)

The object has been attached to a new parent.

detached (AG_Object *parent)

The object has been detached from its parent.

renamed (void)

The object's name has changed.

object-post-load (const char *path)

Invoked by
AG_ObjectLoadData(), on success.
If the object was loaded from file,
path is the pathname of the file.

bound (AG_Variable *V)

A new variable binding has been created, or the value of an existing binding
has been updated; see
AG_Variable(3) for details.

List of child objects.
The
AGOBJECT_FOREACH_CHILD(), AGOBJECT_FOREACH_CHILD_REVERSE(), AGOBJECT_NEXT_CHILD(), AGOBJECT_LAST_CHILD() and
AGOBJECT_FOREACH_CLASS() macros can be used to iterate over this list.

EXAMPLES

The
SG(3) scene-graph structure of Agar-SG is a VFS of
SG_Node(3) objects.
Non-visible nodes can be paged out to storage, saving memory.

Edacious (https://edacious.org/) represents circuits, components and simulation
data using an in-memory VFS.
Circuits are saved to a flat binary file which embeds the circuit's serialized
data with that of its sub-components (which may include third-party components,
in which case
AG_Object will autoload any required DSOs).