Version 12 of persistent itcl

Updated 2002-08-23 01:36:06

The page Persistent incr-Tcl objects got me thinking about persistence.

The code presented there requires too much user intervention, I want something which handles more of the details automatically.


I've just put up a package called itclPersist on my little projects page here: http://sourceforge.net/projects/libtclpq/

itclPersist consists of a Persist class and a PersistServer class (and some test code.)

The idea is that you inherit from Persist, and attach to an instance of PersistServer. After that, you forget about it - all instance variables and arrays are persisted in a metakit, are refreshed at attach time, and just basically ... persist.

Next step for me is to inherit from the PersistServer to make a RemotePersist class, which does all the same things, but does them on a different machine - I'll probably use comm. Next step after that is to use Mux multiplexer to distribute updates to a set of client/listeners.


Here's a function which constructs a script which, when evaluated in global scope, will do what I think I want.

  # serialize extracts all variables and their current values into a script
  # such that `eval [serialize obj]' will set obj to those values
  proc serialize {obj {new ""}} {
      set result ""
      if {$new == ""} {
          set new $obj
      }
      foreach var [$obj info variable] {
          foreach {type name} [$obj info variable $var -type -name] break
          if {([namespace tail $name] != "this") && ($type != "common")} {
              set fromname "@itcl $obj $name"
              set toname "@itcl $new $name"
              if {[catch {
                  append result "eval set [list \[list ${toname}\]] [list [list [set $fromname]]]\n"
              } err]} {
                  puts stderr "err: $err"
              }
          }
      }
      return $result
  }

Here's a small test:

  class test_persist {
      common com "com val"        ;# common vars are unchanged by serialize
      private variable priv "priv default"
      public variable pub "pub default"
      protected variable prot "prot default"
      public variable single single_value_should_not_be_braced

      # make all variables' values uppercase
      method change {} {
          foreach var {priv pub prot} {
              set $var [string toupper [set $var]]
          }
      }

      constructor {} {
          # change values from the default
          set priv {priv value $ [] # \n \{\}}
          set pub {pub value $ [] # \n \{\}}
          set prot {prot value $ [] # \n \{\}}
      }
  }

  class test_p1 {
      inherit test_persist
      public variable sub_pub sub_pub
      private variable sub_priv sub_priv

      method test {} {
          foreach var [$this info variable] {
              foreach {type name val} [$this info variable $var -type -name -value] break
              puts "$name = $val"
          }
      }

      constructor {} {
          # needs to be here
      }
  }

  test_p1 t
  puts "Vars: [t info variable]"
  set lower [serialize t]
  puts "DUMP: '$lower'"
  t change        ;# uppercase t
  puts "Will be uppercase:"
  t test
  eval $lower
  puts "Should be lowercase:"
  t test        ;# check change

  test_p1 t2
  t2 change
  puts "Will be uppercase:"
  t2 test
  set tolower [serialize t \$T]
  puts "DUMP2: '$tolower'"
  set T t2
  eval $tolower
  puts "Should be lowercase:"
  t2 test

CMcC


Persistence and Distribution

This approach raises some interesting possibilities for persistence:

  • Each class instance can be represented by a metakit row
  • Tequila could be adapted to attach instance variables (or whole objects) in addition to simple arrays (Necessary modification to achieve this goal would be moderate.)
  • checkpointing

Note

  • Config code on public variables is not executed when the serialized code is evaluated. Command class documentation says code fragment is executed whenever a public variable is modified by the built-in "configure" method - and this is literally true.