How to dynamically generate function name and call it?

H

Hemant Shah

Folks,

How do I dynamically generate function and then execute the function?

I have a perl script that is used to drop/create/update database tables.
The script has drop/create/update functions for each table, and I
have if statements to call appropriate functions:

Example:

if ($TableName eq "TRAC")
{
CreateTRACTable();
}
elsif ($TableName eq "TRCO")
{
CreateTRCOTable();
}

and so on. I have similar if statements for drop and update.
Instead of having bunch of if statements, I can put the table
names in a hash and call:

if ($Action eq "Create")
{
Create${TableName}Table():
}
elsif ($Action eq "Drop")
{
Drop${TableName}Table():
}

and so on.


How do I do this?

Thanks.

--
Hemant Shah /"\ ASCII ribbon campaign
E-mail: (e-mail address removed) \ / ---------------------
X against HTML mail
TO REPLY, REMOVE NoJunkMail / \ and postings
FROM MY E-MAIL ADDRESS.
-----------------[DO NOT SEND UNSOLICITED BULK E-MAIL]------------------
I haven't lost my mind, Above opinions are mine only.
it's backed up on tape somewhere. Others can have their own.
 
R

Robert 'phaylon' Sedlacek

Hemant Shah said:
How do I dynamically generate function and then execute the function?

Two ideas: Either try utilising a hash as a lookup table:
Example:

if ($TableName eq "TRAC")
{
CreateTRACTable();
}
elsif ($TableName eq "TRCO")
{
CreateTRCOTable();
}

my %table_actions = (
TRAC => \&CreateTRACTable,
TRCO => \&CreateTRCOTable,
);

$table_actions{$TableName}->(@arguments);

which would mean you still have to define all actions. If this is
good or bad depends on your situation. If there's really no problem
or danger for you (e.g. nothing user supplied or invalidated, etc.)
then there's the symbolic reference way:

{ no strict 'refs'; # turn this off in smallest scope
*{'Create' . $TableName . 'Table'}->(@arguments);
}

This assumes that you're using strict and warnings, which you should
always have turned on.

Depending on your design you might also want to think about some
approach that doesn't need this (the second solution, that is). This can
lead to applications that are harder to maintain in my experience. But
it's imho really great for smaller software projects or prototyping.

hth,
phaylon
 
T

Tad McClellan

Hemant Shah said:
How do I dynamically generate function and then execute the function?


By implementing a "dispatch table", usually a hash where the values
are all CODE references.

I have a perl script that is used to drop/create/update database tables.
The script has drop/create/update functions for each table, and I
have if statements to call appropriate functions:

Example:

if ($TableName eq "TRAC")
{
CreateTRACTable();
}
elsif ($TableName eq "TRCO")
{
CreateTRCOTable();
}

and so on.

How do I do this?


my %actions = ( # a "dispatch table", untested
TRAC => \&CreateTRACTable,
TRCO => \&CreateTRCOTable
);

...

die "don't know what action to take for '$TableName'\n"
unless exists $actions{ $TableName };

$actions{ $TableName }->(); # call the appropriate function
 
B

Brian McCauley

Tad said:
my %actions = ( # a "dispatch table", untested
TRAC => \&CreateTRACTable,
TRCO => \&CreateTRCOTable
);

There are two entries in that hash. A soon as you add another one
you'll hit the rule-of-3. You'll be manually doing the same thing three
times in succession. In programming this is generally a sign that you
are doing it wrong. This is no exception. You probably should be
going down the symbolic references path. There are a lot of reasons not
to use symbolic references in a lot of contexts. This leads some
people, most vocally Tad, to say they are always a bad thing. And
indeed I'll admit that in some purist sense they are always a bad
thing. But in this case they are not as bad as breaking the rule-of-3
(IMNSHO).
 
B

Brian McCauley

Robert said:
*{'Create' . $TableName . 'Table'}->(@arguments);

Or, more simply:

