That is it for a basic object. You can do a lot more, but the fundamentals are all here. The above object is actually a Array reference (in the 'new' function), which is probably not standard. It can just as easily be done with a hash reference. The main reason I choose the array is arrays use about half the memory, and it is easier to enforce 'strict' coding. For more reasons on arrays vs hashes read The Perl Journal article 'Perl Heresies: Building Objects Out of Arrays' (Spring 1999) where Greg Bacon compares and contrasts the use of arrays and hashes as objects. Good article.package Complex; use Carp; #a '_' prefix on variables is a common way to indicate private # variables. (Note that in Perl nothing is actually private # ... well that is a lie  you can use encapsulation to make # variables private, so if you want really private variables # then go read Damian Conway's "Object Oriented Perl" ). sub _REAL {0} #_REAL is a constant '0' sub _IMAG {1} #_IMAG is a constant '1' sub new { my $class = shift; #get class name ('Complex') my $real = shift; #get first argument my $img = shift; #get second argument my $self = [$real, $img]; #create anon array with arguments bless $self, $class; #bless the array as a Complex object return $self; #return our new object } sub AUTOLOAD { my $sub = $AUTOLOAD; #get global $AUTOLOAD function name my $self = shift; #get the current object my $value = shift; #get an optional parameter $sub =~ s/.*://; #strip off package name #let the system handle 'DESTROY' function calls return if $sub =~ /DESTROY/; #make function call upper case to match our constants my $sub = uc($sub); #call the function (should be 'REAL' or 'IMAG' which will # return 0 or 1 respectively). The eval will trap runtime # errors in case someone calls a function that is not _REAL # or _IMAG. my $position = eval("&_".$sub); #if the position is not a number then they called something # bogus like $obj>print, which would call &_PRINT from the # eval, then $postion would be 'undef' unless( $position =~ /\d/ ) { croak("Subroutine undefined: \"$AUTOLOAD\""); } #if no parameter then they just want the Real or Imag value # returned instead of set. if( not defined $value ) { #return the value associated with the position in the array # that was returned from the eval above. return $self>[$position]; } else { #a value was passed in, so assign it to the position in the # array that was returned from the eval above. This # returns $value also, which is not strictly needed; return $self>[$position] = $value; } }
The Real, and the Imag functions are not strictly defined in the Complex package. Perl will first look for the actual function Complex::Real, but if that is not available then it will set $AUTOLOAD to 'Complex::Real' then call the Complex::AUTOLOAD function. Perl will pass AUTOLOAD the same arguments that it would have passed 'Complex::Real' had it existed. So for $x>Real(3), the first argument is '$x', and the second argument is '3'. Our AUTOLOAD function above returns a value if there is not a second argument, otherwise it sets the value.use Complex; #create a new object my $x = new Complex; #create a new object with initial values my $y = new Complex(5, 3); $x>Real(3); #set $x's real part to '3' $x>Imag(5); #set $x's imaginary part to '5' print $x>Real." ".$y>Imag; #prints '3 3'
The first one is the "" operator which stringifies the object. The function &Cmp_string will be called in the following lines:use overload "\"\"" => \&Cmp_string, "+" => \&Cmp_add, "*" => \&Cmp_multiply;
The second operator is the + or addition operator. The function &Cmp_add will be called in the following lines:print $x; #prints '3  5I' my $str = "$x"; #$str = '3  5I'
Similarly the last operator is in the * or multiplication operator. The function &Cmp_multiply will be called in the following lines:$x = $x + $y; $y = $x + 2; $y = 2 + $x;
Finally the only thing left to do is to actually implement the overloaded functions. This is my limited implementation:$y = $x * $x; $y = $x * 2; $y = 2 * $x;
So that is it. The functions are not too difficult, pretty much standard stuff. However I would not consider these to be very robust. I do not actually do any type checking on the second parameter for the Cmp_add and Cmp_multiply. I just check if is a reference of any kind then go ahead. If somebody passed in HASH reference we should definitely treat that different than a 'Complex' reference.sub Cmp_string { #get the object my $a = shift; #figure out what sign to put between the real # and imaginary part. I do this so we don't get # something like '3 + 5I', although it fairly # irrelevant my $sign = ($a>Imag > 0) ? " + " : "  "; #return a string that looks like '3  5I' return $a>Real.$sign.abs($a>Imag)."I"; } sub Cmp_add { #get the calling object; $a is always the Complex object, it # does not matter if we call '$x + 2' or '2 + $x' or # '$x + $y' ... this $a will always be $x. my $a = shift; #get the second object, either a number or an object my $b = shift; #if $b is not a Complex object do the simple math unless( $b>isa( 'Complex' ) ) { #return a new Complex object after doing the simple # arithmetic return new Complex( $a>Real + $b, $a>Imag ); } #return a new Complex object after doing the 'complex' # arithmetic return new Complex($a>Real+$b>Real, $a>Imag+$b>Imag); } sub Cmp_multiply { #get the calling object; $a is always the Complex object, it # does not matter if we call '$x * 2' or '2 * $x' or # '$x * $y' ... this $a will always be $x. my $a = shift; #get the second object, either a number or an object my $b = shift; unless( $b>isa( 'Complex' ) ) { #return a new Complex object after doing the simple # arithmetic return new Complex( $a>Real * $b, $a>Imag * $b); } #figure out the new real and imaginary parts. Good'ol # FOIL method anybody? my $real = ($a>Real * $b>Real) + ($a>Imag * $b>Imag * 1); my $imag = ($a>Real * $b>Imag) + ($a>Imag * $b>Real); #return a new Complex object after doing the 'complex' # arithmetic return new Complex($real, $imag); }
package Complex; use Carp; use strict; use vars '$AUTOLOAD'; use overload "\"\"" => \&Cmp_string, "+" => \&Cmp_add, "*" => \&Cmp_multiply; #a '_' prefix on variables is a common way to indicate private # variables. (Note that in Perl nothing is actually private # ... well that is a lie  you can use encapsulation to make # variables private, so if you want really private variables # then go read Damian Conway's "Object Oriented Perl" ). sub _REAL {0} #_REAL is a constant '0' sub _IMAG {1} #_IMAG is a constant '1' sub new { my $class = shift; #get class name ('Complex') my $real = shift; #get first argument my $img = shift; #get second argument my $self = [$real, $img]; #create anon array with arguments bless $self, $class; #bless the array as a Complex object return $self; #return our new object } sub AUTOLOAD { my $sub = $AUTOLOAD; #get global $AUTOLOAD function name my $self = shift; #get the current object my $value = shift; #get an optional parameter $sub =~ s/.*://; #strip off package name #let the system handle 'DESTROY' function calls return if $sub =~ /DESTROY/; #make function call upper case to match our constants my $sub = uc($sub); #call the function (should be 'REAL' or 'IMAG' which will # return 0 or 1 respectively). The eval will trap runtime # errors in case someone calls a function that is not _REAL # or _IMAG. my $position = eval("&_".$sub); #if the position is not a number then they called something # bogus like $obj>print, which would call &_PRINT from the # eval, then $postion would be 'undef' unless( $position =~ /\d/ ) { croak("Subroutine undefined: \"$AUTOLOAD\""); } #if no parameter then they just want the Real or Imag value # returned instead of set. if( not defined $value ) { #return the value associated with the position in the array # that was returned from the eval above. return $self>[$position]; } else { #a value was passed in, so assign it to the position in the # array that was returned from the eval above. This # returns $value also, which is not strictly needed; return $self>[$position] = $value; } } sub Cmp_string { #get the object my $a = shift; #figure out what sign to put between the real # and imaginary part. I do this so we don't get # something like '3 + 5I', although it fairly # irrelevant my $sign = ($a>Imag > 0) ? " + " : "  "; #return a string that looks like '3  5I' return $a>Real.$sign.abs($a>Imag)."I"; } sub Cmp_add { #get the calling object; $a is always the Complex object, it # does not matter if we call '$x + 2' or '2 + $x' or # '$x + $y' ... this $a will always be $x. my $a = shift; #get the second object, either a number or an object my $b = shift; #if $b is not a Complex object do the simple math unless( $b>isa( 'Complex' ) ) { #return a new Complex object after doing the simple # arithmetic return new Complex( $a>Real + $b, $a>Imag ); } #return a new Complex object after doing the 'complex' # arithmetic return new Complex($a>Real+$b>Real, $a>Imag+$b>Imag); } sub Cmp_multiply { #get the calling object; $a is always the Complex object, it # does not matter if we call '$x * 2' or '2 * $x' or # '$x * $y' ... this $a will always be $x. my $a = shift; #get the second object, either a number or an object my $b = shift; unless( $b>isa( 'Complex' ) ) { #return a new Complex object after doing the simple # arithmetic return new Complex( $a>Real * $b, $a>Imag * $b); } #figure out the new real and imaginary parts. Good'ol # FOIL method anybody? my $real = ($a>Real * $b>Real) + ($a>Imag * $b>Imag * 1); my $imag = ($a>Real * $b>Imag) + ($a>Imag * $b>Real); #return a new Complex object after doing the 'complex' # arithmetic return new Complex($real, $imag); } #module exit status 1;
Of course for more info you must read: perldoc overload: for definitions of all the possibilites for overloading operators and other functions. perldoc perlobj: For a complete intro to perl objects Object Oriented Perl By Damian Conway: this books is well written and has more info than you will ever need.


Replies are listed 'Best First'.  

Re: Basic Objects with Overloaded Operators
by Adam (Vicar) on Feb 22, 2001 at 07:40 UTC  
by perlmonkey (Hermit) on Feb 28, 2001 at 00:59 UTC  
by Anonymous Monk on Nov 15, 2001 at 06:16 UTC  
Re: Basic Objects with Overloaded Operators
by educated_foo (Vicar) on May 23, 2002 at 15:37 UTC  
by Zaxo (Archbishop) on May 31, 2002 at 01:31 UTC  
 
A reply falls below the community's threshold of quality. You may see it by logging in. 