package BCast;

sub spawn {
  my ($package, $alias) = @_;
  my $self = $package->new($alias);
  POE::Session->create(
    object_states => [$self => [qw(_start xmit join split)],],);
}

sub new {
  my ($package, $alias) = @_;
  return bless {alias => $alias}, $package;
}

sub _start {
  my ($kernel, $self) = @_[KERNEL, OBJECT];
  $kernel->alias_set($self->{alias});
  $self->{reg} = [];
}

sub xmit {
  my ($kernel, $self, $something) = @_[KERNEL, OBJECT, ARG0];
  foreach my $s (@{$self->{reg}}) {
    $kernel->post(@$s, $something);
  }
}

sub join {
  my ($kernel, $self, $something) = @_[KERNEL, OBJECT, ARG0];
  push @{$self->{reg}}, $something;
}

sub split {
  my ($kernel, $self, $something) = @_[KERNEL, OBJECT, ARG0];
  $self->{reg} =
    [grep { $_->[0] ne $something->[0] or $_->[1] ne $something->[1] }
      @{$self->{reg}}];
}

With this form, it becomes dead simple to reuse code. Simply overload the the package:

package BCast::Once;
@ISA = qw(BCast);

sub join {
  my ($sender, $self, $something) = @_[SENDER, OBJECT, ARG0];
  $self->{joined}{$sender->ID}{$something} = 1;
  delete $self->{reg};
}

sub split {
  my ($sender, $self, $something) = @_[SENDER, OBJECT, ARG0];
  my $sender = $sender->ID;
  if ($something) {
    delete $self->{already}{$sender}{$something};
    delete $self->{already}{$sender}
      unless keys %{$self->{already}{$sender}};
  }
  else {
    delete $self->{already}{$sender};
  }
  delete $self->{reg};
}

sub xmit {
  my $self = shift @_;
  $self->{reg} ||= [map { keys %$_ } keys %{$self->{already}}];
  $self->SUPER::xmit(@_);
}

This looks stunningly similar /Package Methods, only HEAP and $heap have been replaced with OBJECT and $self.

Advantages

Disadvantages

Notes

I found that it can be easy to form circular references - if you store the session object in the object whose methods you are using for states.

For example, I tend to create and store session objects in my own objects:

sub new {
  my ($package, $alias) = @_;
  my $self = bless {alias => $alias}, $package;
  $self->{session} =
    POE::Session->create(
    object_states => [$self => [qw(_start _stop xmit join split)],],);
  $self;
}

# ... as above ...
# now, don't forget to do this !
# it breaks the reference-cycle
sub _stop {
  delete $_[OBJECT]->{session};
}

I know its not necessarily great form to keep the session object reference, but I do.

--Lachie

Of course, you could use [weak references] instead.

--Merlyn

See also: /Anonymous Inline Subrefs, /Inline Subrefs, /Package Methods moved here