The documentation probably explains it very well but I do not see the difference between this 2 commands in my case :
method dir {} {
puts "method dir..."
}
method pseudomethod {} {
set vardir [my dir]
set vardir [[self] dir]
}
The only difference I can see is that with [self] I can pass it as an argument in a procedure and not with my.
What is the best solution in my case ?
Both solutions have equal performance ?
The self command (with no extra arguments) is equivalent to self object which returns the current public name of the object that is executing the method (you can rename the object). The self command overall provides access to bits of “runtime” state.
The my command is actually the object's internal name; it's created in each object's instance namespace. You can invoke all exported and non-exported methods via my, unlike with the public name. This makes it useful for both calling your internal methods directly, and also for setting up things like callbacks to internal methods (you'll need something like namespace which or namespace code when setting up the callback).
Unlike with the public name, you can delete the internal name command without automatically destroying the object. It'll likely break code (your methods most probably) if you do that, but the base system allows you to do it.
Aside: Tcl 8.7 includes this helper procedure (which also works in 8.6) for creating callback scripts within methods (the funny name means it gets mapped into your methods automatically as callback):
proc ::oo::Helpers::callback {method args} {
list [uplevel 1 {::namespace which my}] $method {*}$args
}
In this case, if the callback was exported, you'd be able to do this instead:
proc ::oo::Helpers::callback {method args} {
list [uplevel 1 self] $method {*}$args
}
but that would be more vulnerable to rename problems. (In all cases, the uplevel 1 is because we want to run a little bit of name-resolving code in the calling context, not inside the scope of the procedure itself.)
I'm not sure how they are implemented, but one reason you'd want to use my is to access non-exported (private) methods. A demo:
oo::class create Foo {
method PrivateMethod {} {puts "this is PrivateMethod"}
method publicMethod {} {puts "this is publicMethod"}
method test {} {
my publicMethod
my PrivateMethod
[self] publicMethod
[self] PrivateMethod
}
}
then:
% Foo create foo
::foo
% foo test
this is publicMethod
this is PrivateMethod
this is publicMethod
unknown method "PrivateMethod": must be destroy, publicMethod or test
my is the mechanism for an object to invoke its methods.
self is the mechanism for introspection on how the current method was called.
Spend some time with the my and self man pages.
Related
I'm writing a PowerShell script that runs a couple of background jobs. Some of these background jobs will use the same set of constants or utility functions, like so:
$FirstConstant = "Not changing"
$SecondConstant = "Also not changing"
function Do-TheThing($thing)
{
# Stuff
}
$FirstJob = Start-Job -ScriptBlock {
Do-TheThing $using:FirstConstant
}
$SecondJob = Start-Job -ScriptBlock {
Do-TheThing $using:FirstConstant
Do-TheThing $using:SecondConstant
}
If I wanted to share variables (or, in this case, constants) in child scopes, I'd prefix the variable references with $using:. I can't do that with functions, though; running this code as-is returns an error:
The term 'Do-TheThing' is not recognized as the name of a cmdlet, function, script file, or operable program.
My question is this: How can my background jobs use a small utility function that I've defined in a higher scope?
If the function in the higher scope is in the same (non-)module scope in the same session, your code implicitly sees it, due to PowerShell's dynamic scoping.
However, background jobs run in a separate process (child process), so anything from the caller's scope must be passed explicitly to this separate session.
This is trivial for variable values, with the $using: scope, but less obvious for functions, but it can be made to work with a bit of duplication, by passing a function's body via namespace variable notation:
# The function to call from the background job.
Function Do-TheThing { param($thing) "thing is: $thing" }
$firstConstant = 'Not changing'
Start-Job {
# Define function Do-TheThing here in the background job, using
# the caller's function *body*.
${function:Do-TheThing} = ${using:function:Do-TheThing}
# Now call it, with a variable value from the caller's scope
Do-TheThing $using:firstConstant
} | Receive-Job -Wait -AutoRemoveJob
The above outputs 'thing is: Not changing', as expected.
I need to modify a parameter named test inside process_data and switch cases outside that function depending on test value.
I couldn't pass it by reference using upvar because the process_data represent a static function for processing received udp packet, and it won't accept more than two parameters 'size and data'.
Also, as far as I found, there is no returned value for the process_data function.
Code:
set test "0"
Agent/UDP instproc process_data {size data} {
//some stuff
if (...)
set test "1"
}
// switch cases depending on test value.
You don't need an extra argument to use upvar if you know the name of the variable you're going to alias. You should be able to do either of these (do not use both):
global test
upvar #0 test test
It's not really classic modular programming, but it will work.
A good place to put the command is at the beginning of the procedure body, like so:
Agent/UDP instproc process_data {size data} {
global test
# some stuff
}
Same thing if you use upvar #0 test test (those two commands are basically equivalent).
Documentation:
global,
upvar
With this Tcl script: A.tcl
itcl::class ::A {
variable list1 {}
public method getList {} {
return $list1
}
}
I do this:
Start the tcl shell and interactively do source A.tcl
then make changes to getList method in A.tcl
To make the changes effective, I do re-source the file A.tcl
When I re-source, I get the following error
% source /home/manid/scripts/test.tcl
class "A" already exists
How can i overcome this error? Is there a way to get the latest changes in the class definition without exiting the shell?
You need to write your code somewhat differently. In particular, you have to put the definitions of the body of the methods (which can be repeated) outside the declaration of the class (which can't be repeated). Then, you do a conditional class creation (with itcl::is class as the tester) and use itcl::body to actually provide the method definitions.
According to these principles, rewriting your A.tcl to be:
if {![itcl::is class ::A]} {
itcl::class ::A {
variable list1 {}
# *Dummy* method body; method must exist, but may be empty
public method getList {} {}
}
}
itcl::body ::A::getList {} {
return $list1
}
would allow you to source that multiple times to change the method bodies however you wanted. This doesn't give you freedom to change everything (e.g., the variable declarations and scope rules are fixed); you need to switch to something like TclOO to get that sort of flexibility.
How can I distinguish a basic type (like string) from an ::oo::object in TclOO?
You can determine precisely if a value is the handle for an object by using info object isa object:
if {[info object isa object $thing]} {
puts "Hey, $thing is an object!"
}
Generally speaking though, Tcl's type system has all values being at least nominally strings. More strictly, every value is serializable to a string. Some values have other natures as well (e.g., numbers know about their numeric-ness as well). TclOO object handles are strings, and command names (and hence can be renamed), and (of course) object handles.
Stupid answer:
# Since a normal string is unlikly to be "::oo::object", this will return 1
# if the argument is not ::oo::object
proc is_oo_object args {
string equals $arg ::oo::object
}
# gettype - higly accurate
proc gettype arg {
# EIAS
return "string"
}
Simple answer: you can't. If someone passes you the name of an object, it's a string.
(See the Tcl/Tk wiki for EIAS)
You can try to guess if it is a ::oo::object if you check if there exists a command with that name:
if {[llength [namespace which $arg]]} {
....
}
This still don't mean that this is an ::oo::object.
You could try to inspect it with expr {[catch {info object class $arg ::oo::object} res] && $res}, but who tells you that someone wanted to pass oo::class as string?
I'm experimenting a little with TclOO from Tcl8.6 and Rivet, but I'm in trouble, because I'm not able to do what I want.
The problem is simply reproduceable with the following code inside a .rvt file:
<?
proc dumbproc {} {
puts "this is dumbproc ([namespace current])"
}
oo::class create foo {
method bar {} {
puts "this is bar ([namespace current])"
dumbproc
}
}
set obj [foo new]
dumbproc
$obj bar
If I simply look at the code, it seems it should work as expected, but it really doesn't because a subtle behavior of the Rivet package and the specific configuration choosen.
In this example, I'm using a .rvt file whose code is executed inside the ::request namespace, so the fully qualified name of the dumbproc procedure is ::request::dumbproc. When the name resolution algorithm is called inside the bar method, it searches for dumbproc inside ::oo::Obj12, then in ::oo, and finally in ::, without finding it and giving the following error.
this is dumbproc (::request) this is bar (::oo::Obj16)
invalid command name "dumbproc"
while executing
"dumbproc"
(class "::request::foo" method "bar" line 3)
invoked from within
"$obj bar"
(in namespace eval "::request" script line 21)
invoked from within
"namespace eval request {
puts -nonewline ""
proc dumbproc {} {
puts "this is dumbproc ([namespace current])"
}
oo::class create..."
So, Tcl is right in doing what it does, a feature, then. But the behavior is unpredictable because when you write some class code, you must know the context it will be used in.
Indeed, I get the same error if I drop the starting <? Rivet magic, put the code inside a test.tcl file and use it in an interactive session:
$ tclsh
% namespace eval ::myns {source test.tcl}
this is dumbproc (::myns)
this is bar (::oo::Obj12)
invalid command name "dumbproc"
I tried to solve the issue by prepending the current namespace to the class creation code
::oo::class create [namespace current]::foo { ... }
then, I also tried to create the obj object inside the namespace
::oo::class create [namespace current]::foo { ... }
namespace eval [namespace current] {set obj [[namespace current]::foo new]}
then, I switched to the create method of the class for giving the object a qualified name which includes the namespace
foo create [namespace current]::obj
obj bar
but everything was unsuccessful. Every trial shows that, no matter how I do it, a method inside a TclOO class is always executed inside its object unique namespace. Am I wrong?
Is there a way to get what I want? Is TclOO not intended to work that way, and in this case why? What really surprise me is this context-dependend behavior, which I'm not sure it's the right thing, but maybe I'm completely wrong and there are sound cases for it, which I'm missing.
The interior of each TclOO object is in reality its own namespace. You can use self namespace or namespace current inside your methods to get the name of the namespace, or info object namespace $theobj to get the namespace from anywhere. The only command placed by default in the namespace is my (for calling private methods), and some commands in other namespaces are made available through the standard Tcl namespace path mechanism (this is how you get self and next available).
The simplest way to fix this would probably be to add this to the foo class's constructor:
namespace path [list {*}[namespace path] ::request]
In your specific case, you'd have to actually add a constructor...
constructor {} {
namespace path [list {*}[namespace path] ::request]
# If you had a non-trivial constructor in a superclass, you'd need to call
# [next] too.
}
Longer term, it may be reasonable to ask for a mechanism for adding to the list of namespaces that are used to make the default for objects of a class. If you want that, do submit a feature request…
[EDIT]: If you're just after adding the parent namespace to the current object's command resolution path, you can do that by adding a bit more magic:
oo::class create foo {
self {
method create args {
set ns [uplevel 1 {namespace current}]
next {*}[linsert $args 1 $ns]
}
method new args {
set ns [uplevel 1 {namespace current}]
next {*}[linsert $args 0 $ns]
}
}
constructor {creatorNS args} {
namespace path [list {*}[namespace path] $creatorNS]
}
method bar {} {
puts "this is bar ([namespace current])"
dumbproc
}
}
That will then automatically put the current namespace at creation on the path of the instance. If you're doing this in many classes, you probably want to create a metaclass with the majority of machinery in it, but the above technique (a self declaration of some methods on the foo class object itself) works fine for simple cases.