warsaw@nlm.nih.gov (Barry A. Warsaw) (06/14/91)
I've just run across T.A. Cargill's article "Controversy: The Case Against Multiple Inheritance in C++" in the Winter '91 issue of Computing Systems (USENIX Journal, Vol 4, No.1). Interesting article. My general impression is that Cargill is not necessary saying MI is totally useless in C++, just that there haven't been any really good "real world" examples that couldn't be rewritten using SI and members. In my current project, I've used MI for a very specific purpose and I was wondering what you folks thought in regards to MI use in general and Cargill's article. Discussions about other approaches are certainly welcome. First some background. I am currently working on a set of classes which implement an API for a full-text retrieval system. There are two parallel API developments going on simultaneously -- one is the API which provides the functionality of the search engine directly to the programmer (as an archive library -- see figure 1). The other provides the same public interface, but in a client/server environment (see figure 2). The idea is that a programmer can write an application to the API once, and by simply recompiling and relinking (i.e., no change to application code at all), the program can be part of the same Un*x process as the search engine, or it can simply communicate with a remote server on some other host: <process on same machine> +-----------+---+--------+ | | : | | user | A : search | | interface | P : engine | | applic. | I : | | | 1 : | | | : | +-----------+---+--------+ figure 1. application and engine are linked together <process on machine 1> <process on machine 2> +-----------+---+--------+ +--------+--------+ | | : | | : | | user | A : IPC | network | IPC : search | | interface | P : client | =======> | server : engine | | applic. | I : | | : | | | 2 : | | : | | | : | | : | +-----------+---+ -------+ +--------+--------+ figure 2. application communicates remotely with server The requirement here is that API1 and API2 must have the same public interface, which are the only methods the application programmer can use. The protected and private methods can be (and are) different for the two API's. To ensure this, and to help the API developers debug the interfaces (i.e., did I forget to implement a method in API1?), both API1 and API2 inherit their public interfaces from a common abstract class. Here's a simplified example: // public abstract class interface class Public_Database { public: // retrieve database name virtual string name( void ) = 0; ... }; // for standalone API (API1) class Database : public Public_Database { public: string name( void ) { return( dbname ); }; ... private: string dbname; // name of database }; // for client API (API2) class Database : public Public_Database { public: string name( void ) { return( handle->getDBname() ); }; private: IPC* handle; }; As you can see, the user of the API need only instantiate a Database class and call the name() method to get the name. The difference comes in which header files are #included and which library is linked to, but the application source never changes. Now, where does MI come into play? Well, in API1, one of the classes is contained in a list class, so it must multiply inherit both its public interface and its "listable object" behavior so that it can be managed by the list: class List { add( ListableObject* obj ) {...}; ... }; class Public_Document { public: // get size of document virtual int size( void ) = 0; ... }; // for standalone API (API1) class Document : public Public_Document, public ListableObject { public: Document( List* list ) { ... list->add( this ); }; int size( void ) { return( sz ); }; private: int sz; // size of document }; Now I'll be the first to admit that I'm not exactly a seasoned pro C++ programmer, but as near as I can tell MI in this (yes, real-world!) example is conceptually clear and useful. I'm not sure how this would be otherwise implemented. Certainly Public_Document must be inherited to ensure the parallel public interface. But it seems to me that the Document class must also be a (not *have a*) ListableObj so that the List class can manage it. So, I hope this generates a bit of discussion. Perhaps this is a valid use of MI, perhaps not? Maybe someone has an alternative solution which only uses SI -- or would that solution be "prohibitive" (*) expensive? Hopefully, my explanation of the situation makes sense... (*) whatever that means. :-) -Barry NAME: Barry A. Warsaw INET: warsaw@nlm.nih.gov TELE: (301) 496-1936 UUCP: uunet!nlm.nih.gov!warsaw