}
}
-# this is our global stash of both
+# this is our global stash of both
# MRO's and method dispatch tables
# the structure basically looks like
# this:
return if $TURN_OFF_C3;
mro::set_mro($class, 'c3') if $C3_IN_CORE;
- # make a note to calculate $class
+ # make a note to calculate $class
# during INIT phase
$MRO{$class} = undef unless exists $MRO{$class};
}
sub uninitialize {
# why bother if we don't have anything ...
%next::METHOD_CACHE = ();
- return unless keys %MRO;
+ return unless keys %MRO;
if($C3_IN_CORE) {
mro::set_mro($_, 'dfs') for keys %MRO;
}
else {
- _remove_method_dispatch_tables();
+ _remove_method_dispatch_tables();
$_initialized = 0;
}
}
$MRO{$class} = { MRO => \@MRO };
my $has_overload_fallback;
my %methods;
- # NOTE:
+ # NOTE:
# we do @MRO[1 .. $#MRO] here because it
# makes no sense to interogate the class
- # which you are calculating for.
+ # which you are calculating for.
foreach my $local (@MRO[1 .. $#MRO]) {
- # if overload has tagged this module to
+ # if overload has tagged this module to
# have use "fallback", then we want to
- # grab that value
- $has_overload_fallback = ${"${local}::()"}
+ # grab that value
+ $has_overload_fallback = ${"${local}::()"}
if !defined $has_overload_fallback && defined ${"${local}::()"};
foreach my $method (grep { defined &{"${local}::$_"} } keys %{"${local}::"}) {
# skip if already overriden in local class
code => \&{"${local}::$method"}
} unless exists $methods{$method};
}
- }
+ }
# now stash them in our %MRO table
- $MRO{$class}->{methods} = \%methods;
- $MRO{$class}->{has_overload_fallback} = $has_overload_fallback;
+ $MRO{$class}->{methods} = \%methods;
+ $MRO{$class}->{has_overload_fallback} = $has_overload_fallback;
}
sub _apply_method_dispatch_tables {
return if $C3_IN_CORE;
foreach my $class (keys %MRO) {
_apply_method_dispatch_table($class);
- }
+ }
}
sub _apply_method_dispatch_table {
${"${class}::$method"} = $$orig if defined $$orig;
}
*{"${class}::$method"} = $MRO{$class}->{methods}->{$method}->{code};
- }
+ }
}
sub _remove_method_dispatch_tables {
return if $C3_IN_CORE;
my $class = shift;
no strict 'refs';
- delete ${"${class}::"}{"()"} if $MRO{$class}->{has_overload_fallback};
+ delete ${"${class}::"}{"()"} if $MRO{$class}->{has_overload_fallback};
foreach my $method (keys %{$MRO{$class}->{methods}}) {
delete ${"${class}::"}{$method}
- if defined *{"${class}::${method}"}{CODE} &&
- (*{"${class}::${method}"}{CODE} eq $MRO{$class}->{methods}->{$method}->{code});
+ if defined *{"${class}::${method}"}{CODE} &&
+ (*{"${class}::${method}"}{CODE} eq $MRO{$class}->{methods}->{$method}->{code});
}
}
sub calculateMRO {
my ($class, $merge_cache) = @_;
- return Algorithm::C3::merge($class, sub {
- no strict 'refs';
+ return Algorithm::C3::merge($class, sub {
+ no strict 'refs';
@{$_[0] . '::ISA'};
}, $merge_cache);
}
=head1 SYNOPSIS
package A;
- use Class::C3;
+ use Class::C3;
sub hello { 'A::hello' }
package B;
use base 'A';
- use Class::C3;
+ use Class::C3;
package C;
use base 'A';
- use Class::C3;
+ use Class::C3;
sub hello { 'C::hello' }
package D;
use base ('B', 'C');
- use Class::C3;
+ use Class::C3;
# Classic Diamond MI pattern
# <A>
# <D>
package main;
-
- # initializez the C3 module
+
+ # initializez the C3 module
# (formerly called in INIT)
- Class::C3::initialize();
+ Class::C3::initialize();
print join ', ' => Class::C3::calculateMRO('Diamond_D') # prints D, B, C, A
print D->hello() # prints 'C::hello' instead of the standard p5 'A::hello'
-
+
D->can('hello')->(); # can() also works correctly
UNIVERSAL::can('D', 'hello'); # as does UNIVERSAL::can()
=head1 DESCRIPTION
-This is pragma to change Perl 5's standard method resolution order from depth-first left-to-right
-(a.k.a - pre-order) to the more sophisticated C3 method resolution order.
+This is pragma to change Perl 5's standard method resolution order from depth-first left-to-right
+(a.k.a - pre-order) to the more sophisticated C3 method resolution order.
=head2 What is C3?
C3 is the name of an algorithm which aims to provide a sane method resolution order under multiple
inheritence. It was first introduced in the langauge Dylan (see links in the L<SEE ALSO> section),
-and then later adopted as the prefered MRO (Method Resolution Order) for the new-style classes in
-Python 2.3. Most recently it has been adopted as the 'canonical' MRO for Perl 6 classes, and the
+and then later adopted as the prefered MRO (Method Resolution Order) for the new-style classes in
+Python 2.3. Most recently it has been adopted as the 'canonical' MRO for Perl 6 classes, and the
default MRO for Parrot objects as well.
=head2 How does C3 work.
-C3 works by always preserving local precendence ordering. This essentially means that no class will
+C3 works by always preserving local precendence ordering. This essentially means that no class will
appear before any of it's subclasses. Take the classic diamond inheritence pattern for instance:
<A>
\ /
<D>
-The standard Perl 5 MRO would be (D, B, A, C). The result being that B<A> appears before B<C>, even
-though B<C> is the subclass of B<A>. The C3 MRO algorithm however, produces the following MRO
+The standard Perl 5 MRO would be (D, B, A, C). The result being that B<A> appears before B<C>, even
+though B<C> is the subclass of B<A>. The C3 MRO algorithm however, produces the following MRO
(D, B, C, A), which does not have this same issue.
This example is fairly trival, for more complex examples and a deeper explaination, see the links in
=head2 How does this module work?
-This module uses a technique similar to Perl 5's method caching. When C<Class::C3::initialize> is
-called, this module calculates the MRO of all the classes which called C<use Class::C3>. It then
-gathers information from the symbol tables of each of those classes, and builds a set of method
-aliases for the correct dispatch ordering. Once all these C3-based method tables are created, it
-then adds the method aliases into the local classes symbol table.
+This module uses a technique similar to Perl 5's method caching. When C<Class::C3::initialize> is
+called, this module calculates the MRO of all the classes which called C<use Class::C3>. It then
+gathers information from the symbol tables of each of those classes, and builds a set of method
+aliases for the correct dispatch ordering. Once all these C3-based method tables are created, it
+then adds the method aliases into the local classes symbol table.
The end result is actually classes with pre-cached method dispatch. However, this caching does not
do well if you start changing your C<@ISA> or messing with class symbol tables, so you should consider
=head1 OPTIONAL LOWERCASE PRAGMA
-This release also includes an optional module B<c3> in the F<opt/> folder. I did not include this in
+This release also includes an optional module B<c3> in the F<opt/> folder. I did not include this in
the regular install since lowercase module names are considered I<"bad"> by some people. However I
think that code looks much nicer like this:
package MyClass;
use c3;
-
+
The the more clunky:
package MyClass;
use Class::C3;
-
+
But hey, it's your choice, thats why it is optional.
=head1 FUNCTIONS
=item B<initialize>
-This B<must be called> to initalize the C3 method dispatch tables, this module B<will not work> if
-you do not do this. It is advised to do this as soon as possible B<after> loading any classes which
+This B<must be called> to initalize the C3 method dispatch tables, this module B<will not work> if
+you do not do this. It is advised to do this as soon as possible B<after> loading any classes which
use C3. Here is a quick code example:
-
+
package Foo;
use Class::C3;
# ... Foo methods here
-
+
package Bar;
use Class::C3;
use base 'Foo';
# ... Bar methods here
-
+
package main;
-
+
Class::C3::initialize(); # now it is safe to use Foo and Bar
-This function used to be called automatically for you in the INIT phase of the perl compiler, but
-that lead to warnings if this module was required at runtime. After discussion with my user base
-(the L<DBIx::Class> folks), we decided that calling this in INIT was more of an annoyance than a
-convience. I apologize to anyone this causes problems for (although i would very suprised if I had
-any other users other than the L<DBIx::Class> folks). The simplest solution of course is to define
-your own INIT method which calls this function.
+This function used to be called automatically for you in the INIT phase of the perl compiler, but
+that lead to warnings if this module was required at runtime. After discussion with my user base
+(the L<DBIx::Class> folks), we decided that calling this in INIT was more of an annoyance than a
+convience. I apologize to anyone this causes problems for (although i would very suprised if I had
+any other users other than the L<DBIx::Class> folks). The simplest solution of course is to define
+your own INIT method which calls this function.
-NOTE:
+NOTE:
If C<initialize> detects that C<initialize> has already been executed, it will L</uninitialize> and
clear the MRO cache first.
=item B<uninitialize>
Calling this function results in the removal of all cached methods, and the restoration of the old Perl 5
-style dispatch order (depth-first, left-to-right).
+style dispatch order (depth-first, left-to-right).
=item B<reinitialize>
=head1 METHOD REDISPATCHING
-It is always useful to be able to re-dispatch your method call to the "next most applicable method". This
-module provides a pseudo package along the lines of C<SUPER::> or C<NEXT::> which will re-dispatch the
+It is always useful to be able to re-dispatch your method call to the "next most applicable method". This
+module provides a pseudo package along the lines of C<SUPER::> or C<NEXT::> which will re-dispatch the
method along the C3 linearization. This is best show with an examples.
# a classic diamond MI pattern ...
<B> <C>
\ /
<D>
-
+
package A;
- use c3;
- sub foo { 'A::foo' }
-
+ use c3;
+ sub foo { 'A::foo' }
+
package B;
- use base 'A';
- use c3;
- sub foo { 'B::foo => ' . (shift)->next::method() }
-
+ use base 'A';
+ use c3;
+ sub foo { 'B::foo => ' . (shift)->next::method() }
+
package B;
- use base 'A';
- use c3;
- sub foo { 'C::foo => ' . (shift)->next::method() }
-
+ use base 'A';
+ use c3;
+ sub foo { 'C::foo => ' . (shift)->next::method() }
+
package D;
- use base ('B', 'C');
- use c3;
- sub foo { 'D::foo => ' . (shift)->next::method() }
-
+ use base ('B', 'C');
+ use c3;
+ sub foo { 'D::foo => ' . (shift)->next::method() }
+
print D->foo; # prints out "D::foo => B::foo => C::foo => A::foo"
-A few things to note. First, we do not require you to add on the method name to the C<next::method>
-call (this is unlike C<NEXT::> and C<SUPER::> which do require that). This helps to enforce the rule
-that you cannot dispatch to a method of a different name (this is how C<NEXT::> behaves as well).
+A few things to note. First, we do not require you to add on the method name to the C<next::method>
+call (this is unlike C<NEXT::> and C<SUPER::> which do require that). This helps to enforce the rule
+that you cannot dispatch to a method of a different name (this is how C<NEXT::> behaves as well).
-The next thing to keep in mind is that you will need to pass all arguments to C<next::method> it can
-not automatically use the current C<@_>.
+The next thing to keep in mind is that you will need to pass all arguments to C<next::method> it can
+not automatically use the current C<@_>.
If C<next::method> cannot find a next method to re-dispatch the call to, it will throw an exception.
You can use C<next::can> to see if C<next::method> will succeed before you call it like so:
- $self->next::method(@_) if $self->next::can;
+ $self->next::method(@_) if $self->next::can;
-Additionally, you can use C<maybe::next::method> as a shortcut to only call the next method if it exists.
+Additionally, you can use C<maybe::next::method> as a shortcut to only call the next method if it exists.
The previous example could be simply written as:
$self->maybe::next::method(@_);
=head1 CAVEATS
-This module used to be labeled as I<experimental>, however it has now been pretty heavily tested by
-the good folks over at L<DBIx::Class> and I am confident this module is perfectly usable for
-whatever your needs might be.
+This module used to be labeled as I<experimental>, however it has now been pretty heavily tested by
+the good folks over at L<DBIx::Class> and I am confident this module is perfectly usable for
+whatever your needs might be.
But there are still caveats, so here goes ...
=item Use of C<SUPER::>.
The idea of C<SUPER::> under multiple inheritence is ambigious, and generally not recomended anyway.
-However, it's use in conjuntion with this module is very much not recommended, and in fact very
+However, it's use in conjuntion with this module is very much not recommended, and in fact very
discouraged. The recommended approach is to instead use the supplied C<next::method> feature, see
more details on it's usage above.
It is the author's opinion that changing C<@ISA> at runtime is pure insanity anyway. However, people
do it, so I must caveat. Any changes to the C<@ISA> will not be reflected in the MRO calculated by this
-module, and therefor probably won't even show up. If you do this, you will need to call C<reinitialize>
+module, and therefor probably won't even show up. If you do this, you will need to call C<reinitialize>
in order to recalulate B<all> method dispatch tables. See the C<reinitialize> documentation and an example
in F<t/20_reinitialize.t> for more information.
=item Adding/deleting methods from class symbol tables.
-This module calculates the MRO for each requested class by interogatting the symbol tables of said classes.
-So any symbol table manipulation which takes place after our INIT phase is run will not be reflected in
-the calculated MRO. Just as with changing the C<@ISA>, you will need to call C<reinitialize> for any
+This module calculates the MRO for each requested class by interogatting the symbol tables of said classes.
+So any symbol table manipulation which takes place after our INIT phase is run will not be reflected in
+the calculated MRO. Just as with changing the C<@ISA>, you will need to call C<reinitialize> for any
changes you make to take effect.
=item Calling C<next::method> from methods defined outside the class
-There is an edge case when using C<next::method> from within a subroutine which was created in a different
-module than the one it is called from. It sounds complicated, but it really isn't. Here is an example which
+There is an edge case when using C<next::method> from within a subroutine which was created in a different
+module than the one it is called from. It sounds complicated, but it really isn't. Here is an example which
will not work correctly:
*Foo::foo = sub { (shift)->next::method(@_) };
-The problem exists because the anonymous subroutine being assigned to the glob C<*Foo::foo> will show up
-in the call stack as being called C<__ANON__> and not C<foo> as you might expect. Since C<next::method>
-uses C<caller> to find the name of the method it was called in, it will fail in this case.
+The problem exists because the anonymous subroutine being assigned to the glob C<*Foo::foo> will show up
+in the call stack as being called C<__ANON__> and not C<foo> as you might expect. Since C<next::method>
+uses C<caller> to find the name of the method it was called in, it will fail in this case.
-But fear not, there is a simple solution. The module C<Sub::Name> will reach into the perl internals and
+But fear not, there is a simple solution. The module C<Sub::Name> will reach into the perl internals and
assign a name to an anonymous subroutine for you. Simply do this:
-
+
use Sub::Name 'subname';
*Foo::foo = subname 'Foo::foo' => sub { (shift)->next::method(@_) };
-and things will Just Work. Of course this is not always possible to do, but to be honest, I just can't
-manage to find a workaround for it, so until someone gives me a working patch this will be a known
+and things will Just Work. Of course this is not always possible to do, but to be honest, I just can't
+manage to find a workaround for it, so until someone gives me a working patch this will be a known
limitation of this module.
=back
=item L<http://www.call-with-current-continuation.org/eggs/c3.html>
-=back
+=back
=head1 ACKNOWLEGEMENTS
=over 4
-=item Thanks to Matt S. Trout for using this module in his module L<DBIx::Class>
+=item Thanks to Matt S. Trout for using this module in his module L<DBIx::Class>
and finding many bugs and providing fixes.
-=item Thanks to Justin Guenther for making C<next::method> more robust by handling
+=item Thanks to Justin Guenther for making C<next::method> more robust by handling
calls inside C<eval> and anon-subs.
-=item Thanks to Robert Norris for adding support for C<next::can> and
+=item Thanks to Robert Norris for adding support for C<next::can> and
C<maybe::next::method>.
=back
L<http://www.iinteractive.com>
This library is free software; you can redistribute it and/or modify
-it under the same terms as Perl itself.
+it under the same terms as Perl itself.
=cut