AG_OBJECT(3)

NAME
AG_Object - agar object system

SYNOPSIS

    #include <agar/core.h>

DESCRIPTION
The Agar object system provides object-oriented programming capabilities (inheritance and virtual functions) as well as serialization, managed timers, abstracted data types and a virtual filesystem (VFS).

Agar objects are organized in a tree structure, which we refer to as the VFS. Any AG_Object can become the root of a VFS. A VFS can be made persistent to the degree required by the application. Agar objects can be serialized to a machine-independent binary format with AG_DataSource(3).

OBJECT INTERFACE
AG_Object * AG_ObjectNew(AG_Object *parent, const char *name, AG_ObjectClass *classInfo)

void AG_ObjectInit(AG_Object *obj, AG_ObjectClass *classInfo)

void AG_ObjectAttach(AG_Object *newParent, AG_Object *child)

void AG_ObjectDetach(AG_Object *child)

void AG_ObjectMoveToHead(AG_Object *obj)

void AG_ObjectMoveToTail(AG_Object *obj)

void AG_ObjectMoveUp(AG_Object *obj)

void AG_ObjectMoveDown(AG_Object *obj)

void AG_ObjectDelete(AG_Object *obj)

AG_Object * AG_ObjectRoot(AG_Object *obj)

AG_Object * AG_ObjectParent(AG_Object *obj)

AG_Object * AG_ObjectFind(AG_Object *vfsRoot, const char *format, ...)

AG_Object * AG_ObjectFindS(AG_Object *vfsRoot, const char *name)

AG_Object * AG_ObjectFindParent(AG_Object *obj, const char *name, const char *type)

AG_Object * AG_ObjectFindChild(AG_Object *obj, const char *name)

char * AG_ObjectGetName(AG_Object *obj)

int AG_ObjectCopyName(AG_Object *obj, char *buf, AG_Size bufSize)

void AG_ObjectSetName(AG_Object *obj, const char *fmt, ...)

void AG_ObjectSetNameS(AG_Object *obj, const char *name)

void AG_ObjectGenName(AG_Object *obj, AG_ObjectClass *classInfo, char *name, AG_Size nameSize)

void AG_ObjectGenNamePfx(AG_Object *obj, const char *prefix, char *name, AG_Size nameSize)

AGOBJECT_FOREACH_CHILD(AG_Object *child, AG_Object *parent, TYPE type)

The AG_ObjectNew() function allocates and initializes a new object instance of the class described by classInfo. The object is attached to parent (unless parent is NULL). If insufficient memory is available, or parent and name are both specified and a child object of the given name already exists then AG_ObjectNew() fails and return NULL.

AG_ObjectInit() initializes an already-allocated instance of the class described by classInfo. It invokes the init() method over each class in the inheritance hierarchy.

The name argument to AG_ObjectNew() and AG_ObjectInit() may be NULL, in which case a unique name (e.g., "object0") will be generated. name may not exceed AG_OBJECT_NAME_MAX characters and must not contain ‘/’. The classInfo argument must point to an initialized AG_ObjectClass structure (see OBJECT CLASSES).

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

