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()
.