Checking If A Function Exists

Discussion in 'Perl Misc' started by Hal Vaughan, Jul 11, 2005.

  1. Hal Vaughan

    Hal Vaughan Guest

    I have to filter some data through a number of possible data filters. The
    names of the filters are stored in a database, then loaded into a hash, so
    I find the name of the needed filter like this:

    $filter = $filters{$type};
    $filter = \&$filter;
    $data = &$filter($data);

    I'd like to be able to do a 2 stage filter, one for incoming, and one for
    outgoing. Most filters don't need 2 stages, but some do. I'd like to
    change it to add an extra step on incoming:

    $filter = $filters{$type};
    $filter = $filter."incoming";
    $filter = \&$filter;
    $data = &$filter($data);

    If I do that, is there a way to check if there is a function
    "filterincoming()"? That way I could only add the "incoming" to the end if
    an incoming filter actually exists, otherwise, I'd just use the regular
    filter.

    Is it possible to check and see if a particular functionname is available
    without forcing an error?

    Thanks!

    Hal
     
    Hal Vaughan, Jul 11, 2005
    #1
    1. Advertisements


  2. Yes there is.

    if ( *filterincoming{CODE} )
    { print "found a filterincoming() func\n" }


    I'd explain the *foo{THING} syntax for you, only I have such poor
    people skills that I'm not even going to attempt an explanation...
     
    Tad McClellan, Jul 11, 2005
    #2
    1. Advertisements

  3. All your problems (the ones you know about, and the ones you don't know
    about) would be solved if you used a hash that maps the name of the
    filter to a ref to the sub that is supposed to do the filtering.

    Sinan
     
    A. Sinan Unur, Jul 11, 2005
    #3
  4. There's a pretty good reference to typeglobs in "perldoc perlmod" - no people
    skills required. :)

    sherm--
     
    Sherm Pendley, Jul 11, 2005
    #4
  5. I prefer to use different variables for different things. First, $filter
    is a string containing the name of your sub -- and later it contains a
    coderef to this sub. In my example below I use two vars for that.
    Look at the value of

    main->can('filterincoming')

    to find out if your package main *can* call a specific function. Btw, on
    success can() will return a coderef to that sub.

    my $filtername = 'filterincoming';
    my $filter = main->can( $filtername );
    if ( $filter ) {
    $filter->( $args );
    }

    Please read `perldoc UNIVERSAL` for details about can()'s limitations.
    Another way doing this is to catch that error (exeption handling). That
    way you moving around the limitations of can(). Check `perldoc -f eval`
    for details on how to catch any kind of runtime errors (such as calling
    non-existent subs).

    eval { $filter->( $args ) };
    warn "catched: $@" if $@;

    regards,
    fabian
     
    Fabian Pilkowski, Jul 11, 2005
    #5
  6. You can use eval() to capture a possible error.

    eval { $data = $filter->($data) };

    See "perldoc -f eval".

    Or maybe you should consider an alternative approach, for instance:

    sub myfilter {
    my ($data, $incoming) = @_;

    INCOMING: {
    last INCOMING unless $incoming;

    # code for filtering incoming data

    return $data;
    }

    # code for filtering outgoing data

    $data;
    }

    my $filter = \&{ $filters{$type} };
    $data = $filter->($data, 1);
     
    Gunnar Hjalmarsson, Jul 11, 2005
    #6
  7. Hal Vaughan

    Joe Smith Guest


    %subs = (
    'special' => \&filter_special;
    'specialincoming' => \&filter_specialincoming;
    );
    $filter_name = $filters{$type};
    $filter_sub1 = $subs{$filter_name};
    $data = $filter_sub1->($data);
    $filter_sub2 = $subs{$filter_name.'incoming'};
    $data = $filter_sub2->($data) if defined $filter_sub2;


    -Joe
     
    Joe Smith, Jul 12, 2005
    #7
  8. Hal Vaughan

    Thomas Kratz Guest

    What about keeping the code of the filters in a hash?

    my %filters = (
    Filter1 => {
    IN => sub { ... filter code ... },
    OUT => sub { ... filter code ... },
    },
    Filter2 => ...,
    );

    Then you could run the filters with:

    my $type = 'Filter1';
    foreach my $direction ( qw/IN OUT/ ) {

    $data = $filter{$type}{$direction}->($data)
    if ref($filter{$type}{$direction}) eq 'CODE';

    ... do something with $data ...
    }

    Looks a bit more clear for me.

    Thomas

    --
    $/=$,,$_=<DATA>,s,(.*),$1,see;__END__
    s,^(.*\043),,mg,@_=map{[split'']}split;{#>J~.>_an~>>e~......>r~
    $_=$_[$%][$"];y,<~>^,-++-,?{$/=--$|?'"':#..u.t.^.o.P.r.>ha~.e..
    '%',s,(.),\$$/$1=1,,$;=$_}:/\w/?{y,_, ,,#..>s^~ht<._..._..c....
    print}:y,.,,||last,,,,,,$_=$;;eval,redo}#.....>.e.r^.>l^..>k^.-
     
    Thomas Kratz, Jul 12, 2005
    #8
  9. Hal Vaughan

    Anno Siegel Guest

    [snip]
    The standard answer to this is Perl's special

    defined &filterincoming

    It is special in that it doesn't call the sub but only tests for
    definedness.

    We have seen three more solutions to this problem.

    Tad McClellan queries the stash:

    *filterincoming{CODE}

    Fabian Pilkowski uses UNIVERSAL::can:

    main->can('filterincoming')

    Gunnar Hjalmarsson employs eval() to catch a possible error:

    eval { $data = filterincoming->($data) }

    Two more solutions avoid the issue and suggest using a hash as a
    dispatch table.

    TIMTOWTDI indeed!

    Anno
     
    Anno Siegel, Jul 12, 2005
    #9
  10. In practice you are only going to want to do this if the function name
    is in a variable (or, in general, the result of an expression.

    defined &$filterincoming
    *$filterincoming{CODE}
    main->can($filterincoming)
    Er, you mean

    eval { $data = filterincoming($data) }

    ...or in a variable..

    eval { $data = $filterincoming->($data) }
     
    Brian McCauley, Jul 13, 2005
    #10
  11. Hal Vaughan

    Hal Vaughan Guest

    Just wanted to say it was a huge help that I got so many responses with so
    many different ways to do this. It even gave me ideas on how to handle a
    few different things.

    Just one note on an idea several people suggested: I am dealing with a
    number of field types, and load in the info on each type from a database.
    It's easier to NOT store the functions in a hash, since in any instance of
    the program, I only use a few different filters, so it's easier to just
    grab the name from a hash and convert it.

    Thanks to all for the help. It really made a difference.

    Hal
     
    Hal Vaughan, Jul 14, 2005
    #11

  12. perldoc AutoLoader

    AutoLoader - load subroutines only on demand
     
    Tad McClellan, Jul 14, 2005
    #12
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.