AG_ObjectDetach() removes an object child from its parent (if any), cancelling scheduled AG_Timer(3) expirations before raising ‘detached’.

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() calls 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 class of the object (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. Components of the path are separated by the path separator AG_PATHSEP ("/" or "\"). The returned pathname is guaranteed to begin and to end with the path separator. AG_ObjectCopyName() returns 0 on success or -1 if buf is too small to contain the complete path.

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 (converted to lowercase) is used as prefix followed by an integer (starting from 0 and counting up). The generated string 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 type. In Debug builds, an object validity and class-membership test is performed.

OBJECT CLASSES
void AG_RegisterClass(AG_ObjectClass *classInfo)

void AG_UnregisterClass(AG_ObjectClass *classInfo)

AG_ObjectClass * AG_CreateClass(const char *classSpec, AG_Size objectSize, AG_Size classSize, Uint major, Uint minor)

AG_ObjectInitFn AG_ClassSetInit(AG_ObjectClass *cl, AG_ObjectInitFn fn)

AG_ObjectResetFn AG_ClassSetReset(AG_ObjectClass *cl, AG_ObjectResetFn fn)

AG_ObjectDestroyFn AG_ClassSetDestroy(AG_ObjectClass *cl, AG_ObjectDestroyFn fn)

AG_ObjectLoadFn AG_ClassSetLoad(AG_ObjectClass *cl, AG_ObjectLoadFn fn)

AG_ObjectSaveFn AG_ClassSetSave(AG_ObjectClass *cl, AG_ObjectSaveFn fn)

AG_ObjectEditFn AG_ClassSetEdit(AG_ObjectClass *cl, AG_ObjectEditFn fn)

void AG_DestroyClass(AG_ObjectClass *cl)

void AG_RegisterNamespace(const char *name, const char *prefix, const char *url)

void AG_UnregisterNamespace(const char *name)

AG_ObjectClass * AG_LookupClass(const char *classSpec)

AG_ObjectClass * AG_LoadClass(const char *classSpec)

void AG_RegisterModuleDirectory(const char *path)

void AG_UnregisterModuleDirectory(const char *path)

int AG_OfClass(AG_Object *obj, const char *pattern)

char * AG_ObjectGetClassName(const AG_Object *obj, int full)

AG_ObjectClass * AG_ObjectSuperclass(const AG_Object *obj)

int AG_ObjectGetInheritHier(AG_Object *obj, AG_ObjectClass **pHier, int *nHier)

AGOBJECT_FOREACH_CLASS(AG_Object *child, AG_Object *parent, TYPE type, const char *pattern)

The AG_RegisterClass() function registers a new object class. classInfo should be an initialized AG_ObjectClass structure:

    typedef struct ag_object_class {
    	char hier[AG_OBJECT_HIER_MAX];	/* Full inheritance hierarchy */
    	AG_Size size;             	/* Size of instance structure */
    	AG_Version ver;          	/* Version numbers */
    	void (*init)(void *obj);
    	void (*reset)(void *obj);
    	void (*destroy)(void *obj);
    	int  (*load)(void *obj, AG_DataSource *ds, const AG_Version *ver);
    	int  (*save)(void *obj, AG_DataSource *ds);
    	void *(*edit)(void *obj);
    } AG_ObjectClass;

New methods (and other class-specific data) can be added by overloading AG_ObjectClass. For example, AG_WidgetClass adds GUI-specific methods:

    typedef struct ag_widget_class {
    	struct ag_object_class _inherit;
    	void (*draw)(void *);
    	void (*size_request)(void *, AG_SizeReq *);
    	/* ... */
    } AG_WidgetClass;

The first field hier describes the full inheritance hierarchy. The string "Agar(Widget:Button)" (or "AG_Widget:AG_Button") says that AG_Button is a direct subclass of AG_Widget.

If a class requires dynamically loadable modules (see AG_DSO(3)), the list of modules can be indicated in the hier string by appending "@" and a comma-separated list of library names. 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)).

The init() method initializes a new object instance. It's called by AG_ObjectInit() (and AG_ObjectNew() after a successful allocation).

The reset() method is an optional cleanup routine. It's called by AG_ObjectLoad() before load() and by AG_ObjectDestroy() before destroy().

The destroy() method frees all resources allocated by the object.

The load() method 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() provides an alternative interface to the passing of a statically-initialized AG_ObjectClass structure to AG_RegisterClass(). AG_CreateClass() allocates and initializes an AG_ObjectClass structure (or derivative thereof). AG_ClassSetInit(), AG_ClassSetReset(), AG_ClassSetDestroy(), AG_ClassSetLoad(), AG_ClassSetSave() and AG_ClassSetEdit() set the function pointer for the respective method (returning the previous one).

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:

    AG_RegisterNamespace("Agar", "AG_", "https://libagar.org/");

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

AG_UnregisterNamespace() deletes the specified namespace.

The AG_LookupClass() function searches for the AG_ObjectClass structure corresponding to the given class The classSpec string can be in conventional or partitioned namespace format (see AG_RegisterNamespace()). If the search is unsuccessful, it returns NULL.

