From: kelsey@mdd.comm.mot.com (Joe Kelsey)
Subject: Re: Planning for Tcl 7.0
Organization: Motorola, Mobile Data Division - Seattle, WA
Date: Wed, 14 Oct 1992 20:54:20 GMT

In <1bfcnqINN1ef@gap.caltech.edu> jnh@wag.caltech.edu (Jon N. Hurley) writes:

>How about methods of accessing C structures from TCL?

>It seems that there are 2 philosophies:

>  1) Create a command object, like the widgets in Tk.
>  2) Create a handle that is referenced by string lookup
>     in a table, such as the file handles from the open command.

I did something slightly different.  I implemented and interface to System
V-style IPC (semaphores and shared memory so far, message queues still to
come).  I use this to implement a Tcl ``snooper'' process which allows someone
to examine and modify a shared memory area while the application process runs.
The shared memory interface basically implements the shmget and shmat/shmdt
functions to acquire a shared memory segment, then a set of functions to
access different sized pieces of memory (shmgetchar, shmgetstr, shmgetshort,
shmgetlong), converting them to numeric strings for Tcl.  Once you have this,
plus the base address of the structure (offset from the start of the shared
memory segment) you can set up traced variables which ``look'' like the C
structures.  I ``define'' a structure by using an array whose elements name
the offset from the structure base.  A parallel array defines the element
type.  Then, you ``declare'' a structure by defining a new array with the same
element names as the original array and place its base address in a globally
accessible variable.  Then, a trace procedure can read and write the actual
memory locations whenever you access the declared array.

An example might help:

Given the C structure:

struct list {
  long next;		/* self-relative offset to next element */
  long prev;		/* self-relative offset to prev element */
  long data1;
  short data2;
  short data3;
  char data4[10];
};

I then declare the tcl type structure:

set list(next) 0
set list(prev) 4
set list(data1) 8
set list(data2) 12
set list(data3) 14
set list(data4) 16
set sizeof(list) 26

set LONG(next) 1
set LONG(prev) 1
set LONG(data1) 1
set SHORT(data2) 1
set SHORT(data3) 1
set CHAR(data4) 10

The LONG, SHORT, USHORT, CHAR, UCHAR arrays encode the element type and size
(for array elements).  In my ``real'' procedures, I compute the offset of each
successive structure element as the offset of the previous plus its size:

set list(prev) [expr $list(next)+4]

When I declare a list item, I use a procedure called structinit:

proc structinit {type name base} {

  global CHAR LONG SHMSEG SHORT UCHAR USHORT struct
  upvar #0 $type stype
  upvar $name nm 
  set SHMSEG($name) $base
  foreach offset [array names stype] {
    if !$base {set nm($offset) 0} {
      set addr [expr $base+$stype($offset)]
      case $offset in \
	[array names CHAR] \
	  {set nm($offset) [shmgetstrn $addr $CHAR($offset)]} \
	[array names UCHAR] \
	  {set nm($offset) [andvec 0xff [shmgetchar $addr $U8($offset)]]} \
	[array names SHORT] \
	  {if $addr&1 {
	     error "short ${type}($offset) on odd boundary $addr"
	   }
	   set nm($offset) [shmgetshort $addr $SHORT($offset)]
	  } \
	[array names USHORT] \
	  {if $addr&1 {
	     error "unsigned short ${type}($offset) on odd boundary $addr"
	   }
	   set nm($offset) [andvec 0xffff [shmgetshort $addr $U16($offset)]]
	  } \
	[array names LONG] \
	  {if $addr&3 {
	     error "long ${type}($offset) on odd boundary $addr"
	   }
	   set nm($offset) [shmgetlong $addr $LONG($offset)]
	  }
    }
  }
  set struct($name) $type
}

For example, to declare a_list as a list at a specific base address:

structinit list a_list [expr $shmbase+$a_list_offset]

I also have a structrace procedure which looks up the structure type in the
struct array, uses that type array to discover the element offset and looks up
the element in the various type arrays to determine which of the shmget
procedures to call and whether or not to and the result with 0xff or 0xfff to
produce unsigned chars or unsigned shorts.

Using the shmget/shmset procedures to convert between Tcl strings and binary
locations consumes a certain processor overhead, but why else do I have a
SPARC on my desk?

I don't know the Motorola policy about releasing the actual Tcl extensions.
I've read some internal news group discussions which seem to indicate that I
might not get to release it.  As soon as I get some spare time, I plan to look
into it, especially if anyone else displays some interest.

/Joe


Newsgroups: comp.lang.tcl