Checking If A Function Exists

H

Hal Vaughan

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
 
T

Tad McClellan

Hal Vaughan said:
If I do that, is there a way to check if there is a function
"filterincoming()"?
Is it possible to check and see if a particular functionname is available
without forcing an error?


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...
 
A

A. Sinan Unur

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);

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
 
S

Sherm Pendley

Tad McClellan said:
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...

There's a pretty good reference to typeglobs in "perldoc perlmod" - no people
skills required. :)

sherm--
 
F

Fabian Pilkowski

* Hal Vaughan said:
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."incoming";
$filter = \&$filter;

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.
$data = &$filter($data);

If I do that, is there a way to check if there is a function
"filterincoming()"?

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.
Is it possible to check and see if a particular functionname is available
without forcing an error?

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
 
G

Gunnar Hjalmarsson

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

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);
 
J

Joe Smith

Hal said:
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);


%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
 
T

Thomas Kratz

Hal said:
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);

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^.-
 
A

Anno Siegel

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

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
 
B

Brian McCauley

Anno said:
[snip]

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

defined &filterincoming
*filterincoming{CODE}
main->can('filterincoming')

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)
eval { $data = filterincoming->($data) }

Er, you mean

eval { $data = filterincoming($data) }

...or in a variable..

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

Hal Vaughan

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
 

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. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,430
Messages
2,571,676
Members
48,796
Latest member
Greg L.

Latest Threads

Top