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

Hi,
I would like to subclass the Log::Agent class and keep the complete interface. I can do a:
use base qw( Log::Agent );
But then, off course I loose the exported functions and also some magical variables, as I call them.
For example, Log::Agent has the notion of a $Driver variable.
Normally it is a class variable, so how do I let the two packages (parent
and inherited) behave the same?
First
use Log::Agent;
$Log::Agent::Driver = ...
Second
use My::Log;
$My::Log::Driver = ...

Would I need to redefine the $Driver variable in My::Log, or does the inheritance mechanism of Perl (looking in the parent class) only works for methods?


johanvdb

Edit kudra, 2002-05-07 Took care of unclosed pre

Replies are listed 'Best First'.
Re: Inheritance and exported methods
by Joost (Canon) on May 07, 2002 at 13:45 UTC
    </code> From top to bottom (and without testing)

    A. You might try something like:

    use Agent::Log; sub import { goto &Log::Agent::import; }

    in your subclass to export everything from Log::Agent to the caller (and your own class).

    B. If you have a $Log::Agent::Driver var that the caller is relying on (and it is not exported by Log::Agent), you're out of luck, unless your subclass also uses $Log::Agent::Driver (maybe aliasing it:

    *My::Log::Driver = \$Log::Agent::Driver #this is sort of what an export does

    C. Built-in inheriting in perl only applies to methods.

    -- Joost downtime n. The period during which a system is error-free and immune from user input.
(tye)Re: Inheritance and exported methods
by tye (Sage) on May 07, 2002 at 18:23 UTC

    In general, mixing OO and exporting is a bad idea. In this case, Log::Agent does only exporting and does no OO. So use base qw( Log::Agent ); is of no value as Log::Agent contains no methods (subroutines that expect an object as the first argument) and so you can never use inheritance with Log::Agent.

    It sounds like you want to share all package variables with Log::Agent but replace or wrap some of its functions with your own versions.

    You can share package variables between two (or more) packages by importing/exporting the variables so that both packages contain aliases to the same variables. Since Log::Agent doesn't list its package variables as being available for export (quite understandably since you aren't expected to modify them directly), you'd have to import "by hand", that is, do yourself what Exporter.pm usually does for you:

    for my $var ( qw( Driver Prefix Trace Debug Confess Caller Priorities Tags DATUM ) ) { no strict 'refs'; *{$var}= \${"Log::Agent::$var"}; } *prio_cache= \%Log::Agent::prio_cache;
    Then any time in your module code that you access or change, for example, $Driver, you are also accessing/changing $Log::Agent::Driver, because those are now both aliases to the same scalar variable.

    However, changing $Log::Agent::Driver directly is not covered in the Log::Agent documentation so doing so violates the defined interface to the module and a future version of the module is free to change how $Log::Agent::Driver is used (or turn it into a lexical, etc.) such that your module would no longer work. This is the primary reason why this is a really bad idea.

    I strongly encourage you instead to try to patch Log::Agent to extend it to also meet your needs and then get your patch incorporated into a new version of Log::Agent.

    Alternately, in your code you could exclusively use defined interfaces, like logconfig().

    The next step is to have your module export the routines from Log::Agent that you don't want to change, export your own routines, and give your own routines access to Log::Agent routines (probably both those that you haven't replaced and those that you have).

    I've thought of many ways to do that but I think perhaps the easiest of them is something like:

    # import _all_ that Log::Agent exports: use Log::Agent qw( /./ ); # Have us export the same stuff Log::Agent does: sub import { my( $selfPkg )= shift( @_ ); local( $Exporter::ExportLevel )= 1 + $Exporter::ExportLevel; Log::Agent->import( @_ ); } # For each routine we redefine, unimport the function # before we redefine it in order to avoid a warning: BEGIN { undef &logcarp; } sub logcarp { # preprocess stuff Log::Agent::logcarp( @_ ); # postprocess stuff }
    But that is still abusing Log::Agent in ways that make your module sensitive to breaking for future versions of Log::Agent.

            - tye (but my friends call me "Tye")
Re: Inheritance and exported methods
by Ido (Hermit) on May 07, 2002 at 15:27 UTC
    The following seems like an evil code to me..But it might help:
    $My::Log::{$_}=$Log::Agent::{$_} for keys %Log::Agent::;
    It'll make aliases in My::Log to everything exists in Log::Agent..