TkGS Specification - Internals: TkGS_Objs
Most public structures defined by TkGS, such as
TkGS_Color,
are in the same time device-independent from a user's point of view, and very
platform- and device-dependent from the implementor's point of view. For example,
a TkGS_Color specified by its RGB values could
hold any device-independent value internally, be it an XColor or a Windows
COLORREF or Pen Object. As the same object may be used on distinct devices, its
internal representation may change as well, however its external representation
remains the same, thus guaranteeing device-independence.
TkGS must then provide a structure that guarantees device-independence while
providing the best overall performance. To do so, it introduces the
TkGS_Obj structure. This structure is
very much inspired from Tcl's Tcl_Obj, in the sense that it
holds a device-independent visible part, and a device-dependent internal part
that is subject to change during the object's lifetime. Both share many
features, such as:
- Reference counting, allowing efficient memory management with
copy-on-write semantics;
- Internal representation shimmering without altering the external part.
- A unified way of defining new object types.
The main differences are:
- Tcl_Objs' external representations are strings.
TkGS_Objs' external representations can be anything such
as colors or drawing attributes;
- Tcl_Objs can hold at most one internal representation.
TkGS_Objs may hold several internal representations at once,
thus giving better performances when objects are used simultaneously on several
devices (for example, the same font being used on display and printer contexts);
- Both Tcl_Objs' external and internal representations may
change: an object can be turned into a list from its string representation, and
the internal list structure can be modified later on, thus invalidating the
old string representation. Only TkGS_Objs' external part may
change. For example, a color can be defined in a device-independent manner by
its RGB values. Its internal part may shimmer between many device-dependent
structures, but the RGB values will never be recomputed from its internal part.
The latter can be seen as a limitation, but in practice generating an object's
external representation from its internals may give unexpected results: a color's
internal values may have been dithered to match the display's color model. The only
possible interaction with objects is through device-independent calls, changing
a color's RGB values for example.
Like with Tcl_Objs, the internal representation of
TkGS_Objs may "shimmer" if subsequently used on different
devices. However such shimmering should seldom occur given the way objects are
used. An example is canvas printing: canvas items are most of the time drawn on
a window, and once in a while output to a PostScript file. The colors' internal
values may then change from screen device representation (eg. XColor) to
PostScript color, and back to screen once printing is complete. Objects holding
more than one internal representation are even less likely to shimmer.
This model provides the best compromise between performance and memory
footprint, and device drivers can fine-tune their implementation depending on
system peculiarities. For example, Object allocation on Windows are notably
slow, but such objects can be used on any device, so caching them provides
increased performances.
- TkGS_InternalRep
- Description
- This structure is used to keep track of objects' internal representations. Objects can cache one or several internal reps simultaneously, depending on their base type.
- This structure is very similar to that used by Tcl_Objs to store their internal representation.
- Status
- Complete.
- Structure
-
typedef struct TkGS_InternalRep {
TkGS_ObjType *typePtr; /* Denotes the object's type. Always
* corresponds to the type of the object's
* internal rep. NULL indicates the object
* has no internal rep (has no type). */
union {
long longValue; /* a long integer value */
unsigned long ulongValue; /* an unsigned long integer value */
double doubleValue; /* a double-precision floating value */
VOID *otherValuePtr; /* another, type-specific value */
struct { /* internal rep as two values */
union {
long longValue;
unsigned long ulongValue;
VOID* otherValuePtr;
} value1, value2;
} twoValue;
} value;
} TkGS_InternalRep;
- TkGS_Obj
- Description
- This structure is very similar to Tcl_Obj.
It is not supposed to be used as is but rather as a base class for
more specific objects (colors, drawables...). Thus it only defines
room for type and internal representation, and no external
representation.
- Depending on their base type, objects may hold one or several
internal representations.
- Status
- Complete.
- Structure
-
typedef struct TkGS_Obj {
int refCount; /* When 0 the object will be freed. */
TkGS_BaseType *baseTypePtr; /* Base type, eg. GC, Drawable... */
union { /* The internal representation: */
TkGS_InternalRep single; /* - single internal rep. */
TkGS_InternalRep *multiple; /* - multiple internal reps: array of
* baseTypePtr->nbIntRep elements. */
} internalRep;
} TkGS_Obj;
- TkGS_ObjType
- Description
- This structure is very similar to Tcl_ObjType.
The main differences are the lack of a duplication procedure and the
baseTypePtr field.
- The latter actually describe the
real device-independent type of objects, since TkGS_Obj
is only a "virtual class" (using OOP terminology). For example, an
object can be a color, and its internal representation can be a
device-specific color structure. The object's base type won't change,
but the internal type may.
- The lack of duplication procedure is due to the fact that only the
public part can be safely duplicated.
- Status
- Complete.
- Structure
-
typedef void (TkGS_FreeInternalRepProc) _ANSI_ARGS_((struct TkGS_InternalRep *intRepPtr));
typedef int (TkGS_SetFromAnyProc) _ANSI_ARGS_((Tcl_Interp *interp,
struct TkGS_Obj *objPtr,
struct TkGS_InternalRep *intRepPtr));
typedef struct TkGS_ObjType {
char *name; /* Name of the type, e.g. "XlibGC". */
TkGS_BaseType *baseTypePtr; /* Base type, eg. GC, Drawable... */
TkGS_FreeInternalRepProc *freeIntRepProc;
/* Called to free any storage for the type's
* internal rep. NULL if the internal rep
* does not need freeing. */
TkGS_SetFromAnyProc *setFromAnyProc;
/* Called to convert the object's internal
* rep to this type. Frees the internal rep
* of the old type. Returns TKGS_ERROR on
* failure, and TKGS_OK on success. NULL if
* the object is immutable */
} TkGS_ObjType;
- TkGS_BaseType
- Description
- This structure has no Tcl equivalent. This is due to the fact that all
Tcl objects have string representations (the "everything is a string" paradigm).
Thus this structure is used to describe the objects' public representation.
- The size field is used to specify the byte size of the object
of this type. Since TkGS_Obj is only a "virtual class", the
real size of objects depends on their external representation.
- The nbIntReps field is used to specify the number
of cached internal reps. If it equals to one, then the internal rep will be stored
in the single field of the TkGS_Obj structure. Else internal reps
will be stored as elements of the multiple array field.
- Status
- Complete.
- Structure
-
typedef void (TkGS_FreeBaseProc) _ANSI_ARGS_((struct TkGS_Obj *objPtr));
typedef void (TkGS_DupBaseProc) _ANSI_ARGS_((struct TkGS_Obj *srcPtr,
struct TkGS_Obj *dupPtr));
typedef struct TkGS_BaseType {
char *name; /* Name of the base type, eg. "GC" */
int size; /* Byte size of TkGS_Objs of this type */
int nbIntReps; /* Number of cached internal reps */
TkGS_FreeBaseProc *freeBaseProc;
/* Called to free any storage for the type's
* base rep. NULL if the base rep does not
* need freeing. */
TkGS_DupBaseProc *dupBaseProc;
/* Called to create a new object as a copy
* of an existing object. It only copies the
* public part of the object, not the
* internal part */
} TkGS_BaseType;
- TkGS_IncrRefCount
- TkGS_DecrRefCount
- Description
- These procedures are the exact counterparts of the corresponding
Tcl_Obj related procedures.
- TkGS_IncrRefCount increments an object's reference
count.
- TkGS_DecrRefCount decrements an object's reference
count, and if it reaches zero, frees the object.
- Status
- Specification complete. They are currently implemented as macros.
Like in Tcl, we may add debugging-specific implementations.
- Declaration
-
void TkGS_IncrRefCount(
TkGS_Obj *objPtr
);
void TkGS_DecrRefCount(
TkGS_Obj *objPtr
);
- Arguments
objPtr
: a pointer to the object.
- Returned value
- None.
- Side effects
- The object's reference count is incremented or decremented. If it falls below 1,
the object is freed.
- TkGS_IsShared
- Description
- This procedure is the exact counterpart of the corresponding
Tcl_Obj related procedure. It checks whether the object is
shared, ie if its refcount is at least 2. It is currently implemented as
a macro.
- Status
- Complete.
- Declaration
-
int TkGS_IsShared(
TkGS_Obj *objPtr
);
- Arguments
objPtr
: a pointer to the object.
- Returned value
- 1 (true) if the object's reference count is at least 2,
else 0 (false).
- Side effects
- Memory is allocated.
- TkGS_DuplicateObj
- Description
- This procedure is the exact counterpart of the corresponding
Tcl_Obj related procedure. It creates a new object
that has the exact type and external representation as the given
object. It doesn't copy the internal part(s).
- Status
- Complete.
- Declaration
-
int TkGS_DuplicateObj(
TkGS_Obj *objPtr
);
- Arguments
objPtr
: a pointer to the object to duplicate.
- Returned value
- A newly allocated object that is the duplicate of objPtr.
- Side effects
- Memory is allocated.
- TkGS_AddNewInternalRep
- Description
- This procedure is the counterpart of the Tcl_Obj-specific
Tcl_ConvertToType. The difference is that it pushes a new internal representation in
the object's cache, whereas the latter changes the (only) type of the
internal representation. The new type must have the same base type as
the object (one can't add a color representation to a drawable for
example).
- Status
- Complete.
- Declaration
-
TkGS_InternalRep * TkGS_AddNewInternalRep(
Tcl_Interp *interp,
TkGS_Obj *objPtr,
TkGS_ObjType *typePtr
);
- Arguments
interp
: Tcl interpreter used for error reporting.
objPtr
: a pointer to the object.
typePtr
: the type of the new internal rep to push to
the object's cache.
- Returned value
- A pointer to the newly created internal rep if the conversion
succeeds.
- NULL if the conversion fails: if typePtr's and
objPtr's base types differ, or if typePtr's conversion
procedure fails. In the latter case, the interp's result is set to
an error message.
- Side effects
- The object's internal representation may change.
- TkGS_PushInternalRep
- Description
- This procedure pushes the specified internal representation in the
object's cache stack.
- Status
- Complete.
- Declaration
-
TkGS_InternalRep * TkGS_PushInternalRep(
TkGS_Obj *objPtr,
TkGS_InternalRep *intRepPtr
);
- Arguments
objPtr
: a pointer to the object.
intRepPtr
: a pointer to the internal rep to push to the
object's cache. It is added at the top of the cache stack, and the last
element of the cache is removed.
- Returned value
- A pointer to the newly created internal rep.
- Side effects
- The object's internal representation may change.
- TkGS_FindInternalRep
- Description
- This procedure tries to find in the object cache an internal rep
whose type is the one specified.
- Status
- Complete.
- Declaration
-
TkGS_InternalRep * TkGS_FindInternalRep(
TkGS_Obj *objPtr,
TkGS_ObjType *typePtr
);
- Arguments
objPtr
: a pointer to the object.
typePtr
: a pointer to the type of the internal rep to
look for.
- Returned value
- A pointer to the internal rep if found, else NULL.
- Side effects
- None.
- TkGSNewObj
- Description
- This procedure is used to allocate a new object of a given base
type. It is not supposed to be called from outside TkGS.
- Newly allocated objects' type is the default type defined by the
base type's emptyTypePtr field. Thus all objects
are guaranteed to have a proper type field set. This differs from
Tcl_Objs which have their internal type set to NULL
by default.
- Status
- Internal use only. Unlikely to change.
- Declaration
-
TkGS_Obj *TkGSNewObj(
TkGS_BaseType *baseTypePtr
);
- Arguments
baseTypePtr
: the object's base type descriptor.
- Returned value
- A pointer to the newly allocated object, or NULL in case of failure.
- Side effects
- Memory is allocated.
- TkGSFreeObj
- Description
- This procedure is used to free an existing object. It is not
supposed to be called from outside TkGS. Users should only call
the public TkGS_IncrRefCount and
TkGS_DecrRefCount for memory managemenet.
- Status
- Internal use only. Unlikely to change.
- Declaration
-
void TkGSFreeObj(
TkGS_Obj *objPtr
);
- Arguments
objPtr
: a pointer to the object.
- Returned value
- None.
- Side effects
- Memory is freed (internal and external representations).