The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Eobj - Easy Object Oriented programming environment

SYNOPSIS

  use Eobj;

  # define the 'myclass' class
  inherit('myclass','myclass.pl','root');

  init; # Always init before creating objects

  # Now create an object, and put the handle in $object
  $object = myclass->new(name => 'MyObject');
  $object->mymethod('hello'); # Call the 'mymethod' method

  # Access some properties...
  $object->set('myscalar', 'The value');
  $scalar = $object->get('myscalar');

  $object->set('mylist', 'One', 'Two', 'Three');
  @list = $object->get('mylist');

  %hash = ('Foo' => 'Bar',
           'Daa' => 'Doo');
  $object->set('myhash', %hash);
  %the_hash = $object->get('myhash');

  # Dump some debug information
  globalobj->objdump();
 

DESCRIPTION

Eobj is OO in Perl for the masses. It makes it possible to write complete OO scripts with plain Perl syntax (unlike classic OO-Perl). And like plain Perl, the syntax usually means what you think it means, as long as things are kept plain.

Eobj doesn't reinvent Perl's natural OO environment, but is a wrapper for it. The real engine is still Perl.

This man page is enough to get you going, but not more than that. If deeper understanding is needed, the documentation, which can be found in the Eobj Programmers guide, eobj.pdf (PDF format), is the place to look. The PDF file should come along with the module files.

CLASSES, OBJECTS, METHODS AND PROPERTIES

If you are acquantied with object-oriented programming, just jump to the next section. If you're not, this little paragraph should explain some basics, but by all means additional reading is recommended.

An object is a creature, which you generate by telling some class to create an object for you. For example,

  $object = myclass->new(name => 'MyObject');

This statement creates a new object of the class myclass. In this context, myclass would be the answer to "what kind of object did we just make?"

The object's reference (sometimes called "handle") is returned and kept in $object. If we want to access the object, we do that by using the value stored in $object. This value is not a number nor a string, but it is otherwise handled like any other value in Perl (it can be returned from subroutines, copied, stored in lists, and so on).

You can do two things with an object: You can call one of its methods, and you can manipulate its properties.

Properties

Each object has its own set of "local variables", which are its properties. The only really special thing about properties is that they are related to a certain object, so changing the properties of one object does not affect another object.

For example,

  $object->set('myscalar', 'The value');

sets the value of the property named myscalar to the string 'The value'. If the property didn't exist, this statement created it. We may then read the string back with

  $scalar = $object->get('myscalar');

There is more about how to handle properties in this man page.

Methods

A method is exactly like a subroutine, only method calls are always related with an object. For example,

 $object->mymethod('hello');

means to tell the object, whose reference is stored in $object, to call a subroutine, which it recognizes as mymethod.

Classes

A class is an "object factory". Objects are sometimes called "class instances". Beyond these metaphores, a class is simply a list of methods, which the object should recognize and execute when it's asked to.

When we create an object, we choose a class. By this choice, we're actually choosing what methods our object will support, and what these methods will do, and also what initial properties our object will carry. It's not that we're necessarily aware of each method and property, but nevertheless we choose them by choosing the class.

Note that even though a class consists of a list of subroutines, they work differently from plain subroutines: Methods are always called from an object, and the actual action taken by a method often depends on the object's properties.

Inheritance

No class is written from scratch. We don't want to define each method that the object should support explicitly, every time we want to write a new class.

Rather, we inherit methods from already existing classes: We create a new class by defining only the methods that are special for this specific class. Then, when declaring the new class, we explicitly point at some other class, and say something like "if you can't find a method in our class declaration, look in this class for it". The result is a new class, which supports all methods that the previous class supported, plus a few more.

It's possible to re-declare certain methods. It's also possible to extend methods, so a call to the method will carry out whatever it did before plus something extra, that we wanted. See "DECLARING CLASSES" below.

HOW IT ALL WORKS

Before beginning, it's important to distinguish between the main script and the class source files.

The main script is what you would usually call "the script": When we run Perl, we give some file as the script to run. That file, possibly with some other Perl modules that it runs, is the main script.

