set TTI 54
set val 54
switch -- $val {
$TTI { puts "tti" }
default { puts "else: $val" }
}
Cheers
John
Your switch command's list of value/script pairs is
wrapped in curlies, so isn't substituted; so you're
comparing $val with the literal string {$TTI}.
This may not do quite what you need, but it should help
to clarify what's going on:
switch -- $val [list \
$TTI {puts "tti"} \
default {puts "else: $val"}
]
HTH
--
Jonathan Bromley, Consultant
DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services
Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan...@MYCOMPANY.com
http://www.MYCOMPANY.com
The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
Hmmm, obviously still a way to go before I understand TCL substitution
then :-)
Many thanks for your help.
John
The logic in your switch block is the same as this:
set val 54
set TTI 54
if {$val eq $TTI} {
# do one thing
} else {
# do something else
}
A switch should be used when you compare one thing to a set of fixed
values, patterns or regular expressions (with possible transforms on
the input thing. If you want to execute exactly one bunch of code
based upon a jumble of mutually exclusive comparisons or conditions,
you could also try this:
if {condition1} {
# do stuff one
} elseif {condition2} {
# do stuff two
} else {
# do default stuff
}
>On Jun 20, 2:17�pm, Jonathan Bromley <jonathan.brom...@MYCOMPANY.com>
It helps to think like Tcl's parser :)
Maybe you find this useful:
If you want to compare a variable to _constants_ use [switch], like
switch $var {
a { ... }
b { ... }
default { ... }
}
However, if you want to compare a variable to other variables use an
if/elseif "chain", like
if { $myVar == $var1 } {
...
elseif { $myVar == $var2 } {
...
} else {
# handle default here
}
Best regards
Helmut Giese
>> Your switch command's list of value/script pairs is
>> wrapped in curlies, so isn't substituted; so you're
>> comparing $val with the literal string {$TTI}.
>
>Hmmm, obviously still a way to go before I understand
> TCL substitution then :-)
I sympathize. It's one of those things that's easy(ish)
to define and write down, but rather hard to internalise
so that it is second nature. Getting familiar with it
is not helped by the way some Tcl commands take matters
into their own hands. For example:
set a 4
set b 3
set x {$a + $b}
set y [expr {$a + $b}]
Now x contains the literal string "$a + $b" because
the curlies prevent substitution. So what about the
call to [expr]? It too sees the literal string "$a + $b"
as its argument, but [expr] then goes and does its OWN
round of substitution and plugs in the variables' values.
Perfectly clear from the docs, and exactly the behaviour
that we need, but quite bewildering at first acquaintance.
Going back to your original problem, there's a second form
of [switch] that may suit your needs. Omit the curlies
around the list of value/script pairs, so that they
become independent arguments to [switch]. They will then
each be substituted by the Tcl parser, just like arguments
to any other command:
switch -- $val \
$TTI {puts "tti"} \
default {puts "default: $val"}
Note the backslashes, since this is now just a long argument
list to [switch] and a newline would terminate the command.
The example I used was just a simplification of my 'real' problem.
I'll tell you what I'm doing, then you can tell me the correct way to
do it :-)
I'm writing a decoder for some messages (diagnostics) that are encoded
in 32 bit values. Bits 0..7 are the message 'type', bits 8..15 are a
user ID, then 16..31 are the 'payload', which are usually either two 8
bit values or one 16 bit value.
The code that generates these messages is written in C, and the
message type is defined as an enumeration of the form:
typedef enum _diagType_e
{
MSG1 = 12,
MSG2 = 34,
:
So my TCL decoder reads the file containing this definition into an
array dtVal() so I can refer to the message type values by name rather
than value. I also create another array containing the type names
indexed by value, dtLit().
So my real code looks something like (using my original incorrect code
format):
set type [expr {$msg & 0xff}]
set id [expr {($msg >> 8) & 0xff}]
set payload [expr {$msg >> 16}]
puts -nonewline "type=$dtLit($type): id=$id "
switch -- $type {
$dtVal(MSG1) { ... }
$dtVal(MSG2) { ... }
:
default { puts "unknown message type: $type" }
}
Does that make sense?
I can see what you're saying, but I am comparing my variable to what
are logically constants. And I only have one variable to compare with
the different 'constant' values, so I feel the switch is more
appropriate because I only refer to the variable once in the code.
But I'm happy to be corrected.
Appreciated :-)
> Going back to your original problem, there's a second form
> of [switch] that may suit your needs. Omit the curlies
> around the list of value/script pairs, so that they
> become independent arguments to [switch]. They will then
> each be substituted by the Tcl parser, just like arguments
> to any other command:
>
> switch -- $val \
> $TTI {puts "tti"} \
> default {puts "default: $val"}
Looks interesting - I'll give it a go. Thanks again.
John
Yes, but how about....
proc MSG_1 {id payload} {...}
proc MSG_2 {id payload} {...}
set type [expr {$msg & 0xff}]
set id [expr {($msg >> 8) & 0xff}]
set payload [expr {$msg >> 16}]
##
## Let Tcl do the dispatching!
##
MSG_$type $id $payload
--
+------------------------------------------------------------------------+
| Gerald W. Lester |
|"The man who fights for his ideals is the man who is alive." - Cervantes|
+------------------------------------------------------------------------+
Forgot your unknown message type handler!
change the last line to:
if {[llength [info proc MSG_$type]]} {
MSG_$type $id $payload
} else {
puts "unknown message type: $type
}
--
arg.
switch -- $var \
$a {
puts hit:$a
} $b {
puts hit:$b
} $c {
puts hit:$c
} default {
puts default,nomatch
}
There is a good reason why switch can take "inline" args
uwe
I'm not sure what reason is demonstrated in this example, all I know
is that it "works" to use variables as the match text, so what? It
makes the code extremely difficult to understand.
Think about what analysis requires: you have to know what values a, b
and c can take, when they overlap (okay, exact matches never overlap),
how matches change in relation to the different possible values of
these variables, and that is just so you can actually write code which
works. Imagine trying to test it.
I recommend Gerald's suggestion. I have used it in a slightly more
difficult situation. I first must make three binary decisions, then
execute one of the eight possible code fragments. There are no gaps in
the possibilities, so no default is required.
C'mon, my example wasn't that bad.
But it tells me that currently I might think too often in terms of
traditional languages.
I am working on a translator from Tcl to Javascript and implementing
'switch' will be one of the next steps. So mentally I am already
looking a bit ahead in terms of possible implementations - and forgot
that there are more possibilities in Tcl.
Thanks for the reminder and best regards
Helmut Giese
Just thinking.
Can I use a dict as arg to switch?
uwe
Read the use-case again. $a, $b, and $c are properly named constants,
albeit whose actual values are defined by the user (the "user" in this
case being a config file). Of course written exactly as above it's
hard to read. But imagine it being written as follows:
switch -- $var \
$msg(FETCH_RESULT) {
puts "returning result"
} $msg(REPORT_STATUS) {
puts "tell them I'm OK"
} $msg(SHUTDOWN) {
puts "should I exit?"
} default {
puts default,nomatch
}
which I find highly readable and also tells me the intention of the
code (given a message type, dispatch an action) much better than a
generic if-else block.
> <snip>
> I recommend Gerald's suggestion. I have used it in a slightly more
> difficult situation. I first must make three binary decisions, then
> execute one of the eight possible code fragments. There are no gaps in
> the possibilities, so no default is required.
In this specific use-case there are 256 possible values so a
"default" (or trailing "else" if implemented as if-else) is a good
idea.
Thanks for the suggestion, but:
1. Sorry, I didn't give a very good description of the problem. The
message type names aren't all of the form MSG_x - they don't share a
common prefix.
2. Your solution appears to assume that MSG_x has type value x, which
isn't true (in my description, MSG_1 has type value 12, MSG_2 has type
value 34).
No, I understood you -- those were examples. Say you only have three types
of messages with values 12, 34 and 36. Then you would declare three procedures:
proc MSG_12 {id payload} {...}
proc MSG_34 {id payload} {...}
proc MSG_36 {id payload} {...}
And see my other follow up which explains how to catch the unknown message
types.
If you find it confusing to have the procedures named MSG_x, then you are
welcome to call them MSG_TYPE_x or whatever -- the key is the _x part.
That is more readable (although somewhat abusive of syntax to avoid
line continuations), but...
> In this specific use-case there are 256 possible values so a
> "default" (or trailing "else" if implemented as if-else) is a good
> idea.
I assume that all the values are known. If so, then the code is even
easier, and can still be generic:
# define or reuse handler code:
proc handle_default_msg {...} { puts "default,nomatch" }
proc handle_shutdown_msg {...} { puts "should I exit?" }
# Map could vary per instance
set map {12 MSG1 34 MSG2 99 SHUTDOWN}
# Handlers could vary per instance
set MSG(DEFAULT) handle_default_msg
set MSG(SHUTDOWN) handle_shutdown_msg
set MSG(MSG1) handle_msg1_msg
...
set type [string map $map [expr {$msg & 0xff}] ]
set id [expr {($msg >> 8) & 0xff}]
set payload [expr {$msg >> 16}]
if {[info exists MSG($type)]} {
$MSG($type) $id $payload
} else {
$MSG(DEFAULT) $id $payload
}
Given the examples, you could use generic handler like so:
proc handle_generic_msg { type {id ""} {payload ""} {extramsg ""} } {
puts [list type $type id $id payload $payload $extramsg]
}
But that ties in the code with the enum definition, which I am trying
to avoid by reading in the enum definition and storing the values 12,
34 etc. in an array (see earlier post). I only want to see the
literals in the TCL, not the values.
As per my previous post, I don't want to tie the code to the enum
values 12, 34 etc. Can I use my enum value lookup array:
set map {$dtVal(MSG1) MSG1 $dtVal(MSG2) MSG2 $dtVal(SHUTDOWN)
SHUTDOWN}
? (I'm guessing I've got the substitution wrong...)
Ok, so you call your procedures:
proc MSG_add {id payload} {...}
proc MSG_sub {id payload} {...}
And your call becomes:
MSG_$translationArray($type) $id $payload
Yes, use [list ... ] instead of { ... }
What he is suggesting is to use the map instead of the array. Either way
you are tie the code to the enum value -- one when you create the array, the
other when you set of the map.
This is basically the same approach I am suggesting (my last post showed it
using the an array with indexes of the enum and the values of the array
being the symbolic).
The map (but it could just as easily be an array) avoids "tying" the
enum values to the code. Note that I use two maps/arrays to "untie"
the enum values from the code. I used a map so that it would look more
like an enum, which is the basic structure that makes the code more
readable. The enum was supposed to come from a config file, which
indicates to me that different enums/maps/arrays would be possible.
The second array maps enum keywords to handler code. This allows
developers to substitute different handlers based upon a configuration
and avoids special names for the actual handler procs. In some cases
you might want to reuse an existing handler, so I recommended passing
the "type" so a generic handler could be reused.
Overall, using two "maps" allows configuration only reprogramming of
the handler code (once the handlers are written).
> This is basically the same approach I am suggesting (my last post showed it
> using the an array with indexes of the enum and the values of the array
> being the symbolic).
Yes, and the enum/map could be converted into an array. The [string
map] just allows you to avoid one [info exists], but in the end gives
less information and could lead to bugs.
I think the solution is a good one (provided that you don't want to
play with local variable) but not presented correctly.
From your originall post the declarations would be:
proc MSG_$TTI {} {puts tti}
proc MSG_$ANOTHERENUM {} {...}
add the call would be:
set proc MSG_$val
if {[info exists $proc]} {
$proc
} else {
puts "notti: $val"
}
You don't need to the values other than as variables.
Better still declare the procs in a namespace.
Note that Tcl is not picky about proc names - it will work even for :
set TTI {this will $ be a [very stupid] proc name}
Yet another solution is to create your own control construct like
switch that does a subst on the pattern args -
something like (untested):
proc myswitch {val body} {
set sw {}
foreach {pat patbody} $body {
lappend sw [uplevel 1 subst $pat] $body
}
uplevel 1 switch -- $val $sw
}