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

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


    Hal Vaughan, Jul 11, 2005
    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
    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.

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

    Sherm Pendley, Jul 11, 2005
  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


    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: [email protected]" if [email protected];

    Fabian Pilkowski, Jul 11, 2005
  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) = @_;

    last INCOMING unless $incoming;

    # code for filtering incoming data

    return $data;

    # code for filtering outgoing data


    my $filter = \&{ $filters{$type} };
    $data = $filter->($data, 1);
    Gunnar Hjalmarsson, Jul 11, 2005
  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 Smith, Jul 12, 2005
  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.


    '%',s,(.),\$$/$1=1,,$;=$_}:/\w/?{y,_, ,,#..>s^~ht<._..._..c....
    Thomas Kratz, Jul 12, 2005
  9. Hal Vaughan

    Anno Siegel Guest

    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

    We have seen three more solutions to this problem.

    Tad McClellan queries the stash:


    Fabian Pilkowski uses UNIVERSAL::can:


    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 Siegel, Jul 12, 2005
  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
    Er, you mean

    eval { $data = filterincoming($data) }

    ...or in a variable..

    eval { $data = $filterincoming->($data) }
    Brian McCauley, Jul 13, 2005
  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 Vaughan, Jul 14, 2005

  12. perldoc AutoLoader

    AutoLoader - load subroutines only on demand
    Tad McClellan, Jul 14, 2005
    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.