modifying @ISA

  • Thread starter Gibbering Poster
  • Start date
G

Gibbering Poster

Hey .. i'm playing around with the silly idea (yah I know) of writing a
roguelike in Perl, and had a quick question about dynamic multiple
inheritance.

I have a main IO Handler object called Handler. When the game is started,
it checks your OS, and returns you an object of class Handler::Win or
Handler::Linux depending on what $^O contains. So far, so good.

The main Handler super-object contains methods to return objects like
player, map, etc for the child handler, so he can know how to paint the
screen and such.

I've decided that I'd like to be able to subclass the handler on levels
other than the OS. I'd like to have:

Handler::DungeonMovement
Handler::playerCreation
Handler::Inventory
Handler::prompt
etc....

What I was thinking I would do is morph the object as I needed to change the
handler object. For instance, When the game starts, the win32 handler adds
Handler::playerCreation to its @ISA list so that one can choose
name/class/etc ... Once that's complete, I was hoping I could remove
Handler::playerCreation from the @ISA list, and push on
Handler::DungeonMovement so that the process_key method would know how to
properly interprate the keystrokes to move the player around the dungeon.

I'm having a tough time dynamically modifying the Handler::Win32's @ISA list
for a blessed object instance...

Here's some code that demonstrates a constuctor for the main super Handler
class that will create the proper OS handler, then TRY to immediatly throw
them into 'movement' mode, by trying to twiddle @ISA:

sub new ($$) {
my ($handler, $mode) = @_;

if ($^O =~ /win/i) {
$handler = Handler::Win -> new;
} else {
$handler = Handler::Unix -> new;
}
if ($mode eq 'movement') {
push @$handler::ISA => 'Movement';
}
return $handler;
}

I KNOW that push line is ridiculously wrong syntactically, but I wanted to
give you an idea of what I was trying to do...
What's the proper syntax to do this, and is the whole architecture lame?

Thanks VERY much in advance for help :)
 
B

Brian McCauley

Gibbering said:
Hey .. i'm playing around with the silly idea (yah I know) of writing a
roguelike in Perl, and had a quick question about dynamic multiple
inheritance.

I have a main IO Handler object called Handler. When the game is started,
it checks your OS, and returns you an object of class Handler::Win or
Handler::Linux depending on what $^O contains. So far, so good.

No, but I'll explain why later.
The main Handler super-object contains methods to return objects like
player, map, etc for the child handler, so he can know how to paint the
screen and such.

I've decided that I'd like to be able to subclass the handler on levels
other than the OS. I'd like to have:

Handler::DungeonMovement
Handler::playerCreation
Handler::Inventory
Handler::prompt
etc....

What I was thinking I would do is morph the object as I needed to change the
handler object. For instance, When the game starts, the win32 handler adds
Handler::playerCreation to its @ISA list so that one can choose

No, you have got the wrong end of the stick.

@ISA describes (static) interhitance relationships between _classes_ not
_objects_. You should mess with it like this. If you have an object
that wants to morph from one class into a one of it's child classes you
can simply re-bless it although this can mke for rather incoprehensible
code.

In fact the only time you would usually have anything other than a
totally static @ISA is in cases like of your Handler::Linux and
Handler::Win32 as the OS is not going to change withing the lifetime of
the process.

Your basic Handler object should simply be of class Handler. Subclasses
should inherit from that.

i.e.

@Handler::DungeonMovement::ISA = qw( Handler );
# etc...

but the classes Handler::Linux and Handler::Win32 are not decendants of
Handler in the class hierachy - they are alternate parents!
Here's some code that demonstrates a constuctor for the main super Handler
class that will create the proper OS handler, then TRY to immediatly throw
them into 'movement' mode, by trying to twiddle @ISA:

sub new ($$) {

Constructors in Perl are conventionaly called as class methods. Methods
don't have prototypes in Perl.
my ($handler, $mode) = @_;

if ($^O =~ /win/i) {
$handler = Handler::Win -> new;
} else {
$handler = Handler::Unix -> new;
}
if ($mode eq 'movement') {
push @$handler::ISA => 'Movement';
}
return $handler;
}

I KNOW that push line is ridiculously wrong syntactically,

The syntax is not nearly as ridiculous as the semantics.

The Handler class should tune it's @ISA at compile time.

package Handler;
BEGIN {
if ($^O =~ /win/i) {
# May want to require Handler::Win;
@ISA = qw ( Handler::Win );
} else {
# May want to require Handler::Unix;
$handler = Handler::Unix -> new;
}
}

sub os_specific_init {
# Nothing!
}

sub new {
my $class = shift;
my $self = bless {}, $class;
$self->os_specific_init;
$self;
}


package Handler::Win;

sub os_specific_init {
my $self = shift;
# Do windows specific bit.
}

Or somesuch approach. As ever TIMTOWTDI.

Do not pass $mode as an argument to Handle->new.

Instead just call Handler::DungeonMovement->new.

This method call will in fact resolve as subroutine call
Handle::new('Handler::DungeonMovement') which will construct a
Handler::DungeonMovement object.
 
J

Joe Smith

Gibbering said:
Once that's complete, I was hoping I could remove
Handler::playerCreation from the @ISA list, and push on
Handler::DungeonMovement so that the process_key method would know how to
properly interprate the keystrokes to move the player around the dungeon.

That does not make sense, unless you are using code like:
$key = get_keystroke();
$handle->a() if $key eq 'a';
$handle->b() if $key eq 'b';
$handle->c() if $key eq 'c';

Diddling with @ISA is not the way to handle dispatch tables.

Create a hash with coderefs for each legal letter in PlayerCreation mode.
This will be used to map letters to subroutines.
Create another hash with coderefs for DungeonMovement mode.
Set your command loop to use one or the other depending on which mode
you're in. The selected hash can be used to determine which command
letters are legal in this mode, and which subroutine to invoke when
a legal letter is seen.

You could push and pop hash references onto an array to have stacked
dictionaries (like Postscript uses).

@dispatch = \%Help; # These commands are legal everywhere
push @dispatch, \%Create;
... # acceptable commands = union of Create + Help;
pop @dispatch;
push @dispatch, \%Movement;
... # Move around
push @dispatch, \%Inventory
... # acceptable commands = union of Inventory + Movement + Help
pop @dispatch;
... # acceptable commands = union of Movement + Help
pop @dispatch;

sub process_keystroke {
my $self = shift;
my $key = shift;
for my $n (1 .. @dispatch) {
my $href = $dispatch[-$n]; # Traverse from end to front
if (defined $href->{$key}) {
return $href->{$key}($self); # Invoke appropriate function
}
}
return unknown_command($self,$key);
}

-Joe
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,528
Members
45,000
Latest member
MurrayKeync

Latest Threads

Top