[Chicago-talk] calling functions in a package by its name

Matt Hucke hucke at cynico.net
Wed Jan 16 10:50:40 PST 2008


Hi,

I have a project in which I make much use of package names that aren't known at compile-time, but 
are instead read from a config file.  I'd like to be able to load modules, create objects, and call 
arbitrary functions in unknown packages without an object reference.

Right now, I can do it by making more use of 'eval' than I'm comfortable with.

Example package:

package MysteryClass;
use strict;

our %secretStuff = (	# caller doesn't know this variable name
     text => 'this is config info about package ' . __PACKAGE__
);

sub getConfig   # public API, implemented by lots of similar classes
{
     return \%secretStuff;
}

sub new
{
     my ($class, %args) = @_;
     my $self = {
         name => $args{name} || "John Doe",
     };
     bless ($self, $class);
     print "I am a new '$class'\n";
     return $self;
}

sub doSomethingUseful
{
     my ($self) = @_;
     print "Hello World from " . ref($self) . "!\n";
}

1;

What I'd like to do is call functions in this package, without explicitly using its name, or 
creating an object.

Presently, I use lots of "eval"'s, and it works:

# test.pm

use strict;
use Data::Dumper;

exit(main());

sub main
{
     my $classname = "MysteryClass";  # in real life, read it from a config file.

     # Load a module identified only at runtime
     my $prog = "use $classname;";
     eval $prog; die("eval '$prog' failed with " . $@ ) if ($@);

     # call a static function from this module, without an object ref
     my $conf;
     $prog = '$conf = ' . $classname . '::getConfig();';
     eval $prog;     die("eval '$prog' failed with " . $@ ) if ($@);
     print "config is: ".  Dumper($conf);

     # create an object
     my $fred;
     my $name = "Fred";
     $prog = '$fred=new ' . $classname . '(name => $name)';
     eval $prog;    die("eval '$prog' failed with " . $@ ) if ($@);
     print "object is: ".  Dumper($fred);
}


I have been wondering if there's a better way to do it.  PerlCritic tells me that stringy eval is 
bad; a bit of googling tells me that it's considered harmful because it causes the interpreter to 
begin another parsing phase.

Can anyone offer ways to call a function (known at compile-time) in a class known only at run-time, 
with neither an object reference or "eval"?

Ideally, I'd also like to be able to do something like "isa" or "can" using just the class name 
(after use'ing it) - that way I can have some sanity checking of whatever class name the user put in 
the config file, rather than creating a wholly inappropriate object type and only finding out later 
that it won't fit.

The project is a web framework that allows for object types to be specified in the config file:
     session-manager = FileSession # (or DatabaseSession or NullSession)
     template-adapter = TemplateToolkitAdapter # (or TextTemplateAdapter or others)
     user-object-class = LocalStuff::MySiteUser # (classes might be customer-written rather than
						   #	part of the framework)

(None of this is really critical... right now, I have a solution - lots of eval's - I'm just hoping 
for something a bit more efficient).

thanks

-- 
hucke at cynico.net
http://www.graveyards.com


More information about the Chicago-talk mailing list