When you wrap up a load of C/C++ code and use it as a Tcl shared object extension, you often find yourself coding large, ungainly *TclC* procedures which make much use of pointers. I found it useful to write a defaultDestructor procedure to do a final cleanup of pointers created in procedure context:
proc defaultDestructor { { level 1 } } { set msg "defaultDestructor:" set vnames [ uplevel $level info vars ] foreach vname $vnames { ;## dereference set vname [ uplevel $level set $vname ] if { [ llength $vname ] > 1 } { continue } switch -regexp -- $vname { {^_[0-9a-f]+_Frame_p$} { set seqpt "destructFrame($vname):" if { [ catch { uplevel $level destructFrame $vname } err ] } { lappend msg "$seqpt $err" } } {^_[0-9a-f]+_Element_p$} { set seqpt "destructElement($vname):" if { [ catch { uplevel $level destructElement $vname } err ] } { lappend msg "$seqpt $err" } ;## . ;## . more of the same elided... ;## . {^_[0-9a-f]+_FrameFile_p$} { set seqpt "closeFrameFile($vname):" if { [ catch { uplevel $level closeFrameFile $vname } err ] } { lappend msg "$seqpt $err" } ;## . ;## . even more elided! ;## . } ;## end of switch } ;## end of foreach if { [ llength $msg ] > 1 } { return -code error $msg } }
If it is not too much to ask, could you please supply a bit more context on what the purpose of this code fragment is? Sure, I can see what it is doing per se, but I have no feeling for why it should be doing this. I happen to believe the why of things is extremely important, since it is what determines the what of things... DKF
Of course! When you wrap a C++ API for use as a shared object by Tcl, you might expose a number of methods which create and manipulate pointers to various types of data objects. You then write a set of wrapper procs in Tcl that make use of this low_level API. Sometimes you just want to clean up everything that one of your wrapper procs has created in the global pointer namespace, and that is what this code fragment is designed to do. This code is called from within a procedure which has generated a lot of pointers, and it cleans them all up with the appropriate call to their destructor.
I thought the code was interesting because it shows the use of uplevel for getting inside of the caller, and it also demonstrates a method of error handling which does not short circuit the cleanup effort. The result is much more readable working code. PSE