Pass a method of a specific object as an input arg

2019-07-27 01:51发布

问题:

I am writing an EDA utility, relying on a TCL 8.6 compliant API. My challenge is as follows:
My utility runs on transistors' models in the database, and does some analyisis, using an EDA vendors' TCL API command. I can pass to the TCL commands a TCL procedure name/pointer, and the analysis will rely on my code, rather than the EDA vendors' code. The in-house written proc accepts as an argument a pointer to the specific transistor's instance in the EDA vendor's database. Now, the EDA vendor allows for TCL 8.6, which means that I want to pass rather than a global proc name, or a namespace proc name, the name/pointer of the specific object's name. How do I do that? In code example:

oo:class create foo {
   constructor {} {
      variable numy 2
   }
   method fooshta { mos_pointer } {
      puts "now in mosy [get name $mos_pointer ]"
   }
   destructor {}
}
foo create bar
analyse_tx -proc < how do I refer to bar's method fooshta?>

In a non OOP context, the code would look like:

proc fooshta { mos_pointer } {
      puts "now in mosy [get name $mos_pointer ]"
   }
 analyse_tx -proc fooshta


As can be seen, I am looking for the answer for < how do I refer to bar's method fooshta, so that the EDA tool will invoke it for each transistors' instance? and pass the parameter?>
Thanks.

回答1:

You can't, not directly, at least not if it is going to be invoked like this:

$procname $thing_to_give_to_your_code

If instead it is invoked like this:

{*}$procname $thing_to_give_to_your_code

Then you can do it by passing in a command prefix.

analyse_tx -proc [list bar fooshta]

This is the one I'd recommend. It might also work if the invoke is done like this:

eval $procname [list $thing_to_give_to_your_code]

This sort of thing is great since it also lets you pass in things like lambda terms bound to apply and so on. It's a very flexible system (since it actually works as a general function currying mechanism) and it's pretty simple.

However, if you're stuck with this style of invoke:

$procname $thing_to_give_to_your_code

then we have to use an indirect mechanism: an intra-interpreter alias will let us make a command (so yes, it will have a name) that delegates to the method:

# The {} are to indicate that this command is aliasing from and to the current interpreter context
interp alias {} delegate-bar-fooshta {} bar fooshta

Then we can just pass delegate-bar-fooshta in as the command name. If you're doing this a lot, you probably ought to put the delegates inside the object's namespace context; it's probably easiest to make a method for setting things up:

oo::class create foo {
   constructor {} {
      variable numy 2
   }
   method fooshta { mos_pointer } {
      puts "now in mosy [get name $mos_pointer ]"
   }
   destructor {}

   method delegate {method args} {
      # We'll also bind in any extra arguments you choose to use
      interp alias {} [self namespace]::delegate-$method \
              {} [self] $method {*}$args
      return [namespace which delegate-$method]
   }
}

foo create bar
analyse_tx -proc [bar delegate fooshta]

By doing it like this, killing off the object using the usual mechanisms will also remove the delegate commands that it owns. This is highly convenient in a complex program, as it offloads more of the housekeeping chores to Tcl itself from your script.