"Create${TableName}Table"->(@arguments);

Note: If you were creating a module, say My::FreakyModule, then I'd
suggest separating all your subroutines that are accessed by symref
into separate namespaces:

"My::FreakyModule::Tables::${TableName}::Create"->(@arguments);
 
H

Hemant Shah

While said:
By implementing a "dispatch table", usually a hash where the values
are all CODE references.

Thanks. That's what I was looking for.
my %actions = ( # a "dispatch table", untested
TRAC => \&CreateTRACTable,
TRCO => \&CreateTRCOTable
);

...

die "don't know what action to take for '$TableName'\n"
unless exists $actions{ $TableName };

$actions{ $TableName }->(); # call the appropriate function

--
Hemant Shah /"\ ASCII ribbon campaign
E-mail: (e-mail address removed) \ / ---------------------
X against HTML mail
TO REPLY, REMOVE NoJunkMail / \ and postings
FROM MY E-MAIL ADDRESS.
-----------------[DO NOT SEND UNSOLICITED BULK E-MAIL]------------------
I haven't lost my mind, Above opinions are mine only.
it's backed up on tape somewhere. Others can have their own.
 
H

Hemant Shah

While said:
Or, more simply:

"Create${TableName}Table"->(@arguments);
Thanks.


Note: If you were creating a module, say My::FreakyModule, then I'd
suggest separating all your subroutines that are accessed by symref
into separate namespaces:

"My::FreakyModule::Tables::${TableName}::Create"->(@arguments);

--
Hemant Shah /"\ ASCII ribbon campaign
E-mail: (e-mail address removed) \ / ---------------------
X against HTML mail
TO REPLY, REMOVE NoJunkMail / \ and postings
FROM MY E-MAIL ADDRESS.
-----------------[DO NOT SEND UNSOLICITED BULK E-MAIL]------------------
I haven't lost my mind, Above opinions are mine only.
it's backed up on tape somewhere. Others can have their own.
 
U

Uri Guttman

HS> Thanks.

why are you saying thanks and quoting the symref answer? i am always
appalled to see symref answers given out when a dispatch table (as you
were shown) is much safer and a better solution.

uri
 
U

Uri Guttman

BM> There are two entries in that hash. A soon as you add another one
BM> you'll hit the rule-of-3. You'll be manually doing the same thing three
BM> times in succession. In programming this is generally a sign that you
BM> are doing it wrong. This is no exception. You probably should be
BM> going down the symbolic references path. There are a lot of reasons not
BM> to use symbolic references in a lot of contexts. This leads some
BM> people, most vocally Tad, to say they are always a bad thing. And
BM> indeed I'll admit that in some purist sense they are always a bad
BM> thing. But in this case they are not as bad as breaking the rule-of-3
BM> (IMNSHO).

i agree with tad about avoiding symrefs. if the command is truly part
of the function name, then another choice is to use class method calls
which can be generated without symrefs or breaking strict.

my $meth = "Create${command}able" ;
MyClass->$meth( ... ) ;

just make sure all those methods expect (and probably ignore) the class
name as their first argument.

remember:

symrefs are for munging the symbol table, not for flow control or data
structures.

uri
 
D

DJ Stunks

Hemant said:
Folks,

How do I dynamically generate function and then execute the function?

I have a perl script that is used to drop/create/update database tables.
The script has drop/create/update functions for each table, and I
have if statements to call appropriate functions:

Example:

if ($TableName eq "TRAC")
{
CreateTRACTable();
}
elsif ($TableName eq "TRCO")
{
CreateTRCOTable();
}

and so on. I have similar if statements for drop and update.
Instead of having bunch of if statements, I can put the table
names in a hash and call:

if ($Action eq "Create")
{
Create${TableName}Table():
}
elsif ($Action eq "Drop")
{
Drop${TableName}Table():
}

and so on.


How do I do this?

Thanks.

