geoffleach has asked for the wisdom of the Perl Monks concerning the following question:

I would like to be able to write code that requires a package that is specified at runtime and then execute a subroutine in that package. For example:
$name = 'SomeModule"; require $name; $name::somesub();

Is there any way to do this?

Replies are listed 'Best First'.
Re: using a variable with require
by haukex (Archbishop) on Jun 22, 2022 at 06:36 UTC

    See the core module Module::Load (there are others, like Module::Runtime, but this one is core), plus the \&{'subname'} symbolic reference syntax which is allowed even under strict. However, if $name is user input, this is still a security risk!

    use warnings; use strict; use Module::Load qw/load/; my $name = 'SomeModule'; load $name; (\&{$name.'::somesub'})->();
      Hmmm ... from the doc, I would not have guessed it, but it appears that Module::Load is exactly what I need. Many thanks.
Re: using a variable with require
by GrandFather (Saint) on Jun 22, 2022 at 04:23 UTC

    A nasty solution to the invocation problem is to use eval. If SomeModule comes from the outside world this is a really bad solution!

    use strict; use warnings; my $modName = 'Getopt::Long'; my $fixedMod = $modName; $fixedMod =~ s!::!/!g; $fixedMod .= '.pm'; require $fixedMod; my $length = 24; eval "${modName}::GetOptions('length:i' => \\\$length)";

    However, there may be a much better solution to your big picture issue. What are you trying to do that seems to need a require?

    Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
      Thanks. While I digest this suggestion, here's the big picture as requested. I download podcasts from a number of sources. Each feed has its own peculiarities, requiring feed-specific coding, which I wish to isolate as modules. On the other hand, local processing is not feed-specific.
      % download_processor feed1 % download_processor feed2 % ....
      and there are modules feed1.pm, feed2.pm, and so on. What I would like to accomplish is when a new feed comes along, I just write a module for the new feed and don't have to modify the download_processor.

        You can do that in a very light weight way by creating a package for each feed type. They can be in a single file or multiple files or even the main file. If not in the main file you can use or require as suits your purpose then create an instance of the appropriate type. Consider:

        use strict; use warnings; package FetchOne; sub new { my ($class, %params) = @_; return bless\%params, $class; } sub type { return 'Fetch type One'; } package FetchTwo; sub new { my ($class, %params) = @_; return bless\%params, $class; } sub type { return 'Fetch type Two'; } package main; my $type = 'Two'; my $obj = "Fetch$type"->new(); print $obj->type();

        Prints:

        Fetch type Two
        Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
        The usual pattern for this is to put each implementation into a package hierarchy of objects, then know the package name you want to load, then:

        use Module::Load; ... load $package; my $obj= $package->new( %options ); $obj->do_something;

        No need for awkward cross-package function-calls.

        Maybe GrandFather wanted to recommend a plugin-supporting module (e.g. Module::Pluggable)?

        geoffleach:

        Your question already seems to be solved, but I thought I'd post a note with respect to your original problem and the use case you're describing here. The name of the file and the name of the package inside it isn't necessarily related. That's one of the difficulties that I expect Grandfather and others were thinking about.

        So one thing you can do is use the same package name for all of your podcast processors. Then you can do something like:

        $podcast_name = 'comedy_with_joe'; require "podcast_processors/${podcast_name}.pm"; PodcastProcessor::somesub();

        where each of your podcast processor modules would look like:

        # process podcasts for XYZ package PodcastProcessor; ... yadda ... sub somesub { ... }

        ...roboticus

        When your only tool is a hammer, all problems look like your thumb.

Re: using a variable with require
by hippo (Archbishop) on Jun 22, 2022 at 08:41 UTC

    Are you by chance trying to reinvent the Plugin wheel? If so, then why not just use (or take inspiration from) one of the Plugin architecture modules already available, eg. Plugin::Simple by stevieb.

    If not, then it would likely be beneficial if you could elaborate on this apparent XY problem.


    🦛

      It does appear that I have re-invented the Plugin wheel :-) Module::Load appears to satisfy my need.
Re: using a variable with require
by GrandFather (Saint) on Jun 22, 2022 at 04:02 UTC

    See require for the full story. The short version is you need to replace :: with / and append a .pm

    my $modName = 'Getopt/Long.pm'; require $modName;

    Update Oops, this just answers the easy part of the question! Still thinking about the second part.

    Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
Re: using a variable with require
by Anonymous Monk on Jun 22, 2022 at 15:58 UTC

    Another couple alternatives for executing a subroutine in a package determined at run time:

    If it can be invoked as a static method, $name->somesub() will work.

    In the general case, $name->can( 'somesub' )->() will also work. If you need to call somesub() more than once,

    my $somesub = $name->can( 'somesub' );
    $somesub->();
    
Re: using a variable with require
by Anonymous Monk on Jan 24, 2023 at 00:32 UTC
    TIMTOWTDI:
    require UNIVERSAL::require; $name = 'SomeModule'; $name->require; $name->somesub();
      UNIVERSAL::require

      I would not recommend this. It adds two methods to the UNIVERSAL class, the base class for all Perl classes. This means that loading this module can affect every single class and object in the program.