Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

tcl objc type: args uses only string of own type

22 views
Skip to first unread message

Stefan Kuhr

unread,
Jun 19, 2002, 2:18:42 PM6/19/02
to
Hello!

I have a general problem with an own defined object type which just holds a
pointer to a not copyable structure holding informations about a COM port
connection or other connections.

OK, first I have a function, let's say con, building up the connection and
then returning a Tcl_Obj containing informations about this connection.

This result will normally be given to another function, say setcon, which
uses the connection. Therefore it increases the RefCount of the connection
object given as argument, to prevent tcl from deleting the object.

Then there are other functions, let's say send and receive, which relay on
the connection internally stored by setcon.

And at least there's a command releasecon that releases the conection object
again by decreasing the ref count.

So normally a script looks like

# setcon increases ref count of it's argument
setcon [con 1]
# here tcl will not delete the object
send "Hello world!"
set result [receive]
# even more send and recieve commands
# releasecon decreases the ref count again
releasecon
# here tcl will free the result of [con 1]

This allows to easily change the connection type by simply changing the
[con] command.

So far so good.

But in some cases to get logging information I want to rename setcon into
origin_setcon and define a new procedure setcon:

rename setcon origin_setcon
# setcon might take more than one argument therfore args
proc setcon {args}
#do some logging stuff
set result [eval origin_setcon $args]
# do some other logging stuff using the result
return $result
}

With that defined the following script will fail at send:

setcon [con 1]
send "Hello"

So [con 1] will return my connection type object, which will be converted
into the args list of the new setcon and then [eval origin_setcon $args]
will be called. Somewhere in that calling stack my (Tcl_UpdateStringProc)
function of the Tcl_ObjType struct is called (and (Tcl_DupInternalRepProc)
is never called).

My (Tcl_UpdateStringProc) stores the Tcl_Obj.internalRep.otherValuePtr as
hex number into the string.

Inside origin_setcon I call Tcl_ConvertToType() which leads to a call of my
(Tcl_SetFromAnyProc) where I get the struct pointer back from the hex
string.

So far so good, again. [con 1] works and also setcon and origin_setcon do
work.

But directly after setcon [con 1] tcl thinks, that the result of [con 1] --
my connection object -- is not needed any more (origin_setcon increased the
ref count of another object) and calles my (Tcl_FreeInternalRepProc) where I
of course delete the struct.

Well, then send relays on the connection object internally saved from
origin_setcon, but the object was deleted from tcl just before and so the
script crashes.


Can anybody help me? I see no real solution for this.


Schüss

Stefan


miguel sofer

unread,
Jun 19, 2002, 3:19:37 PM6/19/02
to Stefa...@mch.siemens.de
Stefan Kuhr wrote:

> rename setcon origin_setcon
> # setcon might take more than one argument therfore args
> proc setcon {args}
> #do some logging stuff
> set result [eval origin_setcon $args]
> # do some other logging stuff using the result
> return $result
> }

[eval] will reparse the thing; it is the guilty party in requesting the
string form! Also, if some of your args contain any whitespace that will
consider it as two different args ...

If my understanding is right, replace the line


set result [eval origin_setcon $args]

with
set result [eval [linsert $args 0 origin_setcon]]
and watch everything run fine (assuming a recent tcl). OTOH, if my
understanding is wrong all should stay the same.

List-quoting the arguments to eval prevents the problem with white
spaces or other separators; it is good tcl style and a requisite for
robust code (except of course when you actually need reparsing). Since
tcl8.3.1, [eval] will refrain from reparsing a _pure_ list
(http://mini.net/tcl/3074 )

Hope this helps
Miguel

miguel sofer

unread,
Jun 19, 2002, 3:36:09 PM6/19/02
to Stefa...@mch.siemens.de
miguel sofer wrote:
> Stefan Kuhr wrote:
>
>> rename setcon origin_setcon
>> # setcon might take more than one argument therfore args
>> proc setcon {args}
>> #do some logging stuff
>> set result [eval origin_setcon $args]
>> # do some other logging stuff using the result
>> return $result
>> }
>
>
> [eval] will reparse the thing; it is the guilty party in requesting the
> string form! Also, if some of your args contain any whitespace that will
> consider it as two different args ...

That may have been less-than-crystal-clear, sorry.

What happens is that, after reparsing, the Tcl_Obj sent to
[origin_setcon] have been freshly created - they are *not* the original
elements of the args list received by [setcon].

Miguel

Jeff Hobbs

unread,
Jun 19, 2002, 4:01:14 PM6/19/02
to
Stefan Kuhr wrote:
...

> set result [eval origin_setcon $args]

There are others way to do the magic object management, but in the case
of the above, instead do:
set result [eval [list origin_setcon] $args]

as long as args is always handled as a list, the string for it won't be
requested and you don't have to worry about the shimmering effects.
Otherwise it sounds like there may be a refcount issue more than a
shimmering issue.

--
Jeff Hobbs The Tcl Guy
Senior Developer http://www.ActiveState.com/
Tcl Support and Productivity Solutions
Join us in Sept. for Tcl'2002: http://www.tcl.tk/community/tcl2002/

Ralf Fassel

unread,
Jun 20, 2002, 3:26:58 AM6/20/02
to
* miguel sofer <m...@utdt.edu>

| Also, if some of your args contain any whitespace that will consider
| it as two different args ...

