Version 7 of static data in command procedures

Updated 2004-10-04 18:49:47

Tcl offers several techniques that can be used to implement static data in command procedures. This page is intended to present the pros and cons.


Example:

Here is a command procedure for a simple Tcl command that makes use of a "static" value. Roughly, it's a constant value that each invocation of the command will use without changing, but a value that need not be created at all if the command is never evaluated.

  int
  OneObjCommand(ClientData cd, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
  {
    Tcl_SetObjResult(interp, Tcl_NewIntObj(1));
    return TCL_OK;
  }

  Tcl_CreateObjCommand(interp, "one", OneObjCommand, NULL, NULL);

Note that each time [one] is evaluated, a new Tcl_Obj is created to hold the value 1. Since Tcl is capable of managing shared Tcl_Objs, other alternatives are possible that would set the result to be an additional reference to one shared Tcl_Obj rather than a new one each time. The alternatives differ mostly on where that shared Tcl_Obj is stored between calls.

Why might we want an alternative? Most compelling reason is memory efficiency. Consider the script:

  for {set i 0} {$i < 1000000} {incr i} {
    set a($i) [one]
  }

With the implementation of [one] above, 1 million Tcl_Obj structs have to be allocated.


Alternative 1: ClientData

  int
  OneObjCommand(ClientData cd, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
  {
    Tcl_SetObjResult(interp, (Tcl_Obj *)cd);
    return TCL_OK;
  }
  void
  OneDelete(ClientData cd)
  {
    Tcl_Obj *objPtr = (Tcl_Obj *)cd;
    Tcl_DecrRefCount(objPtr);
  }

  objPtr = Tcl_NewIntObj(1);
  Tcl_IncrObjCount(objPtr);
  Tcl_CreateObjCommand(interp, "one", OneObjCommand,
    (ClientData)objPtr, OneDelete);

DGP