AG_Object
— agar
object system
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).
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.
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).
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);
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.
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;
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.
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.
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.
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.
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.
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).
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.