Path: utzoo!utgpu!news-server.csri.toronto.edu!mailrus!uwm.edu!zaphod.mps.ohio-state.edu!tut.cis.ohio-state.edu!pt.cs.cmu.edu!pt!dld From: dld@F.GP.CS.CMU.EDU (David Detlefs) Newsgroups: comp.lang.c++ Subject: Re: Filesystem classes: how to? Message-ID: Date: 7 Jun 90 21:36:10 GMT References: <1567@swbatl.sbc.com> Organization: CMU CS Department Lines: 104 In-reply-to: gilstrap@swbatl.sbc.com's message of 7 Jun 90 13:23:25 GMT Brian Gilstrap presents an a problem in object oriented design. I think it is an excellent example, and want to take a shot at answering his concerns. He wants to organize a Unix file system in an object-oriented manner. He presents the following class structure: > > FileSystemNode > | > +------------+--------------+-------+---------+ > | | | | | > Directory CharacterFile BlockFile Link SymbolicLink > | > +-------------------+ > RegularFile CharacterSpecialFile He then asks: > However, since C++ won't let you cast down from a base class to a derived > class, I can't open up a directory (which would ideally contain a set of > FileSystemNodes), examine each entry, and work with it as the kind-of object > it is. That is, if I'm wanting to traverse the directories and print out > the name of each FileSystemNode and then recursively traverse all directories, > how do I treat Directory objects as directory objects if directories contain > FileSystemNodes (which means I can't get into Directories since they are only > recognized as being a FileSystemNode). > > I suppose the Directory class could keep lists of CharacterFile's and > BlockFile's and ... However, this means that the Directory class has to change > each time a new derived class of FileSystemNode is created. Bad news. It would be bad indeed if the Directory class had to change each time a new derived class were created. Avoiding this, after all, is the purpose of inheritance. Let's examine the specific question Brian poses: how to print the names of all the files "in" a filesystem node. I'll assume that everything other than a directory is a single file that has a single name. The answer is to use virtual functions. Define a virtual "print_names" function on FileSystemNode. This will do different things depending on the actual type of the object it's invoked upon. Here is some partial code. class FileSystemNode { private: char* name; // ... public: virtual void print_names(int indent =0) { cout << str("", indent) << name << "\n"; } }; class Directory : virtual public FileSystemNode { private: Set entries; public: void print_names(int indent =0); }; void Directory::print_names(int indent) { FileSystemNode::print_names(indent); SetIttr iter(entries); for (FileSystemNode fsn = iter.get(); !iter.done(); iter++) fsn.print_names(indent+4); } Notes: I'm assuming the existence of Templates here, to implement the parameterized Set type. The SetIttr type provides an iterator that allows one to iterate over all elements of a set. It provides "get", "done" and "++" operations, with the obvious interpretations. I added indentation so it prints out in a tree style. I assumed that every FileSystemNode has a string name, so I store this data in the private data of that class. Note the mileage one gets out of providing a default implementation of print_names() in the base class. Only Directory need redefine print_names(). More generally, you'd like to be able to traverse the file system and do things other than print files. To extend this solution to that concept, you'd have to provide a FileSystemNode virtual function for every other thing you'd like to do in a traversal. It would be nice if these all had the same signature. Then you could provide a virtual "traverse_and_apply" function that took a pointer to member function as its argument. For everything except directories, traverse_and_apply would simply invoke it's argument on "this." For directories, it would invoke its argument on "this," and then call traverse_and_apply with the same arguement for each FileSystemNode in the directory. I hope this helps, and invite comments and discussion. Dave -- Dave Detlefs Any correlation between my employer's opinion Carnegie-Mellon CS and my own is statistical rather than causal, dld@cs.cmu.edu except in those cases where I have helped to form my employer's opinion. (Null disclaimer.)