No, it won't. That's the magic of `args'. However, if one uses
`eval' to call the original function, the original function also
should expect `args', not one argument.

I.e.:
This will work:
proc foo {args} {puts [llength $args]}
rename foo foo_orig
proc foo {args} {eval foo_orig $args}

This will not work in the general case:
proc foo {list} {puts [llength $list]}
rename foo foo_orig
proc foo {args} {eval foo_orig $args}
(Feed the new `foo' more than one arg to see the `eval' fail).

R'

Ralf Fassel

unread,
Jun 20, 2002, 3:39:31 AM6/20/02
to
* "Stefan Kuhr" <Stefa...@mch.siemens.de>

| Well, then send relays on the connection object internally saved
| from origin_setcon, but the object was deleted from tcl just before
| and so the script crashes.

If you keep a pointer (at C level) to your object, _you_ should be
increasing the refcount immediately after creating the object. Do not
rely upon another function to do that for you.

TCL might use the object in a `list', and when the list is freed, all
list elements' refcounts are decreased as well. If one of the
refcounts has been 0 before the `list' call, it is increased to 1
during use in the list, then decreased again to 0 and thus the object
is freed.

| # setcon might take more than one argument therfore args
| proc setcon {args}
| #do some logging stuff
| set result [eval origin_setcon $args]
| # do some other logging stuff using the result
| return $result
| }
|
| With that defined the following script will fail at send:
|
| setcon [con 1]

`con 1' creates an object with refcount 0, passes it to `setcon' which
passes it to `eval' which _then_ passes it to `origin_setcon'. If now
`eval' incr/decr the refcount of objects in `args', it much depends on
whether your `orig_setcon' has the chance to incr the refcount before
eval `decr's it again. When it comes to TCL refcounts,
con1 | eval | setcon
{0 -> 1} -> {2 -> 1}
is different from
0 -> {1 -> 0} -> 1

I'm not sure whether this scenario applies here, but my advice would
be to `incr' the refcount the obj which con1 creates immediately.

R'

Stefan Kuhr

unread,
Jun 20, 2002, 6:11:32 AM6/20/02
to
Hello!

Thanks to all!

Jeff's solution works fine, even with two or more connection objects. And
also with uplevel, which I use in real instead of eval (sometimes I do more
simplifications then appropiate ...)

But there's still another problem. Remember my little example:

proc setcon {args}
#do some logging stuff

...

The "do some logging stuff" is the problem. There I want to log also the
arguments given to the function.
That line looks (simplified) like this:

puts $LogFile "setcon $args"

and though args again will be converted. If I don't use this line,
everything is OK, but if I use it, the eval again does not work.

I tried already not working things like this:

set arguments $args
puts $LogFile "setcon $arguments"

A little workaround I can use by now is to put the logging behind the eval,
but that's not the solution I desire (maybe arguments change after the
uplevel).


Schüss

Stefan


Jeffrey Hobbs

unread,
Jun 21, 2002, 10:48:31 AM6/21/02
to
Stefan Kuhr wrote:
...

> Jeff's solution works fine, even with two or more connection objects. And
> also with uplevel, which I use in real instead of eval (sometimes I do more
> simplifications then appropiate ...)
>
> But there's still another problem. Remember my little example:
>
> proc setcon {args}
> #do some logging stuff
> ...
>
> The "do some logging stuff" is the problem. There I want to log also the
> arguments given to the function.

I think what you need to do is change the way that you are doing the
object storage to maintain your own hash of the elements based on
their string rep (like a hex code to the mem pointer) and not have
it actually tied into the object itself. This is what I know others
have done for magic objects (even in the core). This way you can
have multiple objects reference the same piece of memory, and if
you convert it to one of your object types, you just keep a ref to
the hash pointer. This does work (trust me).

Stefan Kuhr

unread,
Jun 21, 2002, 12:10:08 PM6/21/02
to
Hello Jeff!

Sorry, I don't realy understand what you mean. Do you have a little example?

Do you mean not to handle with own object types and instead only to use
strings containing the address of the struct?

But if so, a single

con 1

, giving the result string to nowhere, would not delete my struct.


Schüss

Stefan


Donal K. Fellows

unread,
Jun 25, 2002, 6:44:00 AM6/25/02
to
Stefan Kuhr wrote:
> I have a general problem with an own defined object type which just holds a
> pointer to a not copyable structure holding informations about a COM port
> connection or other connections.

That's your problem. Tcl_Obj structures have lifetimes that are relatively
tricky to control (it is very easy to lose them too early, as you have found out
the hard way) and the easiest way to work around this is to store the reference
to the structure in a hashtable (the printed form of the pointer makes a fine
index). Then, modify [con] to return the string (or a Tcl_StringObj())
containing the index, things that use the connection can always just look it up
using the hashtable (it's possible to cache the result of the hash lookup in the
object returned by [con], but absolutely not worth it given how much work it
takes to do properly), and [releasecon] should explicitly delete the entry from
the hashtable (and also the deletion you've been doing on object destruction.)

The only problem with the technique outlined above is that it can leak
connections. Mind you, you could always add a separate command to list the
created connections (by producing a list of the indices into the hash table,
which is pretty trivial coding) and get a fair old way along the route to having
Tcl-style introspection (great for debugging purposes!) And that would let you
find out if you really are leaking...

Donal.
--
Donal K. Fellows http://www.cs.man.ac.uk/~fellowsd/ fell...@cs.man.ac.uk
"[E]ven now, wars are fought and lost, people are killed and unkilled, toilet
rolls are used and unused, pants are derwear and underwear, all because
of the delicious velvety substance that is Marmite." -- Nathan Weston

0 new messages