Are your Create{ xxxx }Table and Drop{ xxxx }Table routines so
different for various xxxx's that you have to have completely different
subs for each distinct create, drop and xxx combination?

I'm sure you could simplify to

sub create_table {
my ($xxxx) = shift
# create as necessary
}

or even

sub alter_table {
my ($action, $xxxx) = @_;
# create as necessary if $action eq 'create'
# drop as necessary if $action eq 'drop'
}

-jp
 
B

Brian McCauley

Uri said:
BM> There are two entries in that hash. A soon as you add another one
BM> you'll hit the rule-of-3. You'll be manually doing the same thing three
BM> times in succession. In programming this is generally a sign that you
BM> are doing it wrong. This is no exception. You probably should be
BM> going down the symbolic references path. There are a lot of reasons not
BM> to use symbolic references in a lot of contexts. This leads some
BM> people, most vocally Tad, to say they are always a bad thing. And
BM> indeed I'll admit that in some purist sense they are always a bad
BM> thing. But in this case they are not as bad as breaking the rule-of-3
BM> (IMNSHO).

i agree with tad about avoiding symrefs. if the command is truly part
of the function name, then another choice is to use class method calls
which can be generated without symrefs or breaking strict.

my $meth = "Create${command}able" ;
MyClass->$meth( ... ) ;

So long as @MyClass::ISA is non-empty and $command doesn't contain '::'
(or similar) that's equivalent to

no strict 'refs';
"MyClass::Create${command}able"->('MyClass', ... ) ;

Uri's solution is still using a symref (i.e.a runtime-lookup of a
string from _data_ in the Perl symbol table) under the hood, but it
doesn't carry the red flag of a "no strict" in front of it.

Now as it happens in this case Uri's solution happens not to open up a
secirty hole in this particular instance because we're augementing
$command. But consider, if you will, the (more common) situation where
you're not.

MyClass->$command( ... ) ;

Now I have to agree that this is looks a whole lot cleaner than my
alternative.

no strict 'refs';
"MyClass::$command"->( ... ) ;

But looks can be deceptive.
remember:

symrefs are for munging the symbol table, not for flow control or data
structures.

Remember, symrefs are very dangerous things, and remain dangerous even
if you pretend not to use them. In fact they become more dangerous
because people looking at the code will not see "no strict" and may be
fooled into thinking you aren't using symrefs so not realize the
danger.

Consider what happens in the code below when fed
'main::private_function' on STDIN

#!perl -T
use strict;
use warnings;

sub private_function { die "Bang!" };

sub AllowedOperations::allowed { print "yes, you can do that" }

while (<>) {
chomp;
AllowedOperations->$_();
}
__END__
 
U

Uri Guttman

BM> MyClass->$command( ... ) ;

BM> Now I have to agree that this is looks a whole lot cleaner than my
BM> alternative.

BM> no strict 'refs';
BM> "MyClass::$command"->( ... ) ;

BM> But looks can be deceptive.

BM> Remember, symrefs are very dangerous things, and remain dangerous even
BM> if you pretend not to use them. In fact they become more dangerous
BM> because people looking at the code will not see "no strict" and may be
BM> fooled into thinking you aren't using symrefs so not realize the
BM> danger.

in general i would use a dispatch table just so i can control which subs
can be called. even if it was uber symmetrical i would have created it
with hard refs. good editors make that easy.

and if i do the dynamic method call i will usually check for existance
with can() or something. you can even have a hash table of allowed
method names and check that first (no hard refs needed as values).

BM> Consider what happens in the code below when fed
BM> 'main::private_function' on STDIN

BM> #!perl -T
BM> use strict;
BM> use warnings;

BM> sub private_function { die "Bang!" };

BM> sub AllowedOperations::allowed { print "yes, you can do that" }

BM> while (<>) {
BM> chomp;
AllowedOperations-> $_();
BM> }

sure you can break a simple version. i was just showing how to do it
without no strict. i reserve no strict for direct symbol table munging
and not basic flow control.

uri
 

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
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top