How to call a procedure with arguments from argument list

tcl
14,838

Solution 1

(First off, the args parameter to a procedure is only magical when it is last. Put anything after it and it becomes just an ordinary variable. Just so you know. Of course, that might even be what you need here!)

To call out of process_menu_item, you'd want to change:

$command $args

to this (Tcl 8.4 or earlier):

eval $command $args

Well, if you are paranoid you actually write this:

eval [list $command] [lrange $args 0 end]

Or you use Tcl 8.5 (or later):

$command {*}$args

Then, you'd probably build the call to process_menu_item like this:

process_menu_item frobnicate [list foo bar]

There are a few other ways to do it rather than using list (many Tcl commands can produce lists) or you could use a literal:

process_menu_item frobnicate {foo bar}

What isn't recommended is putting anything in that literal refers to variables inside process_menu_item (which would only work with the shortest eval-based form). It lets you do awesome stuff, but it's also incredibly fragile. If you want to pass in the loop variable, that's quite possible but it requires techniques that are a little more advanced (and you should ask another question).

Solution 2

You can always construct a wrapper function:

proc wrapper {arg_list} {
    example [lindex $arg_list 0] [lindex $arg_list 1] [lindex $arg_list 2]
}

TCL 8.5 has the {*} operator which was designed for this sort of thing:

% proc test {a b c} {
    puts a
    puts b
    puts c
}
%
% set inp [list "A" "B" "C"]
A B C
% test $inp
wrong # args: should be "test a b c"
% test {*}$inp
a
b
c
Share:
14,838
ilya1725
Author by

ilya1725

C/C++ from 1996 LabVIEW Tcl

Updated on July 19, 2022

Comments

  • ilya1725
    ilya1725 almost 2 years

    This is Tcl 8.4.

    I'm trying to make a procedure that can do something common on any other procedure specified by the user.

    For example:

    proc process_menu_item {command args {count 1}} {
        set inf     [expr {$count == -1 ? 1 : 0}]
        set end_str [expr {$count == -1 ? "inf" : $count}]
        set loop_cnt 0
    
        while {$inf || $count > 0} {
            incr loop_cnt
            puts [format "------- %s -------" [bold "LOOP $loop_cnt of $end_str"]]
    
            $command $args
        }
        return
    }
    

    This procedure, I hoped, will just execute the specified $command $count times. The problem I run into is with passing the arguments.

    Let's assume I want to call a procedure like this:

    proc example {{par_a {-1}} {par_b {-1}} {par_c 1}} {
        puts "All params: $par_a $par_b $par_c"
    }
    

    How should I call process_menu_item? Is it even possible? I tried a string, a list, but eventually par_a gets a big list of all the arguments.

    Thank you.

  • ilya1725
    ilya1725 about 12 years
    So the wrapper will have to be written for every procedure that can be called?