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

I have created a Perl program that iterates the lines in a log file and passes each line to a processor. I plan on implementing many processors. I have implemented each processor as a module. Each module has a common set of subroutines: init, process, and finish. I declare each processor with a seperate package name and include something similar to the following:
package coloTotals; require Exporter; use strict; our @ISA = qw(Exporter); our @EXPORT = qw(init process finish); our $VERSION = 1.00; sub init { ... } sub process { ... } sub finish { ... }
When i run the program with more than one processor defined, I get:
Subroutine init redefined at lib/perl5/5.6.1/Exporter.pm line 57. Exporter::import('init', 'process', 'finish') called at load.p +l line 10 main::BEGIN() called at ModView.pm line 10 eval {...} called at ModView.pm line 10 Subroutine process redefined at lib/perl5/5.6.1/Exporter.pm line 57. Exporter::import('init', 'process', 'finish') called at load.p +l line 10 main::BEGIN() called at ModView.pm line 10 eval {...} called at SbModView.pm line 10 Subroutine finish redefined at lib/perl5/5.6.1/Exporter.pm line 57. Exporter::import('init', 'process', 'finish') called at load.p +l line 10 main::BEGIN() called at ModView.pm line 10 eval {...} called at ModView.pm line 10
Does anyone know how to resolve this issue. The program still functions properly, but I want to learn the appropriate way to implement a program like this and also to remove these warnings. Does anyone have some sample code for implementing an OO program similar to this?

Replies are listed 'Best First'.
Re: Encapsulation and Subroutine redefined warnings
by NetWallah (Canon) on Nov 24, 2003 at 22:20 UTC
    From my understanding of EXPORT, what you are doing is importing the EXPORTED names into your calling script. This is causing name collisions.

    I suggest NOT exporting anything explicitly in your package/modules. Instead, call the subs by their fully-qualified names, such as coloTotals::process(). This also makes your intentions clear in your code.

      That was it... thanks for the clarification
Re: Encapsulation and Subroutine redefined warnings
by iburrell (Chaplain) on Nov 24, 2003 at 22:37 UTC
    What does the code in load.pl look like? The code in coloTotals look correct for doing exporting.

    BTW, Exporter is not used for object-oriented modules. It is used to exports functions into a namespace. Instead of calling:

    ColoTotals::init(); <code> You can do: <code> use ColoTotals; init();
    Modules implementing objects usually only have class methods and instance methods.
    my $totals = ColoTotals->new(); $totals->init(); $totals->process(); $totals->finish();
Re: Encapsulation and Subroutine redefined warnings
by qq (Hermit) on Nov 25, 2003 at 13:20 UTC

    Here is a super-simple OO framework for you.

    Processor.pm

    package Processor; # constructor in base class only sub new { my $pkg = shift; bless {}, $pkg; } # init, process, finish in subclasses 1;

    Processor/SomeSubClass.pm

    package Processor::SomeSubClass; @ISA = ( Processor ); sub init { my $self = shift; # do something } # should be called process_line # this one upcases it sub process { my $self = shift; my $line = shift; return uc( $line ); } sub finish { my $self = shift; # do something } 1;

    The script that runs it:

    #!/usr/bin/perl use Processor; use Processor::SomeSubClass; my $p = Processor::SomeSubClass->new(); $p->init; while (<>) { print $p->process( $_ ); } $p->finish;

    I'm not totally sure if this is what you intended - init and finish coming before and after processing all the lines for example - but it should get you going if you havent't done it before.

    Note that my examlpe does not store any of the data in the object - again, because its hard to see what problem you are going to be solving.

    qq

      qq,

      Thanks for taking to time to provide that framework. That is what I was looking for to my overall design question. The one other question I have is about self determination of all the available processors. I planned on doing something similar to the following code, but I need to determine the base class versus just matching on the name of the object:
      foreach $symname (sort keys %main::) { #print "$_\n" if /^ybcp/; if ( $symname =~ /^ybcp/ ) { local *sym = $main::{$symname}; $$sym->init(); #print "\$$symname\n" if defined $sym; } }
      With loops like this in my driver script, I should be able to simply add a new use statement for new processors.

      Also... Just to wrap up my thoughts on my original issue. I was confused by my Perl book's explanation of the Exporter implementation. I thought it was necessary to add methods to the Exporter to make them accessible, but I now realize that it meant accessible without the module:: syntax.

        If you want to loop over a list of subclassed processors, you probably want something like:

        use Processor; use Processor::SomeSubClass; use Processor::AnotherSubClass; foreach ( qw( SomeSubClass AnotherSubClass ) ) { my $pkg = "Processor::$_"; my $processor = $pkg->new(); $processor->init(); # do something with $processor }

        Damien Conway's book is very good on OO perl, although he skips fast past some of the simple stuff.

        qq

Re: Encapsulation and Subroutine redefined warnings
by dragonchild (Archbishop) on Nov 25, 2003 at 12:53 UTC
    To expand on iburrell's reply, what you're doing looks and smells very much like filters. You have a thing and you pass that thing to various handlers / processors / filters / whatever. Each filter will take the thing and (possibly) do an action on it. Once the filter is done acting, it will hand the (possibly) modified thing to the next filter.

    Implement them as objects. For one, you don't have the namespace collision problems. Plus, you can now have filters inherit from each other, which becomes really neat.

    ------
    We are the carpenters and bricklayers of the Information Age.

    The idea is a little like C++ templates, except not quite so brain-meltingly complicated. -- TheDamian, Exegesis 6

    ... strings and arrays will suffice. As they are easily available as native data types in any sane language, ... - blokhead, speaking on evolutionary algorithms

    Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.