http://www.perlmonks.org?node_id=269413

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


i've been working with OOP for a while now, and i think i'm getting the hang of it. Then yesterday i came across a problem that i thought would be simple to fix, and it being 10 minutes to 5 left it for the next day. Today i came in, sat down, and have proceeded to spend all day not figuring out what's going wrong.

So here's the rundown, i have a class server.pm that uses a bunch of functions in the directory called functions. The server class has an AUTOLOAD function so that of the myriad of things that the server can do, it does not need to load in all of its functions.

i got the server and all of it's functions working, added a function, everything continued to work, added another function, and everything fell apart. Somehow the server is infinite looping while trying to get to the new function. Doing a perl debug tells me that everytime it gets to the magical goto &$method rather than going to the function correctly (like it does with all other functions) it goes back to the top of the AUTOLOAD method. Even worse is that all of the variables are correct. Following is the code along with a few comments that should illistrate what's happening (according to 'perl -d').

... # in some other function $server->print_forms(add => $data); # this is the exact errant met +hod call ... # in the server class, here's the AUTOLOAD method sub AUTOLOAD { my $self = $_[0]; # Don't disturb the @_! It's important! print STDERR "AUTOLOADing new method into server module.\n" if $self->flag('verbose') > 1; my $method = our $AUTOLOAD; $method =~ s/[^:]+:://; print STDERR "Attempting to find method $method for server module.\n +" if $self->flag('verbose') > 1; $method = -e "functions/$method.pm" ? "functions/$method.pm" : do { $method =~ /^(.)/; <functions/$1*> }; # According to the debugger Exporter is being properly # called and printing out the %:: hash tells me that # %main::functions::print_forms does indeed make it # into the mainspace properly, so i know it's not failing if (eval { require $method }) { $method =~ s/^functions\/(.*)\.pm$/$1/; eval "import functions::${method}"; print STDERR "Found $method: goto in effect...\n" if $self->flag('verbose') > 1; goto &$method; # At this point, even though $method is 'print_forms' # and @_ is correct, it goes back up to the top of # the AUTOLOAD method instead of where it's supposed to go. + } else { warn "Couldn't find $method method for server:\n$@\n"; } }
i don't think it should be necessary to post any other code, but if i've made an assumption or if something isn't obvious just tell me what i need to post and i'll put it up. Also, in case it matters, i'm running this on a Solaris 9 box using Perl 5.8.0, but the code should be platform independant (although i can't vouch for perl 5.6.x compliant).

What confuses me most is that the AUTOLOAD method works for every other function that i've handed to it, and it doesn't seem to be the naming convention or improper variable content or anything else i can think of that's screwing this one method call up. So, to those monks who are just itching for something bizaare to deal with, could you please tell me what's going on? Also, how should i go about fixing this? i'm stumped...

jynx

Replies are listed 'Best First'.
Re: infinite loop blues
by tilly (Archbishop) on Jun 26, 2003 at 21:07 UTC
    What happens if the eval that you use for the import fails for any reason? Check $@ and see if it actually imports correctly.

    If that doesn't fix it, then before your goto I would test a can to be sure that it should be found, and start debugging what package it went into, etc.

    But I will bet that it is an error hidden behind eval's error trapping.

    UPDATE: The reason why this is an infinite loop is that you call a method which is not there, falls into an AUTOLOAD, which calls the same method, it still is not there, falls into the same AUTOLOAD, etc.

      doh, got it in one.

      it was not in fact importing correctly, because of a bad method setup. In the print_forms.pm file it had up near the top something that i didn't notice until i added the following line to server.pm:

      if (eval { require $method }) { $method =~ s/^functions\/(.*)\.pm$/$1/; eval "import functions::${method}"; die $@ if $@; # added this, which gave me the error message i +needed...
      After running it i got the error message "function commit redefined", which told me the error right away! i promptly opened the print_forms.pm file and told it to EXPORT print_forms instead of commit (oops, i'm usually much better at changing things after a copy/paste :-(

      So that fixed the problem, but why was it infinite looping? Why didn't it stop after one iteration?

      jynx

      PS i can't believe i, yet again, made the "select is broken" error. i thought i had progressed past that stage. grrr...

Re: infinite loop blues
by dash2 (Hermit) on Jun 26, 2003 at 23:50 UTC
    Yeah. Some thoughts:

    This looks to me like an attempt to optimize. Otherwise, why not just get rid of the AUTOLOAD and use the functions at the start of your script? Now, you may know exactly what you are doing here, and if so feel free to say so - but have you checked whether this optimization is really necessary? (I once spent ages trying to speed up some Perl by adding SelfLoader to all my modules. This broke on half of my servers, and I later discovered that all the slowness was down to one function which could be speeded up in a much simpler way.) So, unless you know that including all the functions at the start is going to be VERY slow, think about doing that instead.

    And for that matter, if you have that many functions, you probably want to rewrite them more simply :-)

    if (eval{require $method;}) is just an inelegant way of saying if (do $method), IIRC. do will return true or false for you without dying.
    A massive flamewar beneath your chosen depth has not been shown here


      First of all, thank you for the thoughts...

      The way i've designed the code optimizes memory consumption, though that's merely a side effect of the chosen design. Loading in all of the functions would be a waste of memory if i only have to use 3 of them (out of currently 15 -- more arriving all of the time).

      As for putting all of the functions in the server module, that would make it totemically long, and i would much as rather not have to scroll through 500 lines of junk to find out that i missed a parenthesis somewhere. On the level of coding it is an optimization, because i don't have to waste a lot of time looking for the problem.

      One of the major reasons i'm going this route however is it's extensability. If i need to do something else all i have to is add a function to a directory at a later date. If i find code that's in a lot of other functions it might be worth my time to farm it out to a new function so that the other modules do one thing, do it well, and no more (which is usually a Good Thing(tm)).

      As for using the eval version versus do, require checks to make sure that the module has not already been loaded by Perl previously. This not only saves time but it also can point out programmer error (as above shows).

      i'm not trying to be harsh or rude here, it's just that i have had all of these ideas (at one point or another) as they are good ones. Currently i'm working on a project that i've been working on for about 2 years now. i'm not going to tell you what designs i had for some of my earlier attempts as some of them were absolutely pitiful. i think this approach is a step in the right direction because the flexible, extensible design allows me to do very complex things that i would not otherwise be able to easily do.

      jynx

        No offence taken. And good point about require. I'm still not convinced that having each function in a separate file is a good way to lay out your code on the programmer level, but whatever works for you.
        A massive flamewar beneath your chosen depth has not been shown here