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