The main script is divided in two phases:

  • Declaration of classes. The main script tells what classes it wants to have, and what source file declares each class' methods.

  • Creating and using objects. This is usually where the main script does something useful: Objects are created, their methods are called, and so on.

We shift from phase 1 to phase 2 with a call to init(). All class declarations must come before init(), and all objects creations must come afterwards.

init() does not accept any arguments, so it's done exactly like in the Synopsis above.

CREATING CLASSES

Classes are defined by scripts ("class source files") that contain nothing else than subroutine definitions. For example, the myclass.pl file mentioned in the Synopsis could very well consist of exactly the following:

  sub mymethod {
    my $self = shift;
    my $what = shift;
    print "I was told to say $what\n";
  }

This subroutine definition (in effect, method definition) could be followed by other similar subroutine definitions.

When a method is called, the first argument is a reference ("handle") to the object through which it was called. It's common to store this reference in a scalar named $self (as in the above example).

After the shift operation, which removed the first argument from @_, the argument list looks just like a normal subroutine call. Therefore, the second shift operation is related to the parameter that was passed to the method when calling it, and it's put in $what.

Rules for writing classes

  • The class source file should not contain anything else than sub { } declarations.

  • All variables must be localized with my. The method should not access varibles beyond its own, temporary scope. It may, of course, access other objects' properties.

  • If "global variables" are needed, they should be kept as properties in the global object (see below).

  • Use puke() and blow() instead of die(). Use fishy() and wiz() instead of warn(). In error messages, identify your own object with $self->who(), and other objects with $self->safewho($otherobject)

  • Call methods, including your own class' methods, in complete $obj->method()-like format. This will assure consistency if your method is overridden.

  • Properties should be accessed only as described in the Eobj documentation (and not as in classic Perl objects).

Methods vs. Subroutines

Subroutines are routines that are not related to any specific object or other kind of context (this is what plain Perl programmers do all the time). Methods, on the other hand, are routines that are called in conjunction with an object. In other words, calling a method is telling an object to do something. This call's action often depends on the specific object's properties, and it may also affect them.

Therefore, when a method is called in Perl, the first argument is always a handle (reference) to the object whose method was called. In this way, the routine knows what object it is related to.

The rest of the arguments appear exactly like a regular subroutine call. A subroutine can be transferred into a method by putting a shift command in the beginning. In particular, the method's return values mechanism is exactly like the one of plain subroutines.

DECLARING CLASSES

A class is declared by pointing at a source file (which consists of method declarations), and bind its methods with a class name. This is typically done with either inherit() or inheritdir(). For example,

  inherit('myclass','myclass.pl','root');

reads the file myclass.pl, and creates a class named myclass. This class is derived from the root class, and hence any method which is not defined in myclass will be searched for in root.

As a result of this declaration, it will be possible to create a new object with myclass->new(...).

Note that there is no necessary connection between the class' name and the name of the source file when inherit() is used. Also, it should be noted that the source file is not read by the the Perl parser until it's specifically needed (usually because an object is created).

inheritdir() is used to to declare several classes with a single call. The given file directory path is scanned for source files. A class inheritance tree can be set up by setting up the file directory tree in a straightforward way. This is explained further in the programmer's guide.

Also, it's possible to add and extend methods of an existing class, without changing its name. For example, it's possible to change the methods of the root class, which will affect all objects that are created. See the section about override() in the programmer's guide.

A call to init() is mandatory after all class declarations (inherit() and inheritdir() statements) and before creating the first object is generated.

Overriding methods

Suppose that we defined class parent with a method named foo(). Later we define class child, that inherits from class parent, and also contains a method named foo(). If a user instantiates an object of class child, and invokes method foo(), then the method foo() of the child class is invoked, rather than the ome of parent. This is called method overriding (and is common in many OO-languages).

In the above case, method foo() of class child completely hides method foo() of class parent. If we want method foo() of child class to extend, rather than replace foo() of parent, we could use something like the following, in the code of foo() of child:

  sub foo {
    my $self = shift;
    $self->SUPER::foo(@_);
  
    # Here we do some other things...
  }

Note that we call the inherited foo() after shifting off the $self argument, buf before doing anything else. This makes sure that the inherited method gets an unaltered list of arguments. When things are organized like this, both methods may shift their argument lists without interfering with each other.

