| 1 |
=head1 NAME |
| 2 |
|
| 3 |
Coro::Cont - continuations in perl |
| 4 |
|
| 5 |
=head1 SYNOPSIS |
| 6 |
|
| 7 |
use Coro::Cont; |
| 8 |
|
| 9 |
# multiply all hash keys by 2 |
| 10 |
my $cont = csub { |
| 11 |
yield $_*2; |
| 12 |
yield $_; |
| 13 |
}; |
| 14 |
my %hash2 = map &$cont, %hash1; |
| 15 |
|
| 16 |
# dasselbe in grĂ¼n (as the germans say) |
| 17 |
sub mul2 : Cont { |
| 18 |
yield $_[0]*2; |
| 19 |
yield $_[0]; |
| 20 |
} |
| 21 |
|
| 22 |
my %hash2 = map mul2($_), %hash1; |
| 23 |
|
| 24 |
=head1 DESCRIPTION |
| 25 |
|
| 26 |
=over 4 |
| 27 |
|
| 28 |
=cut |
| 29 |
|
| 30 |
package Coro::Cont; |
| 31 |
|
| 32 |
BEGIN { eval { require warnings } && warnings->unimport } |
| 33 |
|
| 34 |
use Carp qw(croak); |
| 35 |
|
| 36 |
use Coro::State; |
| 37 |
|
| 38 |
use vars qw($return); |
| 39 |
|
| 40 |
use base 'Exporter'; |
| 41 |
|
| 42 |
$VERSION = 1.9; |
| 43 |
@EXPORT = qw(csub yield); |
| 44 |
|
| 45 |
{ |
| 46 |
my @csub; |
| 47 |
|
| 48 |
# this way of handling attributes simply is NOT scalable ;() |
| 49 |
sub import { |
| 50 |
Coro::Cont->export_to_level(1, @_); |
| 51 |
my $old = *{(caller)[0]."::MODIFY_CODE_ATTRIBUTES"}{CODE}; |
| 52 |
*{(caller)[0]."::MODIFY_CODE_ATTRIBUTES"} = sub { |
| 53 |
my ($package, $ref) = (shift, shift); |
| 54 |
my @attrs; |
| 55 |
for (@_) { |
| 56 |
if ($_ eq "Cont") { |
| 57 |
push @csub, [$package, $ref]; |
| 58 |
} else { |
| 59 |
push @attrs, $_; |
| 60 |
} |
| 61 |
} |
| 62 |
return $old ? $old->($package, $ref, @attrs) : @attrs; |
| 63 |
}; |
| 64 |
} |
| 65 |
|
| 66 |
sub findsym { |
| 67 |
my ($pkg, $ref) = @_; |
| 68 |
my $type = ref $ref; |
| 69 |
for my $sym (values %{$pkg."::"}) { |
| 70 |
return \$sym if *{$sym}{$type} == $ref; |
| 71 |
} |
| 72 |
(); |
| 73 |
} |
| 74 |
|
| 75 |
sub INIT { |
| 76 |
# prototypes are currently being ignored |
| 77 |
for (@csub) { |
| 78 |
my $ref = findsym(@$_) |
| 79 |
or croak "package $package: cannot declare non-global subs as 'Cont'"; |
| 80 |
*$ref = &csub($_->[1]); |
| 81 |
} |
| 82 |
@csub = (); |
| 83 |
} |
| 84 |
} |
| 85 |
|
| 86 |
=item csub { ... } |
| 87 |
|
| 88 |
Create a new "continuation" (when the sub falls of the end it is being |
| 89 |
terminated). |
| 90 |
|
| 91 |
=cut |
| 92 |
|
| 93 |
sub csub(&) { |
| 94 |
my $code = $_[0]; |
| 95 |
my $prev = new Coro::State; |
| 96 |
|
| 97 |
my $coro = new Coro::State sub { |
| 98 |
# we do this superfluous switch just to |
| 99 |
# avoid the parameter passing problem |
| 100 |
# on the first call |
| 101 |
&yield; |
| 102 |
&$code while 1; |
| 103 |
}; |
| 104 |
|
| 105 |
# call it once |
| 106 |
push @{ $Coro::current->{yieldstack} }, [$coro, $prev]; |
| 107 |
&Coro::State::transfer($prev, $coro, 0); |
| 108 |
|
| 109 |
sub { |
| 110 |
push @{ $Coro::current->{yieldstack} }, [$coro, $prev]; |
| 111 |
&Coro::State::transfer($prev, $coro, 0); |
| 112 |
wantarray ? @_ : $_[0]; |
| 113 |
}; |
| 114 |
} |
| 115 |
|
| 116 |
=item @_ = yield [list] |
| 117 |
|
| 118 |
Return the given list/scalar as result of the continuation. Also returns |
| 119 |
the new arguments given to the subroutine on the next call. |
| 120 |
|
| 121 |
=cut |
| 122 |
|
| 123 |
# implemented in Coro/State.xs |
| 124 |
#sub yield(@) { |
| 125 |
# &Coro::State::transfer(@{pop @$$return}, 0); |
| 126 |
# wantarray ? @_ : $_[0]; |
| 127 |
#} |
| 128 |
|
| 129 |
1; |
| 130 |
|
| 131 |
=back |
| 132 |
|
| 133 |
=head1 AUTHOR |
| 134 |
|
| 135 |
Marc Lehmann <schmorp@schmorp.de> |
| 136 |
http://home.schmorp.de/ |
| 137 |
|
| 138 |
=cut |
| 139 |
|