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

hello monks, i'm having a problem trying to reference a subrotine that is inside a package that maintains an ISA relationship with other package. after here i'm gonna refer to the packages as classes (because that is the conceptualization i'm using) the subroutine (called parse_tag) is declared in the super class (called gadget), this class does not EXPORT any of it's methods (because i want them to be only accesible from the class and subclasses scopes). here's the code of both classes (simplified):
package gadget; use vars qw($VERSION $CLASS); $CLASS = __PACKAGE__; $VERSION = "0.01"; #Generic method, it can (in theory ;-) return an instance of any subcl +ass sub new { #do initialization stuff } sub parse_tag { my ($twig,$elem) = @_; #do stuff }

and the child class code:
package test; use gadget; use vars qw($VERSION $CLASS); $CLASS = __PACKAGE__; $VERSION = "0.01"; BEGIN { @test::ISA = qw(gadget); @test::ISA = qw(Exporter); @test::EXPORT = qw(parse_tag); } sub new { my $class = shift || $CLASS; my $obj = $class->SUPER::new($class); $obj->{test_attr} = "atributo de prueba"; return($obj); } sub test_stuff { my $self = shift; foreach my $a (@_){ print "($self) ARGUMENTO: $a\n"; } }
i don't have problems calling the subroutine from within the subclass (called test), the problem is when i load the subclass (test) into another module and try to make a reference to it, here's the code that causes the problem:
require "test.pm"; $twigroots->{test} = \&{test::parse_tag};
in here i get this message: Undefined subroutine &test::parse_tag
i know that is some referencing stuff, but i'm kind of confused, any help will be greatly appreciated.
thanks.


ignorance, the plague is everywhere
--guttermouth

Replies are listed 'Best First'.
Re: reference to a subroutine inside a package
by chromatic (Archbishop) on Jun 02, 2004 at 22:19 UTC

    There's no parse_tag in test; it inherits that method from gadget.

    You could take a reference to it if you had an instance of twig; call can on the instance and assign the results to a scalar variable. Beware that if you invoke the subroutine reference, it hasn't curried the invocant, so you'll have to pass in an appropriate object yourself.

    I'm not sure what problem you're trying to solve, but there are various other, more painful ways to accomplish the same thing.

      thanks with can i can do the reference, but yes i have to instantiate the class and pass it as an argument (that's not problem, well if you are an OO purist it is)

      what i am trying to do is to load references to subroutines in diferent packages, so i process them with the twig_roots attribute of XML::Twig, in my application i represent an XML tag as a package that inherits the standard behaviour from another (the base class of all xml elements of my application). in here i'm kind of confused because i can't pass parameters to the subroutine i'm referencing, so it this subroutine is a class method (it does not recieve a reference to an instance of the class as the first argument), and i don't know if i should create an instance to the class inside this method (i don't want to do this because i think it is kind of dirty).
      i don't think this approach is very good but i can't think of another one
      thanks


      ignorance, the plague is everywhere
      --guttermouth
        but yes i have to instantiate the class and pass it as an argument (that's not problem, well if you are an OO purist it is)
        why would that be a problem for OO purists?
            $object->$coderef($arg1, $arg2, ...);
        seems to be perfect OO style...

        update: one might think (i thought that too for a while) that only
            $coderef->($object, $arg1, $arg2, ...);
        will work, which, of course, isn't too nice OOP.

Re: reference to a subroutine inside a package
by graff (Chancellor) on Jun 02, 2004 at 23:46 UTC
    I may be totally off-base here, but this part of your code looked strange, and struck me as possibly being related to your problem -- it just doesn't seem right, in any case:
    package test; use gadget; use vars qw($VERSION $CLASS); $CLASS = __PACKAGE__; $VERSION = "0.01"; BEGIN { @test::ISA = qw(gadget); @test::ISA = qw(Exporter); @test::EXPORT = qw(parse_tag); }
    First off, why put a BEGIN block inside a package? If you're going to load this as a module via "use test;", then the script that invokes this "use" statement is already handling the package within its own BEGIN block. If you're loading it with "require test;" then I think it's already too late for your package's BEGIN block to be heeded as such.

    Second, I would have thought that the assignment to @ISA should go like this:

    @ISA = qw/gadget Exporter/;
    You're already in "package test" at this point, so having "@test::ISA" seems redundant. But the main point is that in the code you posted, the assignment of "Exporter" to @ISA would appear to replace (obliterate) the preceding assignment of "gadget" to that array.

    I actually haven't had much practice with OO inheritance, but just in terms of basic Perl syntax, you may want to take a closer look at these things (and let me know if I'm wrong). Good luck.

      thanks for the advices, i have corrected my code (it looks better now)


      ignorance, the plague is everywhere
      --guttermouth
Re: reference to a subroutine inside a package
by dave_the_m (Monsignor) on Jun 02, 2004 at 22:25 UTC
    UNIVERSAL::can may be what you're looking for:
    package A; sub f { print "I'm f\n" } package B; @ISA=qw(A); package main; my $f = B->can('f'); $f->(); $f = A->can('f'); $f->();
    In both cases $f holds a reference to A::f

    Dave.

Re: reference to a subroutine inside a package
by Stevie-O (Friar) on Jun 03, 2004 at 02:07 UTC
    As others have said, this sort of thing:
    package Foo; sub foo(); sub new { bless[] } package Bar; our @ISA = qw(Foo); sub new { bless[] }
    does not actually put a 'foo' method into Bar. Instead, when you do this:
    $b = new Bar; $b->foo();
    Perl calls an internal function called call_method() documented in perlapi to call $b->foo(). call_method() does this:
    1. Looks in the package the object is blessed into for a sub with that name. If one is found, great! That sub is called.
    2. Otherwise, recursively searches packages listed in @ISA for the given sub. If the sub is found, it is called.
    3. Otherwise, checks the magic UNIVERSAL package for the method. (Note that UNIVERSAL is only magic for call_method; you can add new global object methods by adding them to the UNIVERSAL package!)
    4. Failing that, the blessed object's package and its @ISA are scanned again; however, instead of looking for the specified method, it looks for a method named AUTOLOAD. (This can interfere when you want to use autoloaded methods, and you inherit from a class that uses autoloaded.) Note that UNIVERSAL::AUTOLOAD will be called if it exists.
    5. If Perl finally gets to this step, your program will croak with "Can't locate object methd 'foo' via package 'Bar' at...".

    Note that this mechanism plays funnily with things that work by placing specially named methods into packages (specifically, overloading).

    Now with that explanation out of the way, there are two ways you can get at that method.

    The first one was given by someone else:

    $sub = $obj->can('foo'); # $sub references the sub that would be called if you # called $obj->foo(), handling inheritance and everything. # In fact, Perl has enough magic that if everyone plays # their cards right, it will work for autoloaded methods # that haven't even been loaded yet. # ...a bit later... # $obj->$sub(@args); # same as $obj->foo(@args);
    If you really need a reference to the method, that's the only way to do it.

    However, there's another alternative that has mysteriously vanished from the perl 5.8 documentation (it was present in 5.6); if you only care about the method itself, not the address, this will work equally well:

    $meth = 'foo'; $obj->$meth(@args); # same as $obj->foo(@args)
    --Stevie-O
    $"=$,,$_=q>|\p4<6 8p<M/_|<('=> .q>.<4-KI<l|2$<6%s!<qn#F<>;$, .=pack'N*',"@{[unpack'C*',$_] }"for split/</;$_=$,,y[A-Z a-z] {}cd;print lc