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

I have a module, let's call it "SpiderMan". SpiderMan defines a function that contains a function from another module. Let's call this other module "Doctor Octopus".

So of course, SpiderMan will not compile unless it gets that function from Doctor Octopus. (Hey, at least, I didn't use Mister Ed and the characters from Gilligan's Island for module names, okay?).

Now the problem is that Doctor Octopus also calls many functions from SpiderMan. Doctor Octopus must use the SpiderMan module. So both modules need functions from each other.

So I am seeing an error: "Undefined subroutine..." coming from Doctor Octopus. This subroutine is from SpiderMan. I know that SpiderMan is exporting this function. So what's going on? (Note that the function in question is not the one containing the Doctor Octopus function.)

When I do 'perl -cw' on SpiderMan and Doctor Octopus, I get all these "subroutine redefined" warnings... I have a hunch about what's going wrong. But I am unsure how to solve it. Thanks for any insight.

Replies are listed 'Best First'.
Re: Undefined subroutine errors
by ikegami (Patriarch) on Oct 26, 2005 at 00:01 UTC

    Design issues asside, Exporter needs to be loaded (and its configuration variables must be initialized) before the other module of the pair is loaded. Using the following templates will allow both modules to import from each other:

    use strict; use warnings; package SpiderMan; BEGIN { our @ISA = qw( Exporter ... ); our @EXPORT_OK = qw( ... ); require Exporter; } use ...; use ...; use Doctor::Octopus; use ...; use ...; ... 1;
    use strict; use warnings; package Doctor::Octopus; BEGIN { our @ISA = qw( Exporter ... ); our @EXPORT_OK = qw( ... ); require Exporter; } use ...; use ...; use SpiderMan; use ...; use ...; ... 1;
      You were correct. It works now. Thanks. Can you or someone else please tell me why I need the BEGIN blocks?

        use and BEGIN are executed as soon as they are compiled. Without the BEGIN, the following would happen:

        • Spiderman's use strict; is compiled.
        • Spiderman's use strict; is executed.
        • Spiderman's use warnings; is compiled.
        • Spiderman's use warnings; is executed.
        • What was in Spiderman's BEGIN is compiled.
        • Spiderman's use Doctor::Octopus; is compiled.
        • Spiderman's use Doctor::Octopus; is executed.
        • require Doctor::Octopus is executed.
          • Octopus's use strict; is compiled.
          • Octopus's use strict; is executed.
          • Octopus's use warnings; is compiled.
          • Octopus's use warnings; is executed.
          • What was in Octopus's BEGIN is compiled.
          • Octopus's use Spiderman; is compiled.
          • Octopus's use Spiderman; is executed.
          • Spiderman is already loaded, so it's not loaded again.
          • Spiderman is already loaded, so it's not executed again.
          • Spiderman->import doesn't exist, so it isn't called.
            This is the problem. Exporter hasn't been loaded yet, and the variables telling Exporter what Spiderman should export haven't been initialized.
          • The rest of Octopus is compiled.
          • What was in Octopus's BEGIN is executed.
          • The rest of Octopus is executed.
        • The rest of Spiderman is compiled.
        • What was in Spiderman's BEGIN is executed.
        • The rest of Spiderman is executed.
        • Doctor::Octopus->import is called.

        Remember that

        use Module

        means

        BEGIN { require Module; Module->import if Module->can('import'); }
Re: Undefined subroutine errors
by shemp (Deacon) on Oct 25, 2005 at 23:15 UTC
    It would be helpful to see exactly how these module are including one another. Are they being use'd or require'd? If the modules are packages, are the use or require statements within the packages, or outside of the package declarations?

    Also, i may get flamed for this, but it seems like a design flaw to have two modules that depend on each other....flame away!


    I use the most powerful debugger available: print!

      I'm sure no sensible person would flame you for suggesting that OP's base problem looks like a design issue. Given that OP seems to have control over all the code involved OP's best option may be to refactor the code to remove the circular dependencies that are implied, or at least move the circular code into a common module so that that nastyness doesn't leak out to pollute other code.

      However, assuming that the modules are currently being "used", requiring may fix the issue. OP should take a look at Using a module more than once which has some interesting and pertinent replys.


      Perl is Huffman encoded by design.

        Although uncommon, the intentions of OP might not necessary be a design flaw. His situation is similar to C code when we export symbols, not from the library to main, but from plugin to other plugins using the RTLD_GLOBAL with the 2nd parameter to dlopen(), so plugins can have access to the symbols of one another.

        OP has advanced an interesting problem. My efforts so far reveal that the problem is trivially solved by calling the functions with their full package names. It is the import mechanism of Export that fails to make things more convenient and allow us to import all symbols to both namespaces at once.

      They are being use'd not require'd.

      Both modules declare their own namespace with package.

Re: Undefined subroutine errors
by leriksen (Curate) on Oct 26, 2005 at 00:57 UTC
    Another solution - maybe not a good one - is this

    #!/usr/bin/perl -w use strict; use warnings; use Spiderman qw(shootWeb); print STDERR shootWeb() . "\n"; package Spiderman; use base qw(Exporter); our @EXPORT = qw(shootWeb webStrength); use DrOctopus (); sub shootWeb { return DrOctopus::deflectWeb(); # from DrOctopus } sub webStrength { return 1; } package DrOctopus; use base qw(Exporter); our @EXPORT = qw(deflectWeb); use Spiderman (); sub deflectWeb { return Spiderman::webStrength(); # from Spiderman } 1;

    ...reality must take precedence over public relations, for nature cannot be fooled. - R P Feynmann

      None of the functions provided by Exporter are called in your snippet. Why bother using Exporter?

      Update: In answer to my question, I suppose it gives the Green Goblin and other third parties the ability to import from these modules, but it detracts from the point of using fully qualified function names.

        In this case no, but that doesnt stop someone else using Spiderman or DrOctopus with the full export.

        As I said, it fixes his problem, YAWTDI, but not a great way.

        ...reality must take precedence over public relations, for nature cannot be fooled. - R P Feynmann

Re: Undefined subroutine errors
by dragonchild (Archbishop) on Oct 26, 2005 at 00:33 UTC
    Refactor the items that both need into a third module (Hulk?) and have both SpiderMan and DoctorOctopus 'use' it.

    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re: Undefined subroutine errors
by sauoq (Abbot) on Oct 26, 2005 at 02:33 UTC
    (Hey, at least, I didn't use Mister Ed and the characters from Gilligan's Island for module names, okay?).

    Alas, you missed your chance. You could've called the modules Conjunction.pm and Junction.pm...

    -sauoq
    "My two cents aren't worth a dime.";
    
Re: Undefined subroutine errors
by EvanCarroll (Chaplain) on Oct 25, 2005 at 22:52 UTC
    Try installing one of the modules manually if it has xs component and still requires another module this problem could get much more complex. Try forcing the install as well through -MCPAN.


    Evan Carroll
    www.EvanCarroll.com
      What are you talking about? These aren't CPAN modules. These are my own modules that I wrote. The error appears whenever I use the modules (after both have been installed).