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. Examples include:
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, a unique name of the form <class-name> #<number> is automatically 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 the specified class. This involves invoking the init() operation associated with every class in the inheritance hierarchy The name argument specifies a name for the object instance relative to its parent (maximum AG_OBJECT_NAME_MAX bytes and must not contain " / " characters). classInfo should point to an initialized AG_ObjectClass structure (see CLASSES section). The flags argument specifies a default set of flags (see FLAGS section).
The AG_ObjectInitStatic() variant of AG_ObjectInit() implicitely sets the AG_OBJECT_STATIC flag, indicating that the object's structure statically allocated (or allocated through a facility other than malloc(3)). The AG_ObjectInitNamed() variant either sets the object name with AG_ObjectSetNameS() if the name argument is non-NULL, or it implicitely sets the AG_OBJECT_NAME_ONATTACH flag if the name is NULL.
The function AG_ObjectAttach() attaches an object to a new parent object and AG_ObjectDetach() detaches an object from its current parent object. These operations raise attached and detached events. Prior to detaching the object, AG_ObjectDetach() cancels any scheduled AG_Timer(3) callback execution. If parent is NULL, AG_ObjectAttach() is a no-op.
The AG_ObjectMoveUp(), AG_ObjectMoveDown(), AG_ObjectMoveToHead() and AG_ObjectMoveToTail() functions move the object in the parent object's list of child objects. These functions are useful when the ordering is important - when the child objects represent a stack, for instance.
The AG_ObjectDelete() routine invokes AG_ObjectDetach() if the object is attached to a parent, followed by AG_ObjectDestroy().
AG_ObjectAttachToNamed() is a variant of AG_ObjectAttach() which looks up the parent inside the specified VFS using the pathname path.
AG_ObjectRoot() returns a pointer to the root of the VFS which the given object is attached to. AG_ObjectParent() returns the immediate parent of the given object.
The AG_ObjectFind() function returns the object corresponding to the specified path name. If there is no such object, NULL is returned.
AG_ObjectFindParent() returns the first ancestor of the object matching either the name or type string (whichever is non-NULL).
AG_ObjectFindChild() performs a name lookup on the immediate children of the specified object. The function returns the matching object if it was found, otherwise NULL.
AG_ObjectGetName() returns a newly-allocated string containing the full pathname of an object. The function returns NULL if insufficient memory is available. The AG_ObjectCopyName() function copies the object's pathname to a fixed-size buffer.
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 for the object. This mutex 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().
The AG_LockVFS() and AG_UnlockVFS() functions acquire or release the lock protecting the layout of the entire virtual system which the given object is part of.
Note that all lock/unlock routines above become no-ops if Agar is compiled with --disable-threads.
AG_ObjectSetName() updates the name of the given object. If the object is attached to a VFS, the VFS must locked.
AG_ObjectGenName() generates an object name string unique to the specified parent object obj. The class name is used as prefix, followed by a number. The name is written to the fixed-size buffer name of the given size len. In a multithreaded context, the name is only guaranteed to remain unique as long as the parent object's VFS is locked. The AG_ObjectGenNamePfx() variant generates a name using the specified prefix instead of the class name.
AG_ObjectSetAttachFn() and AG_ObjectSetDetachFn() allow custom "attach" and "detach" hooks to be registered. This is useful where it is necessary to control the order of the child objects (for example, in the AG_Window(3) system of Agar-GUI, the ordering of window objects is important as it determines the order of rendering). The hook function must insert/remove the child object directly from the parent's children list.
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 AG_RegisterClass() function registers a new object class. classInfo should be an initialized AG_ObjectClass structure:
We can define new operations (or other class-specific data) by overloading AG_ObjectClass. The AG_WidgetClass class in Agar-GUI, for instance, overloads AG_ObjectClass and adds 3 new methods:
The hier field of AG_ObjectClass specifies a inheritance hierarchy, in the form "MY_Superclass:MY_Subclass", or "MyNamespace(Superclass:Subclass)" . If implementing the class requires specific libraries available as dynamically loaded modules via AG_DSO(3), they may be appended to hier as a comma-separated list of library names preceded by "@". For example:
The size member specifies the size in bytes of the object instance structure. The ver member specifies 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. AG_ObjectLoad() invokes reset() implicitely prior to the load() operation. AG_ObjectDestroy() also invokes reset() implicitely prior to the destroy() operation.
destroy() frees all resources allocated by init() (excluding any resources already freed by reset()).
load() reads the serialized state of object obj from data source ds. It must return 0 on success or -1 if an error has occured. See: AG_DataSource(3) and SERIALIZATION section.
save() saves the state of obj to data source ds. It must return 0 on success or -1 if an error has occured. See: AG_DataSource(3) and SERIALIZATION section.
edit() is a user-defined / application-specific callback. In a typical GUI application, for example, edit() may be expected to create and return an AG_Window(3) or an AG_Box(3).
AG_UnregisterClass() removes the specified object class.
AG_RegisterNamespace() registers a new namespace with the specified name, prefix and informational URL. For example, Agar registers its own namespace using:
Once the namespace is registered, it is possible to specify inheritance hierarchies using the namespace format:
or the equivalent expanded format:
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.
The AG_OfClass() function evaluates whether the given object is an instance of the specified class. The pattern string may contain wildcards such as MyClass:* or MyClass:*:MySubclass:*. AG_OfClass() returns 1 if the object's class matches the given pattern.
The AG_ObjectSuperclass() function returns a pointer to the AG_ObjectClass structure for the superclass of an object. Exceptionally, if the object is an instance of the base class (AG_Object), 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:
The AG_ObjectReset() function restores the state of an object to some initial state. It invokes the object's reset() method. reset() is expected to bring the object to a consistent state prior to deserialization (before the load() method is invoked).
The AG_ObjectDestroy() function frees all resources reserved by the given object (and any of its children that is not being referenced). It invokes reset() and destroy() for each class in the inheritance hierarchy. Note that AG_ObjectDestroy() also cancels any AG_Timeout(3) event scheduled for future execution. Unless the AG_OBJECT_STATIC flag is set, AG_ObjectDestroy() frees the structure with free(3).
Internally, AG_ObjectDestroy() invokes AG_ObjectFreeEvents(), AG_ObjectFreeVariables(), AG_ObjectFreeDeps() and AG_ObjectFreeChildren(). These functions may be used to explicitely reinitialize the event handler table (cancelling any scheduled timed event), clear the AG_Variable(3) table, or destroy all attached child objects.
AG_ObjectFreeChildren() releases all resources allocated by the child objects of a given parent. The assumes that none of the child objects are currently in use.
AG_ObjectFreeDummyDeps() cleans up the dependency table, removing any entry with a reference count of zero (which occurs in objects with AG_OBJECT_PRESERVE_DEPS set).
AG_ObjectRemain() specifies the behavior of AG_ObjectPageOut() once the object's data is no longer in use. The default behavior is to free the data. If an argument of AG_OBJECT_REMAIN_DATA is passed, the data will be kept in memory.
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 occured.
AG_ObjectSetArchivePath() sets the application-specific Archive Path attribute. For example, the "Save" feature of an editor application might use the Archive Path to remember an object's last successful Save or Export location, and AG_ObjectGetArchivePath() to later retrieve it. The Archive Path is a run-time attribute ignored by AG_ObjectSave().
The following public
AG_Object flags are defined:
AG_Object mechanism generates the following events:
AG_ObjectClass structure (see
The following read-only members are initialized internally:
For the AG_Object structure:
|See tests/objsystem.c in the Agar source distribution.|
|AG_Event(3), AG_Intro(3), AG_Timeout(3), AG_Variable(3)|
|The AG_Object interface appeared in Agar 1.0|