Question

I'm running up against a problem in understanding the CLOS way of handling file access within a class. In c++ I would be able to do this:

class Foo {
   Foo (string filename);  // opens the file (my_file) requested by the filename
   ~Foo ();  // close the file

   FILE * my_file;  // a persistent file-handle
   DataStruct my_data;  // some data

   void ParseData ();  // will perform some function on the file and populate my_data
   DataStruct * GetData () { return &my_data; }  // accessor to the data
};

What I'd like to point out is that PraseData() will be called multiple times, and each time a new block of data will be parsed from the file and my_data will be altered.

I'm trying to perform the same trick in CLOS - create all the generic methods to parse the data, load the file, read headers, etc. as well as the class definition which I have as:

(defclass data-file ()
  ((filename :initarg :filename :accessor filename)
   (file :accessor file)
   (frame :accessor frame)))

In the "constructor" (i.e. initialize-instance) I open the file just as my c++ idiom. Then I have access to the data and I can parse the data as before. However, I'm told that using a "destructor" or (finalize) method to close the file is not idiomatic CLOS for handling this type of situation where I need the file to be around so I can access it outside of my data-file methods.

I'm going to define a function that loads a data-file, and then performs a series of analyses with its data, and then hopefully close it. What's a way to go about doing this? (I'm assuming a macro or some type of closure would work in here, but I'm not familiar enough with the lisp way to decide what is needed or how to implement it).

Was it helpful?

Solution

One option is to have the stream as a slot instead of the filename, and then scope it with WITH-OPEN-FILE:

(with-open-file (stream file)
  (let ((foo (make-instance 'foo :stream stream)))
    (frob foo)
    (...other processing of foo...)))

Then your stream will be closed automatically.

OTHER TIPS

I think I would lean towards making classes only to store complete authoritative data (what you call DataStruct?).

You don't really need a special class for "loading + storage of another class". Plus, that way has the unspoken invariant that my_data holds the data of my_file up to the current seek position, which seems a bit strange to my eye.

Put another way: what does Foo do? Given a filename, it loads data, and gives you a DataStruct. That sounds like a function to me. If you need to be able to run it in a thread, or fire events between loading records, a class is the natural way to do it in C++, but you don't need a class for those things in Lisp.

Also, remember that you don't need to use DEFCLASS in order to use generic methods in CLOS.

I don't know what the structure of your data is, but in similar situations I've made a parse-one-chunk function that takes a stream and returns one record, and then create a complete Foo instance inside a loop in a with-open-file. If the stream is never needed outside the scope of a with-open-file expansion, you never need to worry about closing it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top