But this also means, that the extra functionality we added will be carried out after the inherited method's. Besides, we ignore any return value that the method returned.

Whenever the return value is of interest, or we want to run our code before the inherited method's, the following schema should be used:

  sub foo {
    my $self = shift;
  
    # Here we do some other things...
    # Be careful not to change @_ !

    return $self->SUPER::foo(@_);
  }

Note that this is the easiest way to assure that the return value will be passed on correctly. The inherited method may be context sensitve (behave differently if a scalar or list are exptected as return value), and the last implementation above assures that context is passed correctly.

The problem with this way of doing it, is that if we accidentally change the argument list @_, the overridden method will misbehave, which will make it look like a bug in the overridden method (when the bug is really ours).

This could be solved by storing the arguments in some temporary variable, like:

  sub foo {
    my $self = shift;
    my @save_args = @_;
  
    # Here we do some other things...
    # We can change @_ now!
  
    return $self->SUPER::foo(@save_args);
  }

All this was true for methods that work on an already existing object. The new() method is an exception, because it is there to create the object.

Extending the new() method is often a good idea, usually to initialize the newly born object with some properties. It's nevertheless important to stick to the following format, or strange things may happen:

  sub new {
    my $this = shift;
    my $self = $this->SUPER::new(@_);

    # Do your stuff here. $self is the
    # reference to the new object

    return $self; # Don't forget this!
  } 

USING OBJECTS

Objects are created by calling the new() method of the class. Something in the style of:

  $object = myclass->new(name => 'MyObject');

(You didn't forget to call init() before creating an object, did you?)

This statement creates a new object of class myclass, and puts its reference (handle, if you want) in $object. The object is also given a name, Myobject.

Every object must be created with a unique name. This name is used in error messages, and it's also possible to get an object's reference by its name with the root class' objbyname() method. The object's name can not be changed.

If a name isn't given explicitly, like in

  $object = myclass->new();

Eobj will choose a name for the object, which can't be changed later on. It's highly recommended to overcome this laziness, and choose a short but descriptive name for each object.

Since a fatal error occurs when trying to create an object with an already existing name, the root class' method suggestname() will always return a legal name to create a new object with. This method accepts our suggested name as an argument, and returns a name which is OK to use, possibly the same name with some enumeration.

So when the object's names are not completely known in advance, this is the safe way to do it:

  my $name = globalobj->suggestname('MyObject');
  my $object = myclass->new(name => $name);

or, if we don't care about the object's name:

  my $object = myclass->new(name => globalobj->suggestname('MyObject'));

After the object has been created, we may access its properties (see below) and/or call its methods.

For example,

  myclass->mymethod('Hello');

OBJECT CONSTRUCTORS AND DESTRUCTORS

Objects are created with the new() method. In general, there is no need to explicitly define one of your own, and if you do, it must be based on Eobj's native new() method (see Overriding methods above). In particular, this is useful for creating classes which set up properties upon creation.

An object is destroyed by calling its destroy() method. There is no need to call this method explicitly unless you need a certain object destroyed at a certain time.

It's also possible to extend this method, in order to clean up things just before going down.

If how and when objects are destroyed is of your concern, or if you want to do something just before that, there's a section dealing with that issue in the Programmer's guide.

OBJECT'S PROPERTIES

Each object carries its own local variables. These are called the object's properties.

The properties are accessed with mainly two methods, get() and set(). const() is used to create constant properties, which is described in the programmer's guide.

  $obj->set($prop, X);

Will set $obj's property $prop to X, where X is either a scalar, a list or a hash.

One can the obtain the value by calling

  X = $obj->get($prop);

Where X is again, either a scalar, a list or a hash.

$prop is the property's name, typically a string. Unlike plain Perl variables, the property's name is just any string (any characters except newlines), and it does not depend on the type of the property (scalar, list or hash). It's the programmer's responsibility to handle the types correctly.

Use set() and get() to write and read properties in the spirit of this man page's Synopsis (beginning of document). It's simple and clean, but if you want to do something else, there is much more to read about that in the programmer's guide.

THE GLOBAL OBJECT

The global object is created as part of the init() call, and is therefore the first object in the system. It is created from the global class, which is derived from the root class.

The global object has two purposes:

  • Its properties is the right place to keep "global variables".

  • It can be used to call methods which don't depend on the object they are called on. For example, the suggestname() method, mentioned above, is a such a method. We may want to call it before we have any object at hand, since this method is used to prevent name collisions.

    The global object's handle is returned by the globalobj() function in the main script or with the root class' globalobj() method. So when writing a class, getting the global object is done with something like

      my $global = $self->globalobj;

HOW TO DIE

Eobj comes with an error-reporting mechanism, which is based upon the Carp module. It's was extended in order to give messages that fit Eobj better.

There are two main functions to use instead of die(): blow() and puke(). They are both used like die() in the sense that a newline in the end of the error message inhibits prinitng the file name and line number of where the error happened.

  • blow() should be used when the error doesn't imply a bug. A failure to open a file, wrong command line parameters are examples of when blow() is better used. Note that if you use blow() inside a class, it's usually better to give a descriptive error message, and terminate it with a newline. Otherwise, the class source file will be given as where the error occured, which will make it look as if there's a bug in the class itself.

  • puke() is useful to report errors that should never happen. In other words, they report bugs, either in your class or whoever used it. puke() displays the entire call trace, so that the problematic call can be found easier.

For warnings, fishy() is like blow() and wiz() works like puke(). Only these are warnings.

Unlike Carp, there is no attepmt to distinguish between the "original caller", or the "real script" and "modules" or "classes". Since classes are readily written per application, there is no way to draw the line between "module" and "application".

It is possible to declare a class as "hidden" or "system", which will make it disappear from stack traces. This is explained in the programmer's guide.

ISSUES NOT COVERED

The following issues are covered in the Eobj programmer's guide (eobj.pdf), and not in this man page. Just so you know what you're missing out... ;)

  • The override() and underride() functions

  • Constant properties

  • Magic callbacks: How to make properties depend on each other.

  • Setting up properties during object creation with new()

  • List operations on properties: pshift(), punshift(), ppush() and ppop()

  • The property path: A way organize properties in a directory-like tree.

  • Several useful methods of the root class: who(), safewho(), isobject(), objbyname(), prettyval() and linebreak()

