.\" Copyright (c) 2009-2022 Julien Nadeau Carriere .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED .\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, .\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES .\" (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR .\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, .\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING .\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" .Dd December 21, 2022 .Dt AG_VARIABLE 3 .Os Agar 1.7 .Sh NAME .Nm AG_Variable .Nd agar object variables .Sh SYNOPSIS .Bd -literal #include .Ed .Sh DESCRIPTION In the Agar object system, each .Xr AG_Object 3 instance has a set of typed variables. Variables can be named (referenced by a string key) or anonymous (referenced by an .Ft AG_Variable pointer). Discrete types include: .Pp .Bl -bullet -compact -offset indent .It Integers (e.g., .Ft int , .Ft Uint , .Ft long , .Ft Sint16 , .Ft Uint64 ) . .It Real numbers (e.g., .Ft float , .Ft double , .Ft long double ) . .It C strings (unbounded; auto-allocated). .It Generic pointers. .It Functions. .El .Pp Typed references (or "pointer variables") are also supported. Optionally, pointer variables can be configured to acquire an .Xr AG_Mutex 3 locking device prior to accessing the data. Base reference types include: .Pp .Bl -bullet -compact -offset indent .It Integers (e.g., .Ft int * , .Ft Uint * , .Ft long * , .Ft Sint16 * , .Ft Uint64 * ) . .It Real numbers (e.g., .Ft float * , .Ft double * , .Ft long double * ) . .It Bits in a fixed-size word (per given bitmask). .It Bounded C strings (in fixed-size buffer). .It Agar objects (pointer to .Xr AG_Object 3 ) . .It Proxy for another .Nm in an external .Xr AG_Object 3 . .El .Sh GENERIC INTERFACE .nr nS 1 .Ft int .Fn AG_Defined "AG_Object *obj" "const char *name" .Pp .Ft "AG_Variable *" .Fn AG_GetVariable "AG_Object *obj" "const char *name" "void **data" .Pp .Ft "AG_Variable *" .Fn AG_AccessVariable "AG_Object *obj" "const char *name" .Pp .Ft "AG_Variable *" .Fn AG_FetchVariable "AG_Object *obj" "const char *name" "enum ag_variable_type type" .Pp .Ft "AG_Variable *" .Fn AG_FetchVariableOfType "AG_Object *obj" "const char *name" "enum ag_variable_type type" .Pp .Ft void .Fn AG_LockVariable "AG_Variable *var" .Pp .Ft void .Fn AG_UnlockVariable "AG_Variable *var" .Pp .Ft AG_Size .Fn AG_PrintVariable "char *dst" "AG_Size len" "AG_Variable *var" .Pp .Ft void .Fn AG_CopyVariable "AG_Variable *Vdst" "const AG_Variable *Vsrc" .Pp .Ft void .Fn AG_DerefVariable "AG_Variable *Vdst" "const AG_Variable *Vsrc" .Pp .Ft int .Fn AG_CompareVariables "const AG_Variable *a" "const AG_Variable *b" .Pp .Ft "void" .Fn AG_Unset "AG_Object *obj" "const char *name" .Pp .Ft void .Fn AG_VariableSubst "AG_Object *obj" "const char *s" "char *dst" "AG_Size dst_len" .Pp .nr nS 0 .Fn AG_Defined returns 1 if the variable .Fa name is defined under the object .Fa obj , otherwise it returns 0. The object .Fa obj must be locked. .Pp .Fn AG_GetVariable searches for a variable .Fa name under .Fa obj and returns a pointer to the corresponding .Ft AG_Variable in a locked condition. The caller must use .Fn AG_UnlockVariable when finished accessing the variable data. A pointer to the data itself is also returned in the .Fa data argument. If the variable is undefined, a fatal exception is raised. .Pp The .Fn AG_AccessVariable function searches for a variable by .Fa name and returns the matching .Ft AG_Variable in a locked condition. The caller must use .Fn AG_UnlockVariable when done accessing the data. Both .Fn AG_GetVariable and .Fn AG_AccessVariable return NULL if the named variable is undefined. .Pp The .Fn AG_FetchVariable function searches for a variable by .Fa name and .Fa type . If found, return the corresponding .Ft AG_Variable . If the variable is undefined then a new one of the specified .Fa type is automatically created and returned. Raises an exception if insufficient memory is available. .Pp The .Fn AG_FetchVariableOfType variant works like .Fn AG_FetchVariable , except that if the variable exists and is of a different type, then it is mutated into .Fa type and returned. .Pp Note: Unlike .Fn AG_GetVariable and .Fn AG_AccessVariable , .Fn AG_FetchVariable and .Fn AG_FetchVariableOfType do not return the .Ft AG_Variable locked. .Pp .Fn AG_LockVariable and .Fn AG_UnlockVariable acquire and release any locking device associated with the specified variable. .Pp .Fn AG_PrintVariable generates a string from the value of variable .Fa var . The string is written to the fixed-size buffer .Fa dst (of size .Fa len ) . .Fn AG_PrintVariable returns the length of the string it tried to create. .Pp .Fn AG_CopyVariable copies the contents of a variable from .Fa Vsrc to .Fa Vdst. Pointer references are preserved. Discrete strings are duplicated. .Pp .Fn AG_DerefVariable copies the contents of .Fa Vsrc to .Fa Vdst , converting pointer references to immediate values. Discrete strings are duplicated, and pointers to strings are turned into discrete strings. .Pp The .Fn AG_CompareVariables compares the value of two variables, returning zero if they are identical. If they differ, the difference between the two first differing bytes is returned. If .Fn AG_CompareVariables encounters pointer types, they are not dereferenced (rather the value of the pointer itself is compared). .Pp .Fn AG_Unset deletes the named object-bound variable. .Pp .Fn AG_VariableSubst parses the string .Fa s for references of the form "$(foo)", and substitutes those references for the value of variable .Va foo (under object .Fa obj ) . The substituted string is returned into fixed-size buffer .Fa dst , of size .Fa dst_size . .Sh TYPE-SPECIFIC INTERFACES The following functions get and set variables of specific types. .Pp .Fn AG_Get returns the value of variable .Fa name under object .Fa obj with implicit dereferencing. If the variable is a pointer type then the value referenced by it is returned. .Pp The .Fn AG_Init functions initialize an .Ft AG_Variable structure .Fa var with the specified value .Fa val . .Pp The .Fn AG_Set functions set the value of variable .Fa name to the specified value .Fa val . Implicit dereferencing is done. If the variable does not exist, it is created. .Pp The .Fn AG_Bind functions create or modify a typed pointer variable. The argument .Fa pVal is a pointer to the actual value. .Pp The .Fn AG_BindMp variant accepts an extra .Fa lock argument, which is a mutex device (i.e., an .Ft AG_Mutex or .Ft pthread_mutex_t ) to be acquired whenever the data referenced by .Fa pVal will be accessed. .Sh INTEGERS .nr nS 1 .Ft "Uint" .Fn AG_GetUint "AG_Object *obj" "const char *name" .Pp .Ft "void" .Fn AG_InitUint "AG_Variable *var" "Uint val" .Pp .Ft "AG_Variable *" .Fn AG_SetUint "AG_Object *obj" "const char *name" "Uint val" .Pp .Ft "AG_Variable *" .Fn AG_BindUint "AG_Object *obj" "const char *name" "Uint *pVal" .Pp .Ft "AG_Variable *" .Fn AG_BindUintMp "AG_Object *obj" "const char *name" "Uint *pVal" "AG_Mutex *lock" .Pp .Ft "int" .Fn AG_GetInt "AG_Object *obj" "const char *name" .Pp .Ft "void" .Fn AG_InitInt "AG_Variable *var" "int val" .Pp .Ft "AG_Variable *" .Fn AG_SetInt "AG_Object *obj" "const char *name" "int val" .Pp .Ft "AG_Variable *" .Fn AG_BindInt "AG_Object *obj" "const char *name" "int *pVal" .Pp .Ft "AG_Variable *" .Fn AG_BindIntMp "AG_Object *obj" "const char *name" "int *pVal" "AG_Mutex *lock" .Pp .Ft "Uint8" .Fn AG_GetUint8 "AG_Object *obj" "const char *name" .Pp .Ft "void" .Fn AG_InitUint8 "AG_Variable *var" "Uint8 val" .Pp .Ft "AG_Variable *" .Fn AG_SetUint8 "AG_Object *obj" "const char *name" "Uint8 val" .Pp .Ft "AG_Variable *" .Fn AG_BindUint8 "AG_Object *obj" "const char *name" "Uint8 *pVal" .Pp .Ft "AG_Variable *" .Fn AG_BindUint8Mp "AG_Object *obj" "const char *name" "Uint8 *pVal" "AG_Mutex *lock" .Pp .Ft "Sint8" .Fn AG_GetSint8 "AG_Object *obj" "const char *name" .Pp .Ft "void" .Fn AG_InitSint8 "AG_Variable *var" "Sint8 val" .Pp .Ft "AG_Variable *" .Fn AG_SetSint8 "AG_Object *obj" "const char *name" "Sint8 val" .Pp .Ft "AG_Variable *" .Fn AG_BindSint8 "AG_Object *obj" "const char *name" "Sint8 *pVal" .Pp .Ft "AG_Variable *" .Fn AG_BindSint8Mp "AG_Object *obj" "const char *name" "Sint8 *pVal" "AG_Mutex *lock" .Pp .Ft "Uint16" .Fn AG_GetUint16 "AG_Object *obj" "const char *name" .Pp .Ft "void" .Fn AG_InitUint16 "AG_Variable *var" "Uint16 val" .Pp .Ft "AG_Variable *" .Fn AG_SetUint16 "AG_Object *obj" "const char *name" "Uint16 val" .Pp .Ft "AG_Variable *" .Fn AG_BindUint16 "AG_Object *obj" "const char *name" "Uint16 *pVal" .Pp .Ft "AG_Variable *" .Fn AG_BindUint16Mp "AG_Object *obj" "const char *name" "Uint16 *pVal" "AG_Mutex *lock" .Pp .Ft "Sint16" .Fn AG_GetSint16 "AG_Object *obj" "const char *name" .Pp .Ft "void" .Fn AG_InitSint16 "AG_Variable *var" "Sint16 val" .Pp .Ft "AG_Variable *" .Fn AG_SetSint16 "AG_Object *obj" "const char *name" "Sint16 val" .Pp .Ft "AG_Variable *" .Fn AG_BindSint16 "AG_Object *obj" "const char *name" "Sint16 *pVal" .Pp .Ft "AG_Variable *" .Fn AG_BindSint16Mp "AG_Object *obj" "const char *name" "Sint16 *pVal" "AG_Mutex *lock" .Pp .Ft "Uint32" .Fn AG_GetUint32 "AG_Object *obj" "const char *name" .Pp .Ft "void" .Fn AG_InitUint32 "AG_Variable *var" "Uint32 val" .Pp .Ft "AG_Variable *" .Fn AG_SetUint32 "AG_Object *obj" "const char *name" "Uint32 val" .Pp .Ft "AG_Variable *" .Fn AG_BindUint32 "AG_Object *obj" "const char *name" "Uint32 *pVal" .Pp .Ft "AG_Variable *" .Fn AG_BindUint32Mp "AG_Object *obj" "const char *name" "Uint32 *pVal" "AG_Mutex *lock" .Pp .Ft "Sint32" .Fn AG_GetSint32 "AG_Object *obj" "const char *name" .Pp .Ft "void" .Fn AG_InitSint32 "AG_Variable *var" "Sint32 val" .Pp .Ft "AG_Variable *" .Fn AG_SetSint32 "AG_Object *obj" "const char *name" "Sint32 val" .Pp .Ft "AG_Variable *" .Fn AG_BindSint32 "AG_Object *obj" "const char *name" "Sint32 *pVal" .Pp .Ft "AG_Variable *" .Fn AG_BindSint32Mp "AG_Object *obj" "const char *name" "Sint32 *pVal" "AG_Mutex *lock" .Pp .Ft "Uint64" .Fn AG_GetUint64 "AG_Object *obj" "const char *name" .Pp .Ft "void" .Fn AG_InitUint64 "AG_Variable *var" "Uint64 val" .Pp .Ft "AG_Variable *" .Fn AG_SetUint64 "AG_Object *obj" "const char *name" "Uint64 val" .Pp .Ft "AG_Variable *" .Fn AG_BindUint64 "AG_Object *obj" "const char *name" "Uint64 *pVal" .Pp .Ft "AG_Variable *" .Fn AG_BindUint64Mp "AG_Object *obj" "const char *name" "Uint64 *pVal" "AG_Mutex *lock" .Pp .Ft "Sint64" .Fn AG_GetSint64 "AG_Object *obj" "const char *name" .Pp .Ft "void" .Fn AG_InitSint64 "AG_Variable *var" "Sint64 val" .Pp .Ft "AG_Variable *" .Fn AG_SetSint64 "AG_Object *obj" "const char *name" "Sint64 val" .Pp .Ft "AG_Variable *" .Fn AG_BindSint64 "AG_Object *obj" "const char *name" "Sint64 *pVal" .Pp .Ft "AG_Variable *" .Fn AG_BindSint64Mp "AG_Object *obj" "const char *name" "Sint64 *pVal" "AG_Mutex *lock" .Pp .nr nS 0 These functions provide an interface to both natural and fixed-size integers. The .Ft Uint64 and .Ft Sint64 types are only available if .Dv AG_HAVE_64BIT is defined. .Sh REAL NUMBERS .nr nS 1 .Ft "float" .Fn AG_GetFloat "AG_Object *obj" "const char *name" .Pp .Ft "void" .Fn AG_InitFloat "AG_Variable *var" "float val" .Pp .Ft "AG_Variable *" .Fn AG_SetFloat "AG_Object *obj" "const char *name" "float val" .Pp .Ft "AG_Variable *" .Fn AG_BindFloat "AG_Object *obj" "const char *name" "float *pVal" .Pp .Ft "AG_Variable *" .Fn AG_BindFloatMp "AG_Object *obj" "const char *name" "float *pVal" "AG_Mutex *lock" .Pp .Ft "double" .Fn AG_GetDouble "AG_Object *obj" "const char *name" .Pp .Ft "void" .Fn AG_InitDouble "AG_Variable *var" "double val" .Pp .Ft "AG_Variable *" .Fn AG_SetDouble "AG_Object *obj" "const char *name" "double val" .Pp .Ft "AG_Variable *" .Fn AG_BindDouble "AG_Object *obj" "const char *name" "double *pVal" .Pp .Ft "AG_Variable *" .Fn AG_BindDoubleMp "AG_Object *obj" "const char *name" "double *pVal" "AG_Mutex *lock" .Pp .nr nS 0 These functions provide an interface to floating-point numbers. .Sh C STRINGS .nr nS 1 .Ft "AG_Size" .Fn AG_GetString "AG_Object *obj" "const char *name" "char *dst" "AG_Size dst_size" .Pp .Ft "char *" .Fn AG_GetStringDup "AG_Object *obj" "const char *name" .Pp .Ft "char *" .Fn AG_GetStringP "AG_Object *obj" "const char *name" .Pp .Ft "void" .Fn AG_InitString "AG_Variable *var" "const char *s" .Pp .Ft "AG_Variable *" .Fn AG_SetString "AG_Object *obj" "const char *name" "const char *s" .Pp .Ft "AG_Variable *" .Fn AG_SetStringF "AG_Object *obj" "const char *name" "const char *fmt" "..." .Pp .Ft "AG_Variable *" .Fn AG_SetStringNODUP "AG_Object *obj" "const char *name" "const char *s" .Pp .Ft "AG_Variable *" .Fn AG_BindString "AG_Object *obj" "const char *name" "char *s" "AG_Size len" .Pp .Ft "AG_Variable *" .Fn AG_BindStringMp "AG_Object *obj" "const char *name" "char *s" "AG_Size len" "AG_Mutex *lock" .Pp .nr nS 0 These functions provide an interface to C strings. A string variable may contain an unbounded (auto-allocated) string or it may reference a bounded string (i.e., a string contained in a fixed-size buffer). .Pp .Fn AG_GetString copies the contents of a string variable to a fixed-size buffer .Fa dst of size .Fa dst_size and returns the number of bytes that would have been copied were .Fa dst_size unlimited. .Pp .Fn AG_GetStringDup returns a newly-allocated copy of the contents of a string variable. If the copy cannot be allocated, NULL is returned. The returned string should be freed with .Xr AG_Free 3 after use. .Pp The potentially-unsafe .Fn AG_GetStringP returns a direct pointer to the buffer containing the string. It is not free-threaded (so the object must be locked, and calls protected by .Fn AG_LockVariable ) . Auto-allocated strings set by .Fn AG_SetString may be accessed safely without locking as long as the parent object is locked. .Pp .Fn AG_InitString initializes a .Ft AG_Variable structure with the given string, which is copied from .Fa s . .Pp .Fn AG_SetString sets the value of a string variable (possibly creating a new variable). The .Fa s argument is a C string which will be either duplicated or copied. If the given variable exists and is a reference to a fixed-size buffer (i.e., it was generated by a .Fn AG_BindString call), then the contents of .Fa s are copied to the the referenced buffer. If the buffer is too small to fit the string, the string is safely truncated. The .Fa s argument may be set to NULL (in which case further .Fn AG_GetString calls will also return NULL). The .Fn AG_SetStringF variant accepts a .Xr printf 3 style format string argument. .Pp The potentially-unsafe .Fn AG_SetStringNODUP variant accepts a pointer to a dynamically-allocated string buffer which will be free'd whenever the parent object is destroyed. .Pp .Fn AG_BindString creates or modifies a variable referencing a fixed-size string buffer .Fa s , of size .Fa len . .Sh GENERIC POINTERS .nr nS 1 .Ft "void *" .Fn AG_GetPointer "AG_Object *obj" "const char *name" .Pp .Ft "void" .Fn AG_InitPointer "AG_Variable *var" "void *val" .Pp .Ft "AG_Variable *" .Fn AG_SetPointer "AG_Object *obj" "const char *name" "void *val" .Pp .Ft "AG_Variable *" .Fn AG_BindPointer "AG_Object *obj" "const char *name" "void **pVal" .Pp .Ft "AG_Variable *" .Fn AG_BindPointerMp "AG_Object *obj" "const char *name" "void **pVal" "AG_Mutex *lock" .Pp .nr nS 0 These functions provide an interface to generic pointer types. .Sh FUNCTIONS .nr nS 1 .Ft "AG_Variable *" .Fn AG_SetFn "AG_Object *obj" "const char *name" "AG_EventFn fn" "const char *fmt" "..." .Pp .nr nS 0 .Fn AG_SetFn sets the value of a function pointer variable to the specified function .Fa fn and optional function arguments .Fa fmt . The object must be locked. See .Xr AG_Event 3 for more information on the argument format. .Sh BITS .nr nS 1 .Ft "AG_Variable *" .Fn AG_BindFlag "AG_Object *obj" "const char *name" "Uint *pVal" "Uint bitmask" .Pp .Ft "AG_Variable *" .Fn AG_BindFlagMp "AG_Object *obj" "const char *name" "Uint *pVal" "Uint bitmask" "AG_Mutex *lock" .Pp .Ft "AG_Variable *" .Fn AG_BindFlag8 "AG_Object *obj" "const char *name" "Uint8 *pVal" "Uint8 bitmask" .Pp .Ft "AG_Variable *" .Fn AG_BindFlag8Mp "AG_Object *obj" "const char *name" "Uint8 *pVal" "Uint8 bitmask" "AG_Mutex *lock" .Pp .Ft "AG_Variable *" .Fn AG_BindFlag16 "AG_Object *obj" "const char *name" "Uint16 *pVal" "Uint16 bitmask" .Pp .Ft "AG_Variable *" .Fn AG_BindFlag16Mp "AG_Object *obj" "const char *name" "Uint16 *pVal" "Uint16 bitmask" "AG_Mutex *lock" .Pp .Ft "AG_Variable *" .Fn AG_BindFlag32 "AG_Object *obj" "const char *name" "Uint32 *pVal" "Uint32 bitmask" .Pp .Ft "AG_Variable *" .Fn AG_BindFlag32Mp "AG_Object *obj" "const char *name" "Uint32 *pVal" "Uint32 bitmask" "AG_Mutex *lock" .Pp .nr nS 0 These functions provide an interface for binding to specific bits in integers. They follow the standard form, with an extra .Fa bitmask argument. .Sh OBJECT-TO-OBJECT REFERENCES .nr nS 1 .Ft "AG_Variable *" .Fn AG_BindObject "AG_Object *obj" "const char *name" "AG_Object *varObj" .Pp .Ft "AG_Variable *" .Fn AG_BindVariable "AG_Object *obj" "const char *name" "AG_Object *varObj" "const char *varKey" .Pp .nr nS 0 The .Fn AG_BindObject function creates an Object->Object reference and hard dependency to an external object .Fa varObj and return a .Dv P_OBJECT type Variable on success. A hard dependency implies that if both .Fa obj and .Fa varObj share the same VFS then Agar will not allow .Fa varObj to be released from memory (or detached from the VFS) for as long as the reference exists. .Pp The .Fn AG_BindVariable function creates an Object->Variable reference to the variable called .Fa varKey under an external object .Fa varObj , returning a .Dv P_VARIABLE type Variable on success. Whenever this Variable is accessed, the external object will be locked and a copy of its variable .Fa varKey will be returned implicitely. Note: Circular references must be avoided. .Pp .Fn AG_BindVariable creates an anonymous Object->Object reference to .Fa varObj (which is also removed by .Fn AG_Unset or .Xr AG_ObjectFreeVariables 3 when no more Object->Variable references make use of the object). .Pp .Fn AG_BindObject and .Fn AG_BindVariable may fail and return NULL. .Sh STRUCTURE DATA For the .Ft AG_Variable structure: .Pp .Bl -tag -compact -width "char name[AG_VARIABLE_NAME_MAX] " .It Ft char name[AG_VARIABLE_NAME_MAX] Variable name (or "" = anonymous). .It Ft AG_VariableType type Variable type (see ). .It Ft AG_Mutex *mutex Mutex protecting referenced data. .It Ft union ag_variable_data data Stored data (see ). .El .Sh EXAMPLES The following code tests if "delete-me" is defined and if so, deletes it: .Bd -literal -offset indent .\" SYNTAX(c) AG_Object *obj; if (AG_Defined(obj, "delete-me")) AG_Unset(obj, "delete-me"); .Ed .Pp The following code atomically increments a variable "value", which may be either an .Ft int or a .Ft float : .Bd -literal -offset indent .\" SYNTAX(c) void IncrementValue(AG_Object *obj) { AG_Variable *V; void *pValue; V = AG_AccessVariable(obj, "value", &pValue); switch (AG_VARIABLE_TYPE(V)) { case AG_VARIABLE_INT: (*(int *)pValue)++; break; case AG_VARIABLE_FLOAT: (*(float *)pValue) += 1.0f; break; } AG_UnlockVariable(obj); } .Ed .Pp The following code prints a string representation of a variable "value" to a fixed-size buffer .Fa buf : .Bd -literal -offset indent .\" SYNTAX(c) char buf[32]; AG_Object *obj; AG_Variable *V; if ((V = AG_AccessVariable(obj, "value")) == NULL) { AG_FatalError(NULL); } AG_PrintVariable(buf, sizeof(buf), V); AG_UnlockVariable(V); AG_Verbose("value = %s\\n", buf); .Ed .Pp The following code atomically duplicates the contents of variable "copy-me" from one object .Fa objSrc to another object .Fa objDst : .Bd -literal -offset indent .\" SYNTAX(c) AG_Object *objSrc, *objDst; AG_Variable *Vsrc, *Vdst; Vsrc = AG_AccessVariable(objSrc, "copy-me"); Vdst = AG_AccessVariable(objDst, "copy-me"); if (Vsrc == NULL || Vdst == NULL) AG_FatalError(NULL); AG_CopyVariable(Vdst, Vsrc); if (AG_CompareVariables(Vsrc, Vdst) == 0) AG_Verbose("Copy successful\\n"); AG_UnlockVariable(Vdst); AG_UnlockVariable(Vsrc); .Ed .Pp The following code uses object variable substitution to generate the string "Hello world!" into a fixed-size buffer: .Bd -literal -offset indent .\" SYNTAX(c) char buf[32]; AG_Object *obj; AG_SetString(obj, "the-string", "world"); AG_VariableSubst(obj, "Hello $(the-string)!", buf, sizeof(buf)); AG_Verbose("%s\\n", buf); .Ed .Pp .Nm is used to represent .Xr AG_Object 3 instance variables and arguments passed to .Xr AG_Event 3 callback routines. .Pp In Agar-GUI, widgets use .Nm to reference data in memory (also known as "bindings"). Refer to the "BINDINGS" section of each widget's manual page for details. .Pp In Agar-GUI, the "value" of an .Xr AG_Numerical 3 spinbutton can be tied to an .Ft int , a .Ft float a mutex-protected .Ft Uint32 , etc. .Sh SEE ALSO .Xr AG_Intro 3 , .Xr AG_Event 3 , .Xr AG_Object 3 .Sh HISTORY The .Nm interface first appeared in Agar 1.3.4. It replaced the older "AG_Prop" interface and .Xr AG_Widget 3 specific bindings. In Agar 1.6.0, Object->Object references appeared and .Fn AG_GetVariableLocked was renamed .Fn AG_AccessVariable . Functions appeared in Agar 1.7.0.