One of my favorite and least favorite classes in DCL is DCL_System_IO_AsciiFile, an updated version of Konrad Byers' AnyAsciiFileClass originally published in Clarion Magazine. . It's one of my favorites because it's so useful; it's one of my least favorites because there's a very inelegant bit of code needed to start using it.
Shortly after I began using AnyAsciiFileClass I ran into a situation where I needed multiple instances of the class; I needed
One of the changes I made to the class was to add support for multiple instances. Originally, AnyAsciiFileClass contained a single file definition declared in the class's CLW file:
Code Block |
---|
AnyAsciiFile FILE,DRIVER('ASCII'),CREATE,NAME(AnyAsciiFileName),PRE(AsciiFile)
RECORD
TextLine STRING(ANYASCII_IO_SIZE)
END
END |
The problem is that data declared in a class module is shared by all instances of the class.
By way of example, here is a simple class I've called DemoClass. This is the .INC file:
Code Block |
---|
DemoClass class,module('DemoClass.clw')
GetClassModuleDataAddress procedure,long
end |
and here is the CLW:
Code Block |
---|
member
map
end
include('DemoClass.inc'),once
SomeClassModuleData long
DemoClass.GetClassModuleDataAddress procedure!,long
code
return address(SomeClassModuleData) |
You can see that the CLW file contains a declaration of a LONG variable, and the one method in the class returns the address of this variable.
Now here's a small program to prove that SomeClassModuleData is the same in all instances:
Code Block |
---|
PROGRAM
MAP
END
include('DemoClass.inc'),once
InstanceA DemoClass
InstanceB DemoClass
CODE
if InstanceA.GetClassModuleDataAddress() = InstanceB.GetClassModuleDataAddress()
message('Class module data address is the same for both instances')
else
message('Class module data address is different for each instance')
end |
The result of running the program:
Getting back to AnyAsciiFileClass, when I tried to use multiple instances I had problems because each instance was operating on the same file buffer, using the same file name.
I created to workarounds. One was to pass in an external file layout. That worked but required a new Ascii file declaration each time. The other was to create some internal file layouts, with corresponding class instances using those layouts, and specify which one I wanted. This resulted in the following clunky code:
Code Block |
---|
! Declaration
TestFile &DCL_System_IO_AsciiFile
code
TestFile &= DCL_System_IO_AsciiFileManager.GetAsciiFileInstance(DCL_System_IO_AsciiFile_InstanceNumber1) |
This is really pretty awful stuff. First of all I have to declare the file object as a reference rather than an instance. And how is anyone supposed to know they're to use this mysterious DCL_System_IO_AsciiFileManager object, not to mention that ridiculous instance number equate. And what's to prevent someone from using the same instance number twice?
All I should really need is
Code Block |
---|
TestFile DCL_System_IO_AsciiFile
AnotherTestFile DCL_System_IO_AsciiFile
! etc |
There's no reason this can't work, and in fact the secret is to use the same feature that caused the problem: anything declared in the class module file is shared among all instances. That's where the instance management code needs to go.