Simple but complete stand-alone demo perl object program (C++ too) 
Author Message
 Simple but complete stand-alone demo perl object program (C++ too)

Here is a simple but complete example program, illustrating object
technology as implemented in perl, and for comparison, the identical
program written in C++.

I hope to find a more permanent home for this demo (I'll put it in
comp.lang.perl.modules unless someone suggests otherwise), but first I'd
like to put it in comp.lang.perl.misc to see if anyone has any suggestions
or questions.

        The programming task:

Create a Dog class and the subclasses Beagle and Chihuahua. Each dog has a
name and a birthdate. Each dog can speak (bark) and can introduce himself.

When they speak, most dogs say "Woof" but chihuahuas say "Yapyapyap".

Beagles have a nose ("good", "bad", or "indifferent") but for most dogs the
nose is irrelevant to us.

Create two dogs: a beagle named Boris and a chihuahua named Carl. Put Boris
and Carl in a list of dogs; loop through the list and have each dog
introduce himself and speak.

        The demonstration:

These programs illustrate the concepts of

    inheritance (subtyping or specialization)
    object composition (an object as part of another object)
    dynamic binding (polymorphism; subclass is discovered at run time)
    class methods (methods invoked for the class, not for an object in the
        class)
    class data (data belonging to the class, not to an object in the
        class)
    collection classes (lists)
    C++ templates (to create multiple classes, identical except for
        output datatypes or internally-referenced datatypes)

Not illustrated:

    destructors
    multiple inheritance
    C++ operator overloading (redefining "=", "+", "[]", etc.)
    C++ datatype conversion
    C++ enum (which could guarantee that Dog::nose would contain only legal
        values)

        The perl program:

===============================================================================
package Date;

    sub create
    {
        my $type = shift;  
        my $this = {};

        bless $this, $type;
    }

    sub display
    {
        my $this = shift;
        printf
        ("%02d/%02d/%04d", $this->{'Month'}, $this->{'Day'}, $this->{'Year'});
        # ... we use American-style dates
    }

package main;

package Dog;

    sub create
    {
        my $type = shift;  
        # ... we don't need to use $type, since we know that the type
        # is the package is Dog

        my $this = {};
        # ... arguments are passed by name/value associative array, though
        # for the Data class we passed them by position
        $this->{'Name'} = $param{'Name'};
        $this->{'Birth'} = $param{'Birth'};
        ++$dogcount;
        bless $this, $type;   # inside package $this, same as bless $this
        # ... thanks to bless, a Dog can invoke subroutines in this package
        # without explictly specifying the package
        $this;
    }

    sub introduce
    {
        my $this = shift;
        print "\nI am $this->{'Name'}.\n";
        print "I was born "; Date::display ($this->{'Birth'}); print ".\n";
        $this->speak (2);
    }

    sub speak
    {

        print "Woof " x $times, "\n";
    }

    sub countdogs
    {
        return $dogcount;
    }

package main;

package Beagle;

    sub create
    {
        my $type = shift;  

        $this = create Dog ('Name'=>$param{'Name'}, 'Birth'=>$param{'Birth'});
        $this->{'Nose'} = $param{'Nose'};
        bless $this, $type;
    }

    sub introduce
    {
        my $this = shift;
        &Dog::introduce ($this);
        print "My nose is $this->{'Nose'}.\n";
    }

package main;

package Chihuahua;

    sub speak
    {

        print "Yapyapyap " x $times, "\n";
    }

package main;

$dateref = create Date (10, 3, 1995);
$dogref = create Beagle ('Name'=>'Boris', 'Birth'=>$dateref, 'Nose'=>'good');

$dateref = create Date (8, 31, 1993);
$dogref = create Chihuahua ('Name'=>'Carl', 'Birth'=>$dateref);

# we could just print $Dog::dogcount, but let's demo a class method:
$dogq = &Dog::countdogs;
print "\n$dogq dogs exist.\n";