EXPORT

The following functions are exported to the main script:

init(), override(), underride(), inherit(), inheritdir(), definedclass(), globalobj()

These functions are exported everywhere (can be used by classes as well):

blow(), puke(), wiz(), wizreport(), fishy(), wrong(), say(), hint(), wink()

These methods are part of the root class, and should not be overridden unless an intentional change in their functionality is desired:

new(), destroy(), survivor(), who(), safewho(), isobject(), objbyname(), suggestname(), get(), const(), set(), seteq(), addmagic(), pshift(), ppop(), punshift(), ppush(), globalobj(), linebreak(), objdump(), prettyval()

store_hash(), domutate(), getraw()

Note that the last three methods are for the class' internal use only.

HISTORY

Eobj is derived from a larger project, Perlilog, which was written in 2002 by the same author. A special OO environment was written in order to handle objects conveniently. This environment was later on extracted from Perlilog, and became Eobj.

BUGS

Please send bug reports directly to the author, to the e-mail given below. Since Eobj relies on some uncommonly used (but yet standard) features, the bug is sometimes in Perl and not Eobj. In order to verify this, please send your version description as given by running Perl with perl -V (a capital V!).

These are the bugs that are known as of yet:

  • Doesn't work with use strict due to some games with references. Does work with use strict 'vars', though.

  • The environment doesn't tolerate a change in home directory (with chdir) if any of the files used in inherit(), inheritdir() or the likes were given as a path relative to the current directory. Since the files are loaded only when the respective classes are used, changing the directory is prohibited at any stage of the execution.

ACKNOWLEDGEMENTS

This project would not exist without the warm support of Flextronics Semiconductors in Israel, and Dan Gunders in particular.

AUTHOR

Eli Billauer, <elib@flextronics.co.il>

SEE ALSO

The Perlilog project: http://www.opencores.org/perlilog/

The author's home page: http://www.billauer.co.il/