18
21
$USE_XS = $ENV{CAG_USE_XS}
19
22
unless defined $USE_XS;
24
return if $xsa_loaded++;
25
require Class::XSAccessor;
26
Class::XSAccessor->VERSION($__minimum_xsa_version);
30
if (defined $USE_XS) {
31
$load_xsa->() if ($USE_XS && ! $xsa_loaded);
37
# Class::XSAccessor is segfaulting on win32, in some
38
# esoteric heavily-threaded scenarios
39
# Win32 users can set $USE_XS/CAG_USE_XS to try to use it anyway
40
if ($^O ne 'MSWin32') {
42
eval { $load_xsa->(); $USE_XS = 1 };
24
# Yes this method is undocumented
25
# Yes it should be a private coderef like all the rest at the end of this file
26
# No we can't do that (yet) because the DBIC-CDBI compat layer overrides it
28
sub _mk_group_accessors {
29
my($self, $maker, $group, @fields) = @_;
30
my $class = Scalar::Util::blessed $self || $self;
33
no warnings 'redefine';
35
# So we don't have to do lots of lookups inside the loop.
36
$maker = $self->can($maker) unless ref $maker;
39
if( $_ eq 'DESTROY' ) {
40
Carp::carp("Having a data accessor named DESTROY in ".
41
"'$class' is unwise.");
44
my ($name, $field) = (ref $_)
49
my $alias = "_${name}_accessor";
51
for my $meth ($name, $alias) {
53
# the maker may elect to not return anything, meaning it already
54
# installed the coderef for us (e.g. lack of Sub::Name)
55
my $cref = $self->$maker($group, $field, $meth)
58
my $fq_meth = "${class}::${meth}";
60
*$fq_meth = Sub::Name::subname($fq_meth, $cref);
61
#unless defined &{$class."\:\:$field"}
66
# coderef is setup at the end for clarity
86
107
sub mk_group_accessors {
87
my ($self, $group, @fields) = @_;
89
$self->_mk_group_accessors('make_group_accessor', $group, @fields);
96
no warnings 'redefine';
98
sub _mk_group_accessors {
99
my($self, $maker, $group, @fields) = @_;
100
my $class = Scalar::Util::blessed $self || $self;
102
# So we don't have to do lots of lookups inside the loop.
103
$maker = $self->can($maker) unless ref $maker;
106
if( $_ eq 'DESTROY' ) {
107
Carp::carp("Having a data accessor named DESTROY in ".
108
"'$class' is unwise.");
111
my ($name, $field) = (ref $_)
116
my $alias = "_${name}_accessor";
118
for my $meth ($name, $alias) {
120
# the maker may elect to not return anything, meaning it already
121
# installed the coderef for us
122
my $cref = $self->$maker($group, $field, $meth)
125
my $fq_meth = join('::', $class, $meth);
127
*$fq_meth = Sub::Name::subname($fq_meth, $cref);
128
#unless defined &{$class."\:\:$field"}
108
my ($self, $group, @fields) = @_;
110
$self->_mk_group_accessors('make_group_accessor', $group, @fields);
134
114
=head2 mk_group_ro_accessors
517
404
return @{mro::get_linear_isa( ref($_[0]) || $_[0] )};
522
407
=head1 PERFORMANCE
524
409
To provide total flexibility L<Class::Accessor::Grouped> calls methods
525
410
internally while performing get/set actions, which makes it noticeably
526
411
slower than similar modules. To compensate, this module will automatically
527
412
use the insanely fast L<Class::XSAccessor> to generate the C<simple>-group
528
accessors, if L<< Class::XSAccessor >= 1.06|Class::XSAccessor >> is
529
available on your system.
413
accessors if this module is available on your system.
533
417
This is the result of a set/get/set loop benchmark on perl 5.12.1 with
534
418
thread support, showcasing most popular accessor builders: L<Moose>, L<Mouse>,
535
L<CAF|Class::Accessor::Fast>, L<CAF_XS|Class::Accessor::Fast::XS>
536
and L<XSA|Class::XSAccessor>:
419
L<Moo>, L<CAF|Class::Accessor::Fast>, L<CAF_XS|Class::Accessor::Fast::XS>,
420
L<XSA|Class::XSAccessor>, and L<CAF_XSA|Class::XSAccessor::Compat>:
538
Rate CAG moOse CAF HANDMADE CAF_XS moUse_XS CAG_XS XSA
539
CAG 1777/s -- -27% -29% -36% -62% -67% -72% -73%
540
moOse 2421/s 36% -- -4% -13% -48% -55% -61% -63%
541
CAF 2511/s 41% 4% -- -10% -47% -53% -60% -61%
542
HANDMADE 2791/s 57% 15% 11% -- -41% -48% -56% -57%
543
CAF_XS 4699/s 164% 94% 87% 68% -- -13% -25% -28%
544
moUse_XS 5375/s 203% 122% 114% 93% 14% -- -14% -18%
545
CAG_XS 6279/s 253% 159% 150% 125% 34% 17% -- -4%
546
XSA 6515/s 267% 169% 159% 133% 39% 21% 4% --
422
Rate CAG moOse CAF moUse moo HANDMADE CAF_XS moUse_XS moo_XS CAF_XSA XSA CAG_XS
423
CAG 169/s -- -21% -24% -32% -32% -34% -59% -63% -67% -67% -67% -67%
424
moOse 215/s 27% -- -3% -13% -13% -15% -48% -53% -58% -58% -58% -58%
425
CAF 222/s 31% 3% -- -10% -10% -13% -46% -52% -57% -57% -57% -57%
426
moUse 248/s 46% 15% 11% -- -0% -3% -40% -46% -52% -52% -52% -52%
427
moo 248/s 46% 15% 11% 0% -- -3% -40% -46% -52% -52% -52% -52%
428
HANDMADE 255/s 50% 18% 14% 3% 3% -- -38% -45% -50% -51% -51% -51%
429
CAF_XS 411/s 143% 91% 85% 66% 66% 61% -- -11% -20% -20% -21% -21%
430
moUse_XS 461/s 172% 114% 107% 86% 86% 81% 12% -- -10% -11% -11% -11%
431
moo_XS 514/s 204% 139% 131% 107% 107% 102% 25% 12% -- -0% -1% -1%
432
CAF_XSA 516/s 205% 140% 132% 108% 108% 103% 26% 12% 0% -- -0% -0%
433
XSA 519/s 206% 141% 133% 109% 109% 104% 26% 13% 1% 0% -- -0%
434
CAG_XS 519/s 206% 141% 133% 109% 109% 104% 26% 13% 1% 0% 0% --
548
436
Benchmark program is available in the root of the
549
437
L<repository|http://search.cpan.org/dist/Class-Accessor-Grouped/>:
551
439
=head2 Notes on Class::XSAccessor
553
While L<Class::XSAccessor> works surprisingly well for the amount of black
554
magic it tries to pull off, it's still black magic. At present (Sep 2010)
555
the module is known to have problems on Windows under heavy thread-stress
556
(e.g. Win32+Apache+mod_perl). Thus for the time being L<Class::XSAccessor>
557
will not be used automatically if you are running under C<MSWin32>.
559
You can force the use of L<Class::XSAccessor> before creating a particular
560
C<simple> accessor by either manipulating the global variable
561
C<$Class::Accessor::Grouped::USE_XS>, or you can do so before runtime via the
441
You can force (or disable) the use of L<Class::XSAccessor> before creating a
442
particular C<simple> accessor by either manipulating the global variable
443
C<$Class::Accessor::Grouped::USE_XS> to true or false (preferably with
444
L<localization|perlfunc/local>, or you can do so before runtime via the
562
445
C<CAG_USE_XS> environment variable.
447
Since L<Class::XSAccessor> has no knowledge of L</get_simple> and
448
L</set_simple> this module does its best to detect if you are overriding
449
one of these methods and will fall back to using the perl version of the
450
accessor in order to maintain consistency. However be aware that if you
451
enable use of C<Class::XSAccessor> (automatically or explicitly), create
452
an object, invoke a simple accessor on that object, and B<then> manipulate
453
the symbol table to install a C<get/set_simple> override - you get to keep
566
458
Matt S. Trout <mst@shadowcatsystems.co.uk>
586
478
it under the same terms as perl itself.
482
########################################################################
483
########################################################################
484
########################################################################
486
# Here be many angry dragons
487
# (all code is in private coderefs since everything inherits CAG)
489
########################################################################
490
########################################################################
494
die "Huh?! No minimum C::XSA version?!\n"
495
unless $__minimum_xsa_version;
501
$err = eval { require Sub::Name; 1; } ? undef : do {
502
delete $INC{'Sub/Name.pm'}; # because older perls suck
505
*__CAG_NO_SUBNAME = $err
512
require Class::XSAccessor;
513
Class::XSAccessor->VERSION($__minimum_xsa_version);
517
delete $INC{'Sub/Name.pm'}; # because older perls suck
518
delete $INC{'Class/XSAccessor.pm'};
521
*__CAG_NO_CXSA = $err
527
*__CAG_BROKEN_GOTO = ($] < '5.008009')
533
*__CAG_UNSTABLE_DOLLARAT = ($] < '5.013002')
539
*__CAG_TRACK_UNDEFER_FAIL = (
540
$INC{'Test/Builder.pm'} || $INC{'Test/Builder2.pm'}
542
$0 =~ m|^ x?t / .+ \.t $|x
548
# Autodetect unless flag supplied
549
my $xsa_autodetected;
550
if (! defined $USE_XS) {
551
$USE_XS = __CAG_NO_CXSA ? 0 : 1;
555
my $maker_templates = {
557
xs_call => 'accessors',
559
my $set = "set_$_[0]";
560
my $get = "get_$_[0]";
566
? shift->$set('$field', \@_)
567
: shift->$get('$field')
572
xs_call => 'getters',
574
my $get = "get_$_[0]";
580
? shift->$get('$field')
582
my \$caller = caller;
583
my \$class = ref \$_[0] || \$_[0];
584
Carp::croak(\"'\$caller' cannot alter the value of '$field' \".
585
\"(read-only attributes of class '\$class')\");
591
xs_call => 'setters',
593
my $set = "set_$_[0]";
599
? shift->$set('$field', \@_)
601
my \$caller = caller;
602
my \$class = ref \$_[0] || \$_[0];
603
Carp::croak(\"'\$caller' cannot access the value of '$field' \".
604
\"(write-only attributes of class '\$class')\");
612
my ($accessor_maker_cache, $no_xsa_warned_classes);
614
# can't use pkg_gen to track this stuff, as it doesn't
615
# detect superclass mucking
616
my $original_simple_getter = __PACKAGE__->can ('get_simple');
617
my $original_simple_setter = __PACKAGE__->can ('set_simple');
619
# Note!!! Unusual signature
620
$gen_accessor = sub {
621
my ($type, $class, $group, $field, $methname) = @_;
622
if (my $c = Scalar::Util::blessed( $class )) {
626
# When installing an XSA simple accessor, we need to make sure we are not
627
# short-circuiting a (compile or runtime) get_simple/set_simple override.
628
# What we do here is install a lazy first-access check, which will decide
629
# the ultimate coderef being placed in the accessor slot
631
# Also note that the *original* class will always retain this shim, as
632
# different branches inheriting from it may have different overrides.
633
# Thus the final method (properly labeled and all) is installed in the
634
# calling-package's namespace
635
if ($USE_XS and $group eq 'simple') {
636
die sprintf( "Class::XSAccessor requested but not available:\n%s\n", __CAG_NO_CXSA )
639
my ($expected_cref, $cached_implementation);
640
my $ret = $expected_cref = sub {
641
my $current_class = Scalar::Util::blessed( $_[0] ) || $_[0];
643
# $cached_implementation will be set only if the shim got
644
# 'around'ed, in which case it is handy to avoid re-running
645
# this block over and over again
646
my $resolved_implementation = $cached_implementation->{$current_class} || do {
648
$current_class->can('get_simple') == $original_simple_getter
650
$current_class->can('set_simple') == $original_simple_setter
652
# nothing has changed, might as well use the XS crefs
654
# note that by the time this code executes, we already have
655
# *objects* (since XSA works on 'simple' only by definition).
656
# If someone is mucking with the symbol table *after* there
657
# are some objects already - look! many, shiny pieces! :)
659
# The weird breeder thingy is because XSA does not have an
660
# interface returning *just* a coderef, without installing it
662
Class::XSAccessor->import(
664
class => '__CAG__XSA__BREEDER__',
665
$maker_templates->{$type}{xs_call} => {
669
__CAG__XSA__BREEDER__->can($methname);
672
if (! $xsa_autodetected and ! $no_xsa_warned_classes->{$current_class}++) {
673
# not using Carp since the line where this happens doesn't mean much
674
warn 'Explicitly requested use of Class::XSAccessor disabled for objects of class '
675
. "'$current_class' inheriting from '$class' due to an overriden get_simple and/or "
680
# that's faster than local
682
my $c = $gen_accessor->($type, $class, 'simple', $field, $methname);
689
# if after this shim was created someone wrapped it with an 'around',
690
# we can not blindly reinstall the method slot - we will destroy the
691
# wrapper. Silently chain execution further...
692
if ( !$expected_cref or $expected_cref != $current_class->can($methname) ) {
694
# there is no point in re-determining it on every subsequent call,
695
# just store for future reference
696
$cached_implementation->{$current_class} ||= $resolved_implementation;
698
# older perls segfault if the cref behind the goto throws
699
# http://rt.perl.org/rt3/Public/Bug/Display.html?id=35878
700
return $resolved_implementation->(@_) if __CAG_BROKEN_GOTO;
702
goto $resolved_implementation;
705
if (__CAG_TRACK_UNDEFER_FAIL) {
706
my $deferred_calls_seen = do {
708
\%{"${current_class}::__cag_deferred_xs_shim_invocations"}
710
my @cframe = caller(0);
711
if (my $already_seen = $deferred_calls_seen->{$cframe[3]}) {
713
"Deferred version of method $cframe[3] invoked more than once (originally "
714
. "invoked at $already_seen). This is a strong indication your code has "
715
. 'cached the original ->can derived method coderef, and is using it instead '
716
. 'of the proper method re-lookup, causing performance regressions'
720
$deferred_calls_seen->{$cframe[3]} = "$cframe[1] line $cframe[2]";
724
# install the resolved implementation into the code slot so we do not
725
# come here anymore (hopefully)
726
# since XSAccessor was available - so is Sub::Name
729
no warnings 'redefine';
731
my $fq_name = "${current_class}::${methname}";
732
*$fq_name = Sub::Name::subname($fq_name, $resolved_implementation);
734
# need to update what the shim expects too *in case* its
735
# ->can was cached for some moronic reason
736
$expected_cref = $resolved_implementation;
737
Scalar::Util::weaken($expected_cref);
740
# older perls segfault if the cref behind the goto throws
741
# http://rt.perl.org/rt3/Public/Bug/Display.html?id=35878
742
return $resolved_implementation->(@_) if __CAG_BROKEN_GOTO;
744
goto $resolved_implementation;
747
Scalar::Util::weaken($expected_cref); # to break the self-reference
751
# no Sub::Name - just install the coderefs directly (compiling every time)
752
elsif (__CAG_NO_SUBNAME) {
753
my $src = $accessor_maker_cache->{source}{$type}{$group}{$field} ||=
754
$maker_templates->{$type}{pp_code}->($group, $field);
756
no warnings 'redefine';
757
local $@ if __CAG_UNSTABLE_DOLLARAT;
758
eval "sub ${class}::${methname} { $src }";
760
undef; # so that no further attempt will be made to install anything
763
# a coderef generator with a variable pad (returns a fresh cref on every invocation)
765
($accessor_maker_cache->{pp}{$type}{$group}{$field} ||= do {
766
my $src = $accessor_maker_cache->{source}{$type}{$group}{$field} ||=
767
$maker_templates->{$type}{pp_code}->($group, $field);
769
local $@ if __CAG_UNSTABLE_DOLLARAT;
770
eval "sub { my \$dummy; sub { \$dummy if 0; $src } }" or die $@;