[comp.lang.eiffel] Distributed application: solution in Eiffel ?

pjg@osf.org (Paulo Guedes) (03/20/90)

[My apologies if this question was answered before. If it was, please
tell me in which article(s) and where from I can ftp them.]

I have a distributed O-O application that poses a particular
problem. I'm evaluating how it could (or could not) be solved in
different O-O languages, so I can choose the best language for this
class of problems. I know how to solve it in C++ and I'm hopping that
someone in the net shows me a (better ?) solution in Eiffel.

Scenario: 
I have a number of servers and clients each one executing in a
different address space (Unix process). Each server manages a certain
number of different classes of objects. When clients want to access an
object, they send its name to the server and receive back a proxy
object, which represents the desired object in their address space.
Any operation on the object is performed on the proxy, which if
necessary communicates via RPC with the server. 

The name space is uniform, so given a name several types of objects
may be retrieved (e.g. a file, a directory, a device).

There is a standard class Name that exports the method 'resolve'.
This method receives as argument the string name of the object. It
sends a message to the server, receives back the name of the class to
be instantiated, creates the proxy and returns it (in fact the method
is fairly complicated, but its details are not relevant here).
Class Name does not have knowledge of the types of objects it may
retrieve, as new types may be added long after this class was created.
I assume that class Name cannot be recompiled every time a new class
of objects is added to the system.

Problem: What type should method 'resolve' return ?

If 'resolve' returns a Base type (a type which has no knowledge of
the "useful" types), I can't do anything useful with the returned
object, as ilustraded below.

	CLASS Base ...
	CLASS Name EXPORT resolve
	    FEATURE resolve(n: STRING): Base IS ...
	END
	CLASS Useful EXPORT do_something END  

	... code of some class that uses Base, Name and Useful ...
	
	Name my_name_server; -- represents the Name Server, created elsewhere
	Base f;
	...
	-- The object may be retrieved from the Name Server
	f = my_name_server.resolve ("/foo/bar"); 
	 -- But no "useful" methods may be invoked on variable f !
	f.do_something; -- This is illegal

If I declare variable f of type Useful, as seems natural,
then I can't assign to it the object returned by the Name Server.

	Useful f;
	...
	f = my_name_server.resolve ("/foo/bar"); -- This is illegal
	f.do_something; -- Invoke the desired method

Proposed solution:
This problem is trivial using a language with dynamic type checking
(Smalltalk, Objective-C).
With static type checking ... I know how to solve it in C++ (I just
have to cast the pointer returned by method 'resolve'), but I can't
find any solution in Eiffel. Any suggestions ?

I enclose a small C++ program that illustrates both my problem and the
solution in C++. It compiled and executed on a pmax using g++-1.37.1.

If you respond by e-mail I'll summarize. 


Thanks
Paulo


Cut here
--------------------------------------------------------------------------
/*
 * t5: The Problem
 */

extern "C"
{
#include <stdio.h>
}


class Base { // Base class from which all "useful" classes derive
  public:
    virtual Base *instantiate() { return new Base; }
};

class Table { // Should be a real hash table.
    Base *x;
  public:
    void insert (Base *p, char *m) { x = p; printf ("Table::insert\n"); }
    Base *lookup (char *m) { printf ("Table::lookup\n"); return x; }
};

static Table t;

class Name { // Name Manager class, retrieves "useful" classes. 
	     // Assumption: this class was written before any "useful" class
  public:
    virtual Base *resolve (char *);
};

Base* Name::resolve (char *aName)
{
    Base *temp = t.lookup (aName);
    printf ("Name::resolve\n");
    return temp->instantiate();
}

class Useful: public Base { // One "useful" class, written after Name
  public: 		    // was created
    virtual Base *instantiate() { printf ("Useful::instantiate\n");
				  return (Base *) new Useful; }
    virtual int do_something() { printf ("Useful::do_something\n"); }
};

class A: public Base, public Useful { // Now I want to use class Useful
  public:
    virtual Base *instantiate() { printf ("A::instantiate\n"); 
				  return (Base *) new A; }
};

main(int argc, char *argv[])
{
    A *temp = new A;
    A *a;
    Name *my_name_server = new Name;

    t.insert ((Base *) temp, "some-name"); // Initialization
    //  ... 

    // Now I want to retrieve an object named "/foo/bar"
    a = (A *) my_name_server->resolve ("/foo/bar");
    // I know that it is of type A and want to use like an A
    a->do_something();
}

---
Paulo Guedes     Open Software Foundation   11 Cambridge Center, Cambridge, MA
Email: pjg@osf.osf.org    Phone: (617) 621-8878