printf ("\nThere are %d dogs in doglist.\n", $#dogrefs + 1);


{
    # dynamic binding: this invocation always finds the right subclass of Dog:
    $dogref->introduce ();

Quote:
}

===============================================================================

        The output (of the perl program and of the C++ program):

2 dogs exist.

There are 2 dogs in doglist.

I am Boris.
I was born 10/03/1995.
Woof Woof
My nose is good.

I am Carl.
I was born 08/31/1993.
Yapyapyap Yapyapyap

        Implementation notes:

We can store a count of dogs as a class (static) variable in the class Dog.
We can also store a count of dogs as an instance variable in each list of
dogs. For demonstration purposes, we do both; hence the class variable
Dog::dogcount (in C++ and perl), and the instance variable
List<Dog>::listcount (in C++; in perl we just use the "$#" operator).

In the C++ program, we use the template mechanism to create a list of Dogs;
the mechanism prevents us from accidentally putting a Pig or a Truck in the
list. Perl is generally looser about data types, and we do not try to
implement a parallel mechanism in the perl program.

In the C++ program, we use the private/protected/public mechanism to
control access to class members. Perl is generally freer about privacy
issues and we do not try to implement a parallel mechanism in the perl
program.

        Language notes:

C++ introduces a number of keywords (beyond C) to implement its object
technology (class, public, protected, private, new, friend, etc.).

Perl, on the other hand, introduces only three new syntactic elements
(beyond packages) to implement its object technology. These elements are
(1) bless, (2) class method invocation; and (3) instance method invocation:

1. The bless function (see perlfunc.html) prepares the way for instance
method invocation, by letting an object invoke methods (subroutines) in its
package without explictly specifying the package (see below).

2. Class method invocation is a new way to invoke a subroutine. In addition
to the "normal" way, such as

    $x = &pkg::fcn ("arg");

you can invoke any packaged subroutine by specifying its name (without
"&"), and providing the name of its package as the first argument (but not
in parentheses):

    $x = fcn pkg ("arg");
    $dateref = create Date (10, 3, 1995);
    $dogref = create Chihuahua ('Name'=>'Carl', 'Birth'=>$dateref);

The name of the package is automatically provided to the subroutine as the
first argument.

This second syntactic element implements class methods, which are
invocations of class subroutines through the class rather than through any
particular member of the class.

3. Instance method invocation is another new way to invoke a subroutine: In
addition to the "normal" way, such as

    &Dog::introduce ($dogref);

you can invoke a packaged subroutine through a reference (it has to be
blessed first):

    $dogref->introduce ();

The reference is automatically provided to the subroutine as the first
argument.

This third element implements instance methods, which are invocations of
class subroutines through a particular member of the class.

Note that "new" is not a keyword in perl, though programmers typically use
it as a method name where we have used "create".  

Note that C++ uses the keyword "static" to syntactically distinguish a
class method from an instance method. In perl, the two kinds of methods
are not syntactically distinguished. Thus the difference between the two
kinds of methods is vaguer in perl; you can call a static method as if it
were an instance method (for example, you can invoke $dogref->countdogs).

Also, C++ uses the keyword "virtual" to distinguish virtual instance methods
from non-virtual instance methods. In perl, all instance methods are virtual.
Virtual methods provide polymorphism; note that in our example program, both
perl and C++, doglist is simply a list of dogs (it doesn't record the subtype
of each dog). Without polymorphism, if we iterated through the list of dogs,
only Dog methods (not Dog subtype methods) would be accessed, so all
dogs--even Chihuahuas--would speak "Woof Woof". Change "virtual void speak"
to "void speak" in the C++ program to prove this to yourself.

        The class method:

The following program demonstrates the syntactic element that implements class
methods in perl.

===============================================================================
package pkg;

sub fcn
{
    print "This is fcn()\n";

Quote:
}

package main;

# "normal" subroutine invocation:
$x = &pkg::fcn ("xxx");   # "&" is optional if you use parens

# "class method" invocation:
# (works only if 1st arg is package where subroutine is):
$x = fcn pkg ("yyy");  
# ... "&" is prohibited, parentheses around "pkg" are prohibited

# proof that "class method" invocation fails if 2nd arg isn't
# appropriate package:
$x = fcn whatever;  
# ... error: 'Can't call method "fcn" in empty package "whatever"'
===============================================================================

        The C++ program:

===============================================================================
#include "iostream.h"
#include "string.h"

class Date
{
friend ostream& operator<< (ostream& outstream, const Date& d);
// ... so cout can access private and protected members
public:
// constructors:
    Date (int y, int m, int d) { year = y ; month = m; day = d; }
private: int year; int month; int day;

Quote:
};

ostream& operator<< (ostream& outstream, const Date& d)
{ cout << d.month << '/' << d.day << '/' << d.year; return outstream; }
// ... we use American-style dates

class String
{
friend ostream& operator<< (ostream& outstream, const String& str);
// ... so cout can access private and protected members
public:
    String& operator= (char *c) { strcpy (s, c); return *this; }
// constructors:
    String (char *c) { strcpy (s, c); }
private: char s[30];

Quote:
};

ostream& operator<< (ostream& outstream, const String& str)
{ cout << str.s; return outstream; }

/*
Here's a List template, so we can generate different list classes (a Dog
list, a Pig list, a Person list, etc.):
*/
template <class C>
class List
{
friend class ListIter<C>;   // so iterator can access elem, listcount
public:
    int getcount () { return listcount; }
    List<C>& operator+= (C* cp) { elem[listcount++] = cp; return *this; }
// constructors:
    List<C> () : listcount (0) {}
private:
    C* elem[10];
    int listcount;

Quote:
};

/*
Here's a ListIter template, so we can create different iterator classes that
loop through different list classes:
*/
template <class C>
class ListIter
{
public:
    void* next () { return (j > list.listcount - 1 ? 0 : list.elem[j++]); }
// constructors:
    ListIter<C> (List<C>& l) : j (0), list (l) {}
private:
    int j;
    List<C>& list;

Quote:
};

class Dog
{
public:
    virtual void introduce ()
    {
        cout << "\nI am " << name << ".\n";
        cout << "I was born " << birth << ".\n";
        speak (2);
    }
    virtual void speak (int times)
    {
        for (int j = 0; j < times; ++j) cout << "Woof ";
        cout << "\n";
    }
    static int Dog::getcount () { return Dog::dogcount; }
// constructors:
    Dog (String nam, Date d) : birth (d), name (nam) { ++Dog::dogcount; }
protected:
    String name;
    Date birth;
    static int Dog::dogcount;
Quote:
};

int Dog::dogcount;  
// ... C++ requires that we define a static class variable outside the class

class Beagle : public Dog
{
public:
    virtual void introduce ()
    {
        Dog::introduce (); cout << "My nose is " << nose << ".\n";
    }
// constructors:
    Beagle (String nam, Date d, String nose) : Dog (nam, d), nose (nose)
    {}
protected:
    String nose;

Quote:
};

class Chihuahua : public Dog
{
public:
    virtual void speak (int times)
    {
        for (int j = 0; j < times; ++j) cout << "Yapyapyap ";
        cout << "\n";
    }
// constructors:
    Chihuahua (String n, Date d) : Dog (n, d) {}

Quote:
};

main ()
{
    Dog* dp;
    List<Dog> doglist;
    ListIter<Dog> iterdog (doglist);

    dp = new Beagle ("Boris", Date (1995, 10, 3), "good");
    doglist += dp;
    dp = new Chihuahua ("Carl", Date (1993, 8, 31));
    doglist += dp;

    cout << "\n" << Dog::getcount () << " dogs exist.\n";
    cout << "\nThere are " << doglist.getcount () << " dogs in doglist.\n";

    while ((dp = (Dog*) (iterdog.next())) != 0)
    {
        dp->introduce ();
    }
    return 0;

Quote:
}

===============================================================================


Sun, 31 Jan 1999 03:00:00 GMT  
 
 [ 1 post ] 

 Relevant Pages 

1. stand-alone embedding of perl to c++

2. Stand-alone Perl programs in Win32

3. Stand Alone Perl Program !!!! URGENT

4. Stand alone Perl script encryption

5. MKS Perl - ARGV doesn't work with stand alone script

6. Stand-alone executables from any DOS perl?

7. Creating stand alone apps out of perl script

8. A stand alone Perl server to augment NT?

9. Using perl noninternet, stand alone for processing!!????????!!!!

10. Compile Perl into a stand-alone executable

11. Perl 2 Stand alone?

12. Stand alone perl scripts?

 

 
Powered by phpBB® Forum Software