AG_LoadClass() ensures that the class specified by the classSpec string is registered, possibly loading dynamic libraries if needed. If the class description string includes libraries (e.g., "@lib1,lib2") then the registered modules directories (see AG_RegisterModuleDirectory()) will be scanned and the needed modules loaded automatically.

Library names in class description strings should be bare, without prefix or suffix (the actual filename on disk being platform-dependent). Valid libraries 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’), which should be a pointer to the AG_ObjectClass describing the object class.

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.

The AG_OfClass() function tests whether obj is an instance of the class described by pattern and returns 1 if the object belongs to that class. pattern is a class description string which may include "*" wildcards. For example, "AG_Widget:AG_Button:*" would match the "AG_Button" class or any subclass thereof. The pattern "AG_Widget:AG_Button" would match only the "AG_Button" class but not its subclasses. Fast paths are provided for patterns such as "Super:Sub:*" and "Super:Sub", but general 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 class description structure of the superclass of obj. If obj is an instance of the base AG_Object class then the base class (i.e., agObjectClass 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 in pattern. The pattern argument is a class description string (that may include "*"). The child variable is cast to type with type checking in Debug builds (and no checking in Release builds).

SERIALIZATION
int AG_ObjectLoad(AG_Object *obj)

int AG_ObjectLoadFromFile(AG_Object *obj, const char *file)

int AG_ObjectLoadFromDB(AG_Object *obj, AG_Db *db, const AG_Dbt *key)

int AG_ObjectLoadData(AG_Object *obj)

int AG_ObjectLoadDataFromFile(AG_Object *obj, const char *file)

int AG_ObjectLoadGeneric(AG_Object *obj)

int AG_ObjectLoadGenericFromFile(AG_Object *obj, const char *file)

int AG_ObjectSave(AG_Object *obj)

int AG_ObjectSaveAll(AG_Object *obj)

int AG_ObjectSaveToFile(AG_Object *obj, const char *path)

int AG_ObjectSaveToDB(AG_Object *obj, AG_Db *db, const AG_Dbt *key)

int AG_ObjectSerialize(AG_Object *obj, AG_DataSource *ds)

int AG_ObjectUnserialize(AG_Object *obj, AG_DataSource *ds)

int AG_ObjectReadHeader(AG_DataSource *ds, AG_ObjectHeader *header)

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 part of the object is loaded first, followed by any class-specific serialized data (which is read by invoking the load() function over 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 AG_CONFIG_PATH_DATA 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).

AG_ObjectSave() creates an archive of the object in the default location (the AG_CONFIG_PATH_DATA 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:

    typedef struct ag_object_header {
    	char hier[AG_OBJECT_HIER_MAX];	    /* Inheritance hierarchy */
    	char libs[AG_OBJECT_LIBS_MAX];	    /* Library list */
    	char classSpec[AG_OBJECT_HIER_MAX]; /* Full class string */
    	Uint32 dataOffs;                    /* Dataset offset */
    	AG_Version ver;                     /* AG_Object version */
    	Uint flags;                         /* Object flags */
    } AG_ObjectHeader;

FINALIZATION
void AG_ObjectDestroy(AG_Object *obj)

void AG_ObjectReset(AG_Object *obj)

void AG_ObjectFreeEvents(AG_Object *obj)

void AG_ObjectFreeVariables(AG_Object *obj)

void AG_ObjectFreeChildren(AG_Object *obj)

void AG_ObjectFreeChildrenOfType(AG_Object *obj, const char *pattern)

void AG_ObjectFreeChildrenOfTypeLockless(AG_Object *obj, const char *pattern)

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_ObjectFreeEvents() clears all configured event handlers (also cancelling any scheduled timer expirations).

AG_ObjectFreeVariables() clears the AG_Variable(3) table of the object.

The AG_ObjectFreeChildren() routine invokes AG_ObjectDetach() and AG_ObjectDestroy() over every child object under parent.

AG_ObjectFreeChildrenOfType() invokes AG_ObjectDetach() and AG_ObjectDestroy() on every child object under parent which is an instance of the class described by pattern (see AG_OfClass(3)). The AG_ObjectFreeChildrenOfTypeLockless() variant assumes obj is locked.

THREADS
void AG_ObjectLock(AG_Object *obj)

void AG_ObjectUnlock(AG_Object *obj)

void AG_LockVFS(AG_Object *obj)

void AG_UnlockVFS(AG_Object *obj)

void AG_ObjectDetachLockless(AG_Object *child)

void AG_ObjectFreeChildrenLockless(AG_Object *obj)

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 method such as load() and save().

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

Agar is compiled without threads support ("--disable-threads") then AG_ObjectLock(), AG_ObjectUnlock(), AG_LockVFS() and AG_UnlockVFS() are no-ops.

The AG_ObjectDetachLockless() call is a variant of AG_ObjectDetach() which assumes that parent and child are locked.

The AG_ObjectFreeChildrenLockless() call is a variant of AG_ObjectFreeChildren() which assumes that parent and child are locked.

FLAGS
The following public AG_Object flags are defined:

AG_OBJECT_INDESTRUCTIBLE
    Application-specific advisory flag.

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_DEBUG
    Application-specific debugging flag.

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

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.

STRUCTURE DATA
For the AG_ObjectClass structure (see OBJECT CLASSES section):

char *hier
    Full inheritance hierarchy.
AG_Size size
    Size of instance structure (in bytes).
AG_Version ver
    Versioning information (see AG_Version(3)).
void (*init)
    Initialization routine.
void (*reset)
    Cleanup routine (for AG_ObjectReset()).
void (*destroy)
    Final cleanup routine.
int (*load)
    Deserialization routine.
int (*save)
    Serialization routine.
void *(*edit)
    Application-specific entry point.

The following read-only members are initialized internally:

char *name
    The name for this class only.
char *libs
    Comma-separated list of DSO modules.
AG_ObjectClass *super
    Pointer to the superclass.
TAILQ(AG_ObjectClass) sub
    Direct subclasses of this class.

For the AG_Object structure:

char name[AG_OBJECT_NAME_MAX]
    Unique (in parent) identifier for this object instance. May not contain ‘/’.

AG_ObjectClass *cls
    A pointer to the AG_ObjectClass for this object's class (see OBJECT CLASSES).

Uint flags
    Option flags for this object instance (see FLAGS section).

TAILQ(AG_Event) events
    Table of registered event handlers (set by AG_SetEvent(3)) and virtual functions (set by AG_Set<Type>Fn()).

TAILQ(AG_Timer) timers
    Active timers (see AG_Timer(3)).

TAILQ(AG_Variable) vars
    Named variables (see AG_Variable(3)).

TAILQ(AG_Object) children
    Child objects.

EXAMPLES
The following code initializes a stack-allocated object, prints a message on the debug console and finally destroys the object:

    AG_Object myObject;
    AG_ObjectInit(&obj, "myObject", &agObjectClass);
    Debug(&obj, "Hello, world!\n");
    AG_ObjectDestroy(&obj);

The following code creates a VFS representing a document, searches for an element by name and finally destroys the VFS:

    AG_Object *doc, *head, *title, *body, *p;
    AG_Object *result;
    doc   = AG_ObjectNew(NULL, "doc",   &agObjectClass);
    head  = AG_ObjectNew(doc,  "head",  &agObjectClass);
    title = AG_ObjectNew(head, "title", &agObjectClass);
    body  = AG_ObjectNew(doc,  "body",  &agObjectClass);
    p     = AG_ObjectNew(body, "p",     &agObjectClass);
    result = AG_ObjectFindS(doc, "/doc/head/title");
    if (result != NULL) {
    	AG_Verbose("Title = %s\n", result->name);
    }
    AG_ObjectDestroy(doc);

The following code transforms an object's name to upper-case:

    void
    ObjectNameToUpper(AG_Object *obj)
    {
    	char name[AG_OBJECT_NAME_MAX], *c;
    	if (AG_ObjectCopyName(obj, name,
    	    sizeof(name)) == -1) {
    		return;
    	}
    	for (c = name; *c != '\0'; c++) {
    		*c = toupper(*c);
    	}
    	AG_ObjectSetNameS(obj, name);
    }

The following code attaches an object to a parent, detaches it and then reattaches it to a different parent:

    AG_Object parent1, parent2, child;
    AG_ObjectInit(&parent1, NULL, &agObjectClass);
    AG_ObjectInit(&parent2, NULL, &agObjectClass);
    AG_ObjectInit(&child, NULL, &agObjectClass);
    AG_ObjectAttach(&parent1, &child);
    AG_ObjectDetach(&child);
    AG_ObjectAttach(&parent2, &child);

The following code uses AGOBJECT_FOREACH_CLASS to iterate child over every child object of the parent object which is an instance of "MyClass":

    struct my_class *chld;
    AGOBJECT_FOREACH_CLASS(chld, parent, my_class, "MyClass") {
    	printf("Child %s is an instance of MyClass.\n",
    	    AGOBJECT(chld)->name);
    }

The following code performs a class-membership test with a pattern:

    AG_Button *btn = AG_ButtonNew(NULL, 0, NULL);
    if (AG_OfClass(btn, "AG_Widget:AG_Button")) {
    	/*
    	 * btn is an instance of AG_Button,
    	 * and NOT a subclass thereof.
    	 */
    }
    if (AG_OfClass(btn, "AG_Widget:AG_Button:*")) {
    	/*
    	 * btn is an instance of AG_Button,
    	 * OR a subclass thereof.
    	 */
    }

The following code performs the same class-membership test using the numerical class identifier (as opposed to an AG_OfClass() pattern):

    AG_Button *btn = AG_ButtonNew(NULL, 0, NULL);
    if (OBJECT(btn)->cid == AGC_BUTTON) {
    	/*
    	 * btn is an instance of AG_Button,
    	 * and NOT a subclass thereof.
    	 */
    }
    if (AG_BUTTON_ISA(btn)) {
    	/*
    	 * btn is an instance of AG_Button
    	 * OR a subclass thereof.
    	 */
    }

The following code registers a new class "MyClass" and instantiates it:

    AG_ObjectClass myClass = {
    	"MY_Class",
    	sizeof(AG_Object),
    	{ 1,0, 1234, 0xE03A },
    	NULL,    /* init */
    	NULL,    /* reset */
    	NULL,    /* destroy */
    	NULL,    /* load */
    	NULL,    /* save */
    	NULL     /* edit */
    };
    AG_Object *obj;
    AG_RegisterClass(&myClass);
    obj = AG_ObjectNew(NULL, NULL, &myClass);
    Debug(obj, "Hello, world!\n");
    AG_ObjectDestroy(obj);

The following code implements a new class "MY_Dummy" which overloads AG_Object with new structure members. It also handles serialization and provides a public interface in C. The public header file follows:

    /*
     * Public header file for MY_Dummy (my_dummy.h).
     */
    typedef struct my_dummy {
    	struct ag_object _inherit; /* AG_Object ->
    	                              MY_Dummy */
    	Uint flags;
    #define MY_DUMMY_OPT1 0x01  /* Some option */
    #define MY_DUMMY_OPT2 0x02  /* Another option */
    	int x,y;            /* Some integers */
    	void *myData;       /* Allocated data */
    } MY_Dummy;
    extern AG_ObjectClass myDummyClass;
    MY_Dummy *_Nullable MY_DummyNew(int, int, Uint);

The implementation follows:

    /*
     * Implementation of MY_Dummy (my_dummy.c).
     */
    #define MYDATASIZE 1024
    MY_Dummy *
    MY_DummyNew(int x, int y, Uint flags)
    {
    	MY_Dummy *d;
    	d = AG_TryMalloc(sizeof(MY_Dummy));
    	if (d == NULL) {
    		return (NULL);
    	}
    	AG_ObjectInit(d, &mdDummyClass);
    	d->x = x;
    	d->y = y;
    	d->flags = flags;
    	return (d);
    }
    static void
    Init(void *_Nonnull obj)
    {
    	MY_Dummy *d = obj;
    	d->flags = 0;
    	d->x = 0;
    	d->y = 0;
    	d->myData = Malloc(MYDATASIZE);
    	memset(d->myData, 0, MYDATASIZE);
    }
    static void
    Destroy(void *_Nonnull obj)
    {
    	MY_Dummy *d = obj;
    	free(d->myData);
    }
    static int
    Load(void *_Nonnull obj, AG_DataSource *_Nonnull ds,
        const AG_Version *_Nonnull ver)
    {
    	MY_Dummy *d = obj;
    	d->flags = AG_ReadUint8(ds);
    	d->x = (int)AG_ReadUint16(ds);
    	d->y = (int)AG_ReadUint16(ds);
    	return AG_Read(ds, d->myData, MYDATASIZE);
    }
    static int
    Save(void *_Nonnull obj, AG_DataSource *_Nonnull ds)
    {
    	MY_Dummy *d = obj;
    	AG_WriteUint8(ds, (Uint8)(d->flags));
    	AG_WriteUint16(ds, (Uint16)d->x);
    	AG_WriteUint16(ds, (Uint16)d->y);
    	return AG_Write(ds, d->myData, MYDATASIZE);
    }
    AG_ObjectClass myDummyClass = {
    	"MY_Dummy",
    	sizeof(MY_Dummy),
    	{ 1,0, 0,0 },
    	Init,
    	NULL,      /* reset */
    	Destroy,
    	Load,
    	Save,
    	NULL       /* edit */
    };

The following code maps the "MY_" prefix to a new namespace "MyPackage" and uses it in the class description string classSpec passed to the AG_LookupClass() function:

    AG_ObjectClass *C;
    AG_RegisterNamespace("MyPackage", "MY_",
        "https://example.com/");
    AG_RegisterClass(&myImageViewerClass);
    C = AG_LookupClass("Agar(Widget):"
                       "MyPackage(ImageViewer)");
    if (C != NULL) {
    	AG_Verbose("Found class %s\n", C->name);
    	AG_Verbose("Structure size = %d\n", C->size);
    }

The following code prints the inheritance hierarchy of an object:

    void
    PrintInheritHier(AG_Object *obj)
    {
    	AG_ObjectClass *hier;
    	int nHier, i;
    	if (AG_ObjectGetInheritHier(obj,
    	    &hier, &nHier) != 0) {
    		AG_FatalError(NULL);
    	}
    	AG_Verbose("AG_Object");
    	for (i = 0; i < nHier; i++) {
    		AG_Verbose(" -> %s",
    		    hier[i]->name);
    	}
    	AG_Verbose("\n");
    	AG_Free(hier);
    }

The following code loads an object from a file, increments a variable counter and writes back the object to the file:

    AG_Object obj;
    int counter;
    AG_ObjectInit(&obj, NULL, &agObjectClass);
    AG_SetInt(&obj, "counter", 0);
    if (AG_ObjectLoadFromFile(&obj,
        "test.obj") == -1)
    	AG_FatalError(NULL);
    counter = AG_GetInt(&obj, "counter");
    AG_Debug(&obj, "Counter: %d -> %d\n",
        counter, counter+1);
    AG_SetInt(&obj, "counter", counter+1);
    AG_ObjectSaveToFile(obj, "test.obj");

The Agar GUI represents user interfaces using a tree of AG_Widget(3) objects attached to a parent AG_Window(3) which is itself attached to some parent AG_Driver(3).

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.hypertriton.com/) 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).

SEE ALSO
AG_Event(3), AG_Intro(3), AG_Timer(3), AG_Variable(3)

HISTORY
The AG_Object interface appeared in Agar 1.0. AG_ObjectFreeDataset() was renamed AG_ObjectReset() in Agar 1.6.0. The functions AG_CreateClass(), AG_ClassSetInit(), AG_ClassSetReset(), AG_ClassSetDestroy(), AG_ClassSetLoad(), AG_ClassSetSave(), AG_ClassSetEdit(), AG_DestroyClass() and AG_ObjectGetClassName() appeared in Agar 1.6.0.