An Introduction To Embedded Tk (page 13 of 32)

[Previous Page][Next Page][Table of Contents]

7.1 Moving Information From Tcl/Tk To C

The first thing to note about ET() is that, just like a real C function, it has a return value. ET() returns an integer status code which is either ET_OK or ET_ERROR depending on whether the enclosed Tcl/Tk was successful or failed. (ET() might also return TCL_RETURN, TCL_BREAK, or TCL_CONTINUE under rare circumstances.)

The status return of ET() is nice, but in practice it turns out to be mostly useless. What you really need is the string value returned by the enclosed Tcl/Tk script. That's the purpose of the ET_STR() function.

The ET_STR() function works a lot like ET(). You put in a Tcl/Tk script as the argument, and the script gets executed. But instead of returning a status code, ET_STR() returns a pointer to a string that was the result of the last Tcl/Tk command in its argument.

The ET_STR() function turns out to be a very handy mechanism for querying values from Tcl/Tk. For instance, suppose your program has an entry widget named ``.entry'' and some piece of C code needs to know the current contents of the entry. You can write this:

  char *entryText = ET_STR(.entry get);
Or imagine that you need to know the current size and position of your main window. You might use code like this:
  int width, height, x, y;
  sscanf(ET_STR(wm geometry .),"%dx%d+%d+%d",&width,&height,&x,&y);
Does your C routine need to know the value of a Tcl variable? You could use the cumbersome Tcl_GetVar() function, but it's much easier to say:
  char *zCustomerName = ET_STR(set CustomerName);
Possible uses for ET_STR() seem limitless.

But, there are two subtleties with ET_STR() that programmers should always keep in mind. The first is that the Tcl/Tk script in the argument is executed at Tcl's global variable context level. This means that all of the Tcl/Tk variables ET_STR() creates, and the only Tcl/Tk variables it can access, are global variables. This limitation also applies to the regular ET() function, and to two other function we haven't talked about yet: ET_INT() and ET_DBL(). ET provides no means for C code to access or modify local variables. On the other hand, this has not proven to be a serious hardship in practice.

The second subtlety with ET_STR() is more dangerous, but fortunately applies to ET_STR() only. Recall that ET_STR() returns a pointer to a string, not the string itself. The string actually resides in memory that is held deep within the bowels of Tcl/Tk. The danger is that the next Tcl/Tk command may choose to change, deallocate, or reuse this memory, corrupting the value returned by ET_STR(). We say that the return value of ET_STR() is ``ephemeral.''

One way to overcome the ephemerality of ET_STR() is by making a copy of the returned string. The strdup() function is good for this. (Unfortunately, strdup() is missing from a lot of C libraries. You may have to write your own string duplicator.) In place of the examples given above, you might write

  char *entryText = strdup( ET_STR(.entry get) );
or
  char *zCustomerName = strdup( ET_STR(set CustomerName) );
The strdup() function uses malloc() to get the memory it needs, so if you use this approach, be sure to free() the value when you are done to avoid a memory leak!

The other way to overcome the ephemerality of ET_STR() is simply not to use the returned string for very long. You should be safe in using the returned string as long as you don't invoke any other Tcl/Tk commands, or return to the event loop. Code like this

  sscanf(ET_STR(wm geometry .),"%dx%d+%d+%d",&width,&height,&x,&y);
is OK since we need the return value only for the duration of the sscanf() function and sscanf() doesn't use Tcl/Tk.

In addition to ET() and ET_STR(), the ET system provides two other functions named ET_INT() and ET_DBL(). Both take a Tcl/Tk script for their argument, as you would expect. But ET_INT() returns an integer result and ET_DBL() returns a floating-point value (a double). In a sense, these two functions are extensions of ET_STR(). In fact, ET_INT() does essentially the same thing as

   int v = strtol( ET_STR(...), 0, 0);
and ET_DBL() is equivalent to
   double r = strtod( ET_STR(...), 0);
Because ET_INT() and ET_DBL() return a value, not a pointer, their results are not ephemeral nor subject to the problems that can come up with ET_STR().

[Next Page][Table of Contents]