[Richard Suchenwirth] 2006-04-26 - A question on [comp.lang.tcl] brought me to experiment with code that rewrites itself. The idea is that settings are not saved in a separate resource file, but in the script itself, as setting a global variable with the desired value. So the next time the script is run, the previously saved setting applies. Here's what I did: proc save {filename varlist} { set f [open $filename] set data [read $f] close $f set lines {} foreach line [split $data \n] { foreach var $varlist { if [regexp "^ *set $var " $line] { set line [list set $var [set ::$var]] } } lappend lines $line } set f [open $filename w] puts $f [join $lines \n] close $f } #-- Now for a testing demo: package require Tk #-- We will test with this variable: set foo hello label .1 -text foo: entry .2 -textvar foo button .3 -text Save -command [list save [info script] foo] eval pack [winfo children .] -side left #-- Invaluable little helper for rapid restart: bind . {exec wish $argv0 &; exit} ---- [lexfiend] 2006-04-27 - Here's a modification that places all the state variables in a single section, so that [[save]] doesn't inadvertantly overwrite other [[set]]s of the state variables and thereby change the program logic. However, note that this forces you to pre-initialize any namespaces on which the state variables depend. # Create empty namespaces first namespace eval testns {} ### VARIABLE SAVE SECTION set test1 "I am here" set testns::test2 "Here as well" ### VARIABLE SAVE SECTION proc save {filename varlist} { set f [open $filename] set data [read $f] close $f set lines {} set inSaveSection 0 foreach line [split $data \n] { if [regexp "^ *#+ *VARIABLE SAVE SECTION" $line] { if {!$inSaveSection} { # Start of save section lappend lines "### VARIABLE SAVE SECTION" foreach var $varlist { lappend lines [list set $var [set ::$var]] } set inSaveSection 1 } else { # End of save section lappend lines "### VARIABLE SAVE SECTION" set inSaveSection 0 } } elseif {$inSaveSection} { # Somewhere in save section - do nothing } else { lappend lines $line } } set f [open $filename w] puts $f [join $lines \n] close $f } set testns::test2 "Gone forever" set test1 "Not here now" save [info script] {test1 testns::test2} ---- [EMJ] - 2006-04-26 - Aaaaargh! Self-modifying code, the slippery slope to certain unmaintainability! That of course is a remark from the distant past, when clever people wrote very fast but generally incomprehensible self-modifying assembly language. The warning is still valid, however - be careful! For the stated purpose, it's not actually too bad, but I think I would use an alias or something rather than just set, in order to make the regex more discriminating. I guess it's simply the reverse of what [tkbiff] does, writing most of its code to its config file! [IL] - This kind of stuff always makes me think of DNA, the assembly of the organic life. I suppose that makes tRNA the compiler and proteins the executable... heh. [Lars H]: One remark about the DNA analogy is that biologists nowadays think there is more (perhaps far more) to the genes than just the DNA -- some heritable traits are not even encoded into the nucleotides [http://en.wikipedia.org/wiki/Epigenetics]. (Thinking about it as a programmer, it's easy to suspect that a lot is missing from the highschool biology model of DNA spending all its time being copied to mRNA that is used to build proteins; in all but the very simplest processes, the control and feedback parts are as important as the executive parts, but in this model there aren't any!) From that point of view, the above system is about encoding the entire state into the "DNA" of the program. Apparently that isn't how it's normally done in nature, either. ---- [etdxc] - 2006-04-27 - I do something similar. I use a starpack and database (Access in mose cases (ok, everyone groan together!) as a production system. The starpack only contains the libraries, images and the scripts necessary for startup, shutdown, retrieving & storing the data. I retrieve and uplevel the scripts from the database as they are needed. The self modifying ones maintain their data as constants, thus the data becomes the program and vice versa. In three years, I've never had a problem with maintenance because i) of the power of Tcl and ii) I do impact analysis before messing with the code. Of course, all the documentation is in the database seperate but linked to the code. Two of the nicities are i) I never use globals and ii) I can simply store an update as a kit on the web, the program downloads, updates the database and removes it. The updates are able to remove redundant scripts, add functionality, etc. i.e. the original installation script is removed without having any effect on the original program. ---- [Jerry] - 2006-04-27 - I posed the question and choose an intermediate to the above two suggestions. I decided to look for variables that were marked by a "volatile" comment: set maxIdleTime 1200 ;# volatile The processing code ( following Richards example ): proc save {filename varlist} { set f [open $filename] set data [read $f] close $f set lines {} foreach line [split $data \n] { foreach var $varlist { if [regexp "^ *set $var.*;# volatile" $line] { set line [list set $var [set ::$var] ] append line " ;# volatile" } } lappend lines $line } set f [open $filename w] puts $f [join $lines \n] close $f } ---- [Category Example] - [Arts and crafts of Tcl-Tk programming]