in reply to Passing arguments to callback routines with named references

The routine want's a coderef. In the first case you're giving it one. It is a reference to a routine which, when executed, calls change_mode(1).

In the second case you're giving it the result of calling a subroutine whose reference is held in $change_mode.

And the third case is the same as the second, the except dereferencing is done with different symbols.

#!/usr/bin/perl -w use Data::Dumper; $Data::Dumper::Deparse = 1; my $change_mode = \&change_mode; sub change_mode { return "returning: @_ " } #Case 1: Callback routine called directly with anonymous reference Button(-text => 'To Lower Case', -command => sub { &change_mode(1) } ); #Case 2: Callback routine called with argument via named reference ve +r. 1 Button(-text => 'To Upper Case', -command => &{$change_mode}(2) ); #Case 3: Callback routine called with argument via named reference ve +r. 2 Button(-text => 'To Mixed Case', -command => $change_mode->(3) ); sub Button { my %h = @_; print Dumper(\%h); }
Result:
$VAR1 = { '-text' => 'To Lower Case', '-command' => sub { &change_mode(1); } }; $VAR1 = { '-text' => 'To Upper Case', '-command' => 'returning: 2 ' }; $VAR1 = { '-text' => 'To Mixed Case', '-command' => 'returning: 3 ' };
--Bob Niederman, http://bob-n.com

Replies are listed 'Best First'.
Re: Re: Passing arguments to callback routines with named references
by nysus (Parson) on Jul 15, 2003 at 02:23 UTC
    OK, so in other words, there is no way to use a named reference to a subroutine to supply an argument to a callback routine? You are always forced to use an anonymous subroutine as in case 1?

    $PM = "Perl Monk's";
    $MCF = "Most Clueless Friar Abbot Bishop Pontiff";
    $nysus = $PM . $MCF;
    Click here if you love Perl Monks

      Actually, bobn is not right in this case. Your "case 2" is very close to the mark -- the right way would be:
      my $change_mode = \&change_mode; ... # Case 2: Callback routine called with argument (corrected): # given that $change_mode is a ref to a sub $main->Button(-text => 'To Upper Case', -command => [ $change_mode, 2 ] )->pack;
      The "-command" option on Tk widgets (like the "bind" method) can accept an array ref (anonymous in this case), in which the first element of the array is the subroutine reference, and the remaining elements are args to pass to the sub.

      The "bind" case is a little different, because the sub will actually get the widget handle that invoked it as the first arg every time a "bound" event triggers the sub, whereas the "-command" option does not pass the widget handle as the first arg.

        Well, that's a style I don't see everyday, but perldoc Tk::callbacks says exactly that - use the array reference and this can be done as shown.

        --Bob Niederman, http://bob-n.com
      You can use closures to do it:
      # generate a closure: sub gen_change_mode { my $mode = shift; return { # same code as in change_mode }; } # then do this $main->Button(-text => 'To Upper Case', -command => gen_change_mode(2) )->pack; # or this $some_ref = gen_change_mode(3); $main->Button(-text => 'To Mixed Case', -command => $some_ref )->pack;

      so in the last case gen_change_mode(3) will return a code reference with $mode set as 3, meaning you can pass this around as a code ref, which is what you're after.

      there is an example in `perldoc perlref` which uses a lexical (my) var inside the anon sub that gets returned, so you can pass more arguments to it when you finally run it as a subroutine.

      Yes. Otherwise, it'd be Very Hard to determine automatically whether you meant a subroutine call to execute now or at some point in the future.

      I'm pretty sure that's right. There was another node about that recently.

      --Bob Niederman, http://bob-n.com