Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"

Re^3: Yet another does-function-exist question

by f00li5h (Chaplain)
on Apr 15, 2007 at 05:30 UTC ( #610160=note: print w/replies, xml ) Need Help??

in reply to Re^2: Yet another does-function-exist question
in thread Yet another does-function-exist question

You can do it one of two ways.

  1. Check that the thing is valid before hand (as ref $coderef in my class below does)
  2. Check when you call that the result is valid (as the trigger_event sub will do if you remove the test in the first point)

At the risk of getting a TL;DR, here's something that takes coderefs and then dispatches events with them, as I wrote this, I realised that it's surprisingly long for a simple example ;)

This is the class that takes care of dispatching the events (to a single handler for each event type)

package f00li5h::Events; use Class::Std; { use strict; my %callbacks :ATTR(); =head1 register_callback $object->register_callback( event_name => $subref ); the subref will be passed a list of args provided when the event is tr +iggered, and will be protected in an eval block. =cut sub register_callback { my($self, $name, $subref) = @_; unless ( ref $subref eq 'CODE' ) { use Data::Dumper; warn "You call that a callback? it's not a real coderef, s +o i'm going to ignore it! :" . Dumper( $subref ); return; } $callbacks{ ident $self }->{ $name } = $subref; } =head1 trigger_event trigger an event, will return the exception or the result of the sub $object->trigger_event( event_name => $argref); $object->trigger_event( event_name => [ $user, $data, $foos ...] ); =cut sub trigger_event { my($self, $name, @data) = @_; warn "Callback '$name' not registered" if not defined $callbacks{ ident $self }-> { $name }; my $result; eval { $result = $callbacks{ ident $self }->{ $name }->( @data ); }; return $@ if $@; # if calling the handler fails, return the e +xception return $result; # toherwise return what the sub returned } }

This is the client code, that registeres event handlers (to be used as callbacks) and triggers the actual events on the object. Normally, the object would find it's own events, and handle them within a run() type method (Tl's MainLoop for example)

package main; use strict; { # these are the events to be triggered my @events= ( [moose => q/Moose saves thousands from terror attack!/] , [antelope => q/Antelope wins baseball game /], [moose => q/Moose alerts medial over email scams!/] , ); sub events_happening { # this sub will just iterate over @events, using closure funky +ness # it will return an empty list when there are none left ( henc +e the [] ) @{ shift @events || [] }; } } # here are some callbacks sub moose { print "[HANDLED moosed ->", @_ , " for you ]\n"; } sub antelope { print "[ANTELOPE HANDLER WON'T BE CALLED->", @_ , "]\n" +; } # set up our event handling object my $handler = f00li5h::Events->new(); # we pass in a proper coderef here $handler->register_callback( 'moose' => \&moose ); # here, we just pass in a name, the class will reject it... $handler->register_callback( 'antelope' => 'antelope' ); # this one uses an anonymous sub (which is a coderef) # it would run happily, if i had a 'llama' event in the list # you can add that as an exercise if you want ;) $handler->register_callback( 'llama' => sub { print "I'm there is no event to trigger me, because f +00li5h thought of adding me after he'd written and run the code"; } ); use Data::Dumper; # neat, an event loop, normally, you would $f00li5h->run() and have th +e object get it's events from somewhere while( my($event_name, @event_args) = events_happening ){ my $result = $handler->trigger_event($event_name, @event_args); warn "Triggering $event_name with (" . join( ",", @event_args ) . ") resulted in ". Dumper($result) if $result; }

You call that a callback? it's not a real coderef, so i'm going to ign +ore it! :$VAR1 = 'antelope'; [HANDLED moosed ->Moose saves thousands from terror attack! for you ] Triggering moose with (Moose saves thousands from terror attack!) resu +lted in $VAR1 = 1; Callback 'antelope' not registered at callback line 29. Triggering antelope with (Antelope wins baseball game ) resulted in $V +AR1 = 'Can\'t use string ("") as a subroutine ref while "strict refs" + in use at callback line 33. '; [HANDLED moosed ->Moose alerts medial over email scams! for you ] Triggering moose with (Moose alerts medial over email scams!) resulted + in $VAR1 = 1;

The class will reject 'antelope' because it's not a real coderef, although if you were to pass in 'main::antelope' as the name, and remove the check for ref $coderef, you should be able to use just names of subs. I don't see why you would want to do this, since it's the same number of characters to write \& as it is to enclose the sub's name quote likes

Justification: this does answer your question about checking if it's callable before hand (because it uses ref $subref to decide if it can be callled (long before the events actually turn up). This is a fairly painless way to check if something is callable, you can also use anonymous subs if you want

Hope this helps.

@_=qw; ask f00li5h to appear and remain for a moment of pretend better than a lifetime;;s;;@_[map hex,split'',B204316D8C2A4516DE];;y/05/os/&print;

Replies are listed 'Best First'.
Re^4: Yet another does-function-exist question
by Anonymous Monk on Mar 27, 2008 at 10:13 UTC
    $ perl -le 'print ref (\&no_such_sub);' CODE Useless test.

      Oh, my!

      @_=qw; ask f00li5h to appear and remain for a moment of pretend better than a lifetime;;s;;@_[map hex,split'',B204316D8C2A4516DE];;y/05/os/&print;

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://610160]
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (7)
As of 2021-01-26 18:21 GMT
Find Nodes?
    Voting Booth?