Jump to content

Controlling Yourself. A Framework for Configurable Options: Difference between revisions

From EDM2
mNo edit summary
Ak120 (talk | contribs)
No edit summary
Line 15: Line 15:
==Class Relationship Structure==
==Class Relationship Structure==


'''<strong>Overview</strong>'''
'''Overview'''


The class structure is depicted in the diagram below.  The relationship marked IS-A denotes class derivation.  The relationship marked HAS-A shows that the class contains an object of the class.  The KeyedOptionSet class is the result of a template generation using the KeyedSequentialSet class in the compiler collection classes library.
The class structure is depicted in the diagram below.  The relationship marked IS-A denotes class derivation.  The relationship marked HAS-A shows that the class contains an object of the class.  The KeyedOptionSet class is the result of a template generation using the KeyedSequentialSet class in the compiler collection classes library.
Line 21: Line 21:
[[Image:Control1.gif|thumb|Class Relationship Structure]]  
[[Image:Control1.gif|thumb|Class Relationship Structure]]  


'''<strong>Set Classes</strong>'''
'''Set Classes'''


The option set behaviors have been factored into a base class (BOptionSet) and two implementation classes (INIOptionSet and EAOptionSet).  The behaviors specific to manipulation of EAs or profiles is in EAOptionSet and INIOptionSet respectively.
The option set behaviors have been factored into a base class (BOptionSet) and two implementation classes (INIOptionSet and EAOptionSet).  The behaviors specific to manipulation of EAs or profiles is in EAOptionSet and INIOptionSet respectively.
Line 76: Line 76:
The resetSet method performs two operations.  First, the set of names in the profile or EAs is made to correspond with the set of names in the set. Any names with a prefix other than &lt;application name&gt; are ignored. The second operation is to perform a storeSet.
The resetSet method performs two operations.  First, the set of names in the profile or EAs is made to correspond with the set of names in the set. Any names with a prefix other than &lt;application name&gt; are ignored. The second operation is to perform a storeSet.


'''<strong>Item Classes</strong>'''
'''Item Classes'''


The BOptionItem class provides some default implementations for the behaviors.  The class interface is primarily intended to be the communication portal between an option set and the real data objects that are the elements of that set.  The interface is:
The BOptionItem class provides some default implementations for the behaviors.  The class interface is primarily intended to be the communication portal between an option set and the real data objects that are the elements of that set.  The interface is:
Line 98: Line 98:
definition provides for the normal string behaviors.
definition provides for the normal string behaviors.


<p><strong>Interface to Programmer Supplied Objects</strong>
'''Interface to Programmer Supplied Objects'''


<p>The interface used by the option set to manipulate the items in memory is
The interface used by the option set to manipulate the items in memory is
BOptionItem.  Because all option items are derived from BOptionItem, the
BOptionItem.  Because all option items are derived from BOptionItem, the
correct method is invoked.
correct method is invoked.


<p>When an addItem method is invoked on a set, the set obtains a BOptionItem
When an addItem method is invoked on a set, the set obtains a BOptionItem
type pointer to that item. When the set needs the size of the item, e.g.
type pointer to that item. When the set needs the size of the item, e.g.
to allocate a buffer, the pointer to a BOptionItem is used to invoke the
to allocate a buffer, the pointer to a BOptionItem is used to invoke the
correct method. Similarly, the importData and exportData methods are
correct method. Similarly, the importData and exportData methods are
invoked to move the value to and from the persistent store.
invoked to move the value to and from the persistent store.


<p><strong>Adding New Object Classes</strong>
'''Adding New Object Classes'''


<p>There are two different strategies that can be employed for extending the
There are two different strategies that can be employed for extending the
range of types of items that can be used in option sets. The programmer
range of types of items that can be used in option sets. The programmer
may implement interface behaviors directly in the object to be added, or
may implement interface behaviors directly in the object to be added, or
the programmer can implement a wrapper class. If the class is under
the programmer can implement a wrapper class. If the class is under
control of the programmer, direct implementation is usually the best
control of the programmer, direct implementation is usually the best
approach. If the object to be used is provided by a third party class
approach. If the object to be used is provided by a third party class
library, or is not a first class object (e.g., an INT) then a wrapper class
library, or is not a first class object (e.g., an INT) then a wrapper class
is the right approach. The sample program uses the later approach because
is the right approach. The sample program uses the later approach because
it is the most non-obvious of the two approaches. The reader will notice
it is the most non-obvious of the two approaches. The reader will notice
that the example is contrived.
that the example is contrived.


Line 127: Line 127:
The sample/test program illustrates how to create a set, add option items to that set, and store the set of option items.  To exercise the code, these tasks were performed for both profile and EA based option sets.  The file test.cpp [see test_src.zip within options.zip - Editor] should be followed for this discussion. You may also want look at BOptionSet.cpp and StrOptionItem.cpp [see source.zip within options.zip - Editor] for additional detail.
The sample/test program illustrates how to create a set, add option items to that set, and store the set of option items.  To exercise the code, these tasks were performed for both profile and EA based option sets.  The file test.cpp [see test_src.zip within options.zip - Editor] should be followed for this discussion. You may also want look at BOptionSet.cpp and StrOptionItem.cpp [see source.zip within options.zip - Editor] for additional detail.


<p><table BORDER=1 WIDTH=100% CELLSPACING=0 CELLPADDING=5>
<table BORDER=1 WIDTH=100% CELLSPACING=0 CELLPADDING=5>
 
<tr>
<tr>
<td>Housekeeping</td>
<td>Housekeeping</td>
<td>test.cpp</td>
<td>test.cpp</td>
<td>Initialize several variables, get an anchor block, and open
<td>Initialize several variables, get an anchor block, and open the test profile</td>
the test profile</td>
</tr>
</tr>
<tr>
<tr>
<td>create empty set</td>
<td>create empty set</td>
<td>test.cpp</td>
<td>test.cpp</td>
<td>Declare object of type INIOptionset with a string and handle
<td>Declare object of type INIOptionset with a string and handle for the profile</td>
for the profile</td>
</tr>
</tr>
<tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>INIOptionSet.cpp</td>
<td>INIOptionSet.cpp</td>
<td>Constructor for string and handle pass string to the BOptionSet
<td>Constructor for string and handle pass string to the BOptionSet for the application name, then hold onto the handle for subesquent file operations</td>
for the application name, then hold onto the handle for subesquent file operations</td>
</tr>
</tr>
<tr>
<tr>
<td>create items</td>
<td>create items</td>
Line 159: Line 153:
<td>Add items to the set</td>
<td>Add items to the set</td>
<td>test.cpp</td>
<td>test.cpp</td>
<td>The addItem method is invoked for each item</td>
<td>The addItem method is invoked for each item</td>
</tr>
</tr>
Line 172: Line 165:
<tr>
<tr>
<td>store the set</td>
<td>store the set</td>
<td>INIOptionSet.cpp</td>
<td>INIOptionSet.cpp</td>
<td>The items are stored on the profile test.ini. The set is traversed
<td>The items are stored on the profile test.ini. The set is traversed
Line 186: Line 178:
callers responsibility to free the areas.</td>
callers responsibility to free the areas.</td>
</tr>
</tr>
<tr>
<tr>
<td>change the values of the items</td>
<td>change the values of the items</td>
Line 194: Line 185:
<tr>
<tr>
<td>reset the items from the store</td>
<td>reset the items from the store</td>
<td>test.cpp</td>
<td>test.cpp</td>
<td>The loadset method is invoked to update the items from the
<td>The loadset method is invoked to update the items from the
Line 207: Line 197:
memory</td>
memory</td>
</tr>
</tr>
<tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
Line 217: Line 206:
<td>delete some items</td>
<td>delete some items</td>
<td>test.cpp</td>
<td>test.cpp</td>
<td>Items 1 and 2 are deleted from the set, and the version of the set
<td>Items 1 and 2 are deleted from the set, and the version of the set
on the profile is update via resetSet so that only Item3 remains.</td>
on the profile is update via resetSet so that only Item3 remains.</td>
Line 229: Line 217:
<tr>
<tr>
<td>Create an EAOptionSet</td>
<td>Create an EAOptionSet</td>
<td>test.cpp</td>
<td>test.cpp</td>
<td>The EAOptionSet constructor is invoked, using the INIOptionSet
<td>The EAOptionSet constructor is invoked, using the INIOptionSet
Line 240: Line 227:
for later use as the applicaion name. The handle of the file is retained.</td>
for later use as the applicaion name. The handle of the file is retained.</td>
</tr>
</tr>
<tr>
<tr>
<td>reset EA's to known values</td>
<td>reset EA's to known values</td>
Line 251: Line 237:
<td>Exercise the EAmethods</td>
<td>Exercise the EAmethods</td>
<td>test.cpp</td>
<td>test.cpp</td>
<td>Items are added to the set; the set is stored; values are
<td>Items are added to the set; the set is stored; values are
changed and reset in a manner similar to the tests for the INIOptionSet.</td>
changed and reset in a manner similar to the tests for the INIOptionSet.</td>
Line 257: Line 242:
</table>
</table>


<p>There are several things worth keeping in mind. The flow for the above example is designed to test the logic, and not how the objects would be used in a real application. In a real application, the steps for use would be:
There are several things worth keeping in mind. The flow for the above example is designed to test the logic, and not how the objects would be used in a real application. In a real application, the steps for use would be:


<ol>
<ol>
Line 271: Line 256:


If there are file specific options, then the EAOptionSet object is used as
If there are file specific options, then the EAOptionSet object is used as
well. The EAOptionSet object would be created from the INIOptionSet
well. The EAOptionSet object would be created from the INIOptionSet
object, and if there were any non-file related options, those option items
object, and if there were any non-file related options, those option items
would be removed from the EAOptionSet via the delItem operation.
would be removed from the EAOptionSet via the delItem operation.


<p>At an appropriate time, the option sets would be written to either the
At an appropriate time, the option sets would be written to either the
profile or the EAs or both.
profile or the EAs or both.


==Implementation notes==
==Implementation notes==


<p>This section discusses some of the less obvious aspects of the set classes.
This section discusses some of the less obvious aspects of the set classes.
To warrant the appellation of "less obvious," it had to be something I
To warrant the appellation of "less obvious," it had to be something I
needed some effort to figure out.  Which of course means that they may be
needed some effort to figure out.  Which of course means that they may be
completely obvious to you.
completely obvious to you.


<p><strong>Profiles</strong>
'''Profiles'''


<p>The API for the profile was just a joy to use (compared to EAs), and the
The API for the profile was just a joy to use (compared to EAs), and the
documentation makes everything pretty clear.
documentation makes everything pretty clear.


<p><strong>EAs</strong>
'''EAs'''


<p>The EAs are not at all the same story. I used two books, and the
The EAs are not at all the same story. I used two books, and the
documentation, to puzzle my way through how EAs worked.  The books used
documentation, to puzzle my way through how EAs worked.  The books used
were <u>The Art of OS/2 2.1 C Programming</u>, by Panov, Salomon, and Panov; the
were <u>The Art of OS/2 2.1 C Programming</u>, by Panov, Salomon, and Panov; the
other book was <u>OS/2 2.1 Application Programmers Guide</u>, by Kelly et. al.
other book was <u>OS/2 2.1 Application Programmers Guide</u>, by Kelly et. al.


<p>The EAOptionSet operations work on all of the EAs at once.  That way there
The EAOptionSet operations work on all of the EAs at once.  That way there
are no windows where a a particular EA may change, or the ordinal positions
are no windows where a a particular EA may change, or the ordinal positions
of the EAs change.
of the EAs change.


<p>The layout of the EA buffer is as follows:
The layout of the EA buffer is as follows:


<dl>
<dl>
<dt><b>FEA2</b>
<dt><b>FEA2</b>
<dd>The head for each item in the buffer.  The cbName contains the
<dd>The head for each item in the buffer.  The cbName contains the
Line 322: Line 306:
==Wrap-up==
==Wrap-up==


<p>This article has present a set of classes that can be used to manage an
This article has present a set of classes that can be used to manage an
application's set of options items.  The basic model is that there exists a
application's set of options items.  The basic model is that there exists a
set of default values for the options of an application; the user is
set of default values for the options of an application; the user is
Line 328: Line 312:
may be some sub-set of options that are associated with particular files.
may be some sub-set of options that are associated with particular files.


'''<p><strong>Industrial Strength.</strong>'''
'''Industrial Strength.'''


<p>There are several obvious features lacking which are required for an industrial strength implementation.  In the case of EAs, there are no checks for exclusive control while writing or reading the set of EAs from the file. In the realm of memory management, there are no safeguards against bad pointers.
There are several obvious features lacking which are required for an industrial strength implementation.  In the case of EAs, there are no checks for exclusive control while writing or reading the set of EAs from the file. In the realm of memory management, there are no safeguards against bad pointers.


One extension worthwhile making is that BOptionItems could have reference counts and perhaps references lists of the option sets where they are members.
One extension worthwhile making is that BOptionItems could have reference counts and perhaps references lists of the option sets where they are members.


'''<p><strong>Useful Test Tools</strong>'''
'''Useful Test Tools'''


There are a couple of REXX scripts included in the zip file.  In addition, there are several very useful tools at ftp-os2 such as EABrowse. The REXX
There are a couple of REXX scripts included in the zip file.  In addition, there are several very useful tools at ftp-os2 such as EABrowse. The REXX
script to print EAs only works when the the test program has closed the file, but I have no idea why I could not get it to share read access.
script to print EAs only works when the the test program has closed the file, but I have no idea why I could not get it to share read access.


[[Category:Languages Articles]]
[[Category:Languages Articles]]

Revision as of 02:28, 14 March 2016

Written by John Holt

Introduction

Most programs have a set of persistent options that are under user control. These options must be managed. This article discusses an approach to uniform management of option sets that may reside in a profile (INI file) or on a file's extended attributes (EA's).

The home for user specified general application defaults varies by system. In a UNIX system, environment variables or entries in .mwmrc (the Motif resource file) are used for system-wide defaults. In Windows and OS/2, INI files are used to hold the general application defaults. The OS/2 file system also provides an additional means of keeping file specific default settings.

Problem and Environment Description

Any general framework of classes must have certain attributes. Two attributes that are germane to this article are programmer-extensibility and harmony with the external environment. The facility for programmer extensions is simply providing a capability for the programmer to add new classes to be stored in the profile or extended attributes. The attribute of harmony with the external environment in this case is using profiles and extended attributes in a way that facilitates the use of existing tools.

Several base and implementation classes were developed. The task of managing persistent option sets is common to many programs, and therefore is an excellent candidate for reusable objects. The classes developed for the article represent an attempt to divide the implementation into classes so as to reuse common behaviors. The model for implementation is individual objects grouped into a set. The set is then stored on either a profile or in EAs.

Class Relationship Structure

Overview

The class structure is depicted in the diagram below. The relationship marked IS-A denotes class derivation. The relationship marked HAS-A shows that the class contains an object of the class. The KeyedOptionSet class is the result of a template generation using the KeyedSequentialSet class in the compiler collection classes library.

Class Relationship Structure

Set Classes

The option set behaviors have been factored into a base class (BOptionSet) and two implementation classes (INIOptionSet and EAOptionSet). The behaviors specific to manipulation of EAs or profiles is in EAOptionSet and INIOptionSet respectively.

BOptionSet is concerned with the maintenance of the list of items to be stored or retrieved. The following public methods are implemented:

addItem
Adds an item in the form of a pointer to a BOptionItem and a name for the option
delItem
Removes an item from the set
applName
The character string used as the high order name qualifier for EAs or the application name parameter for INI calls.
~BOptionSet
The virtual destructor used to clean up.

The BOptionSet object implements several protected methods used by the derived classes to access and manipulate the private data members. Those behaviors are:

firstItem
Position the set cursor to the first item
nextItem
Advance the set cursor to the next item in the set
item
Returns the pointer to an item in the set that is at the current cursor position.
copy
Makes this set a copy of another set.
keySpace
Returns the number of bytes needed to store the item key names.
flatSpace
Returns the number of bytes needed to store a flat version of the set of items.
itemCount
Returns the number of items in the set.
BOptionSet
The object constructors. There is a copy constructor and a constructor taking the application name and producing an empty set.

The KeyedOptionSet is generated from the IKeySet template class which is a keyed sequential set class in the IBM collection classes. A cursor is provided by the template. The object is a set of OptionSetElem objects.

The OptionSetElem is a simple object holding only the name of the option and a pointer to a BOptionItem object.

The derived classes EAOptionSet and INIOptionSet provide the implementations for the loadSet, storeSet, and resetSet as well as the usual expected complement of constructors and an equal operator.

The loadSet method updates each item with the value stored for that item's name. For the case of the items stored as EAs, the name is <application name>.<item name> and is mono-case. For objects stored in profiles, the name is forced to be in uppercase for consistency. The storeSet method is the reverse of the loadSet.

The resetSet method performs two operations. First, the set of names in the profile or EAs is made to correspond with the set of names in the set. Any names with a prefix other than <application name> are ignored. The second operation is to perform a storeSet.

Item Classes

The BOptionItem class provides some default implementations for the behaviors. The class interface is primarily intended to be the communication portal between an option set and the real data objects that are the elements of that set. The interface is:

itemType
Returns an enum indicating that the object is a character string or is binary data
itemSize
Returns the size of the item in bytes
exportData
Returns a flattened version of the item consisting of a length and a pointer to a string of bytes.
importData
Accepts a flattened version of the item for updating the real data object.

The StrOptionItem is a class definition for a string data object. The definition provides for the normal string behaviors.

Interface to Programmer Supplied Objects

The interface used by the option set to manipulate the items in memory is BOptionItem. Because all option items are derived from BOptionItem, the correct method is invoked.

When an addItem method is invoked on a set, the set obtains a BOptionItem type pointer to that item. When the set needs the size of the item, e.g. to allocate a buffer, the pointer to a BOptionItem is used to invoke the correct method. Similarly, the importData and exportData methods are invoked to move the value to and from the persistent store.

Adding New Object Classes

There are two different strategies that can be employed for extending the range of types of items that can be used in option sets. The programmer may implement interface behaviors directly in the object to be added, or the programmer can implement a wrapper class. If the class is under control of the programmer, direct implementation is usually the best approach. If the object to be used is provided by a third party class library, or is not a first class object (e.g., an INT) then a wrapper class is the right approach. The sample program uses the later approach because it is the most non-obvious of the two approaches. The reader will notice that the example is contrived.

Walkthrough the Sample/Test Program

The sample/test program illustrates how to create a set, add option items to that set, and store the set of option items. To exercise the code, these tasks were performed for both profile and EA based option sets. The file test.cpp [see test_src.zip within options.zip - Editor] should be followed for this discussion. You may also want look at BOptionSet.cpp and StrOptionItem.cpp [see source.zip within options.zip - Editor] for additional detail.

Housekeeping test.cpp Initialize several variables, get an anchor block, and open the test profile
create empty set test.cpp Declare object of type INIOptionset with a string and handle for the profile
  INIOptionSet.cpp Constructor for string and handle pass string to the BOptionSet for the application name, then hold onto the handle for subesquent file operations
create items test.cpp Declares for a StrOptionItem and two Option items. Option is a contrived

class defined in the file option.h. Both of these objects take a value an d a string

used as the name of the option. The name is passed on to the BOptionItem parent class.
Add items to the set test.cpp The addItem method is invoked for each item
  BOptionSet.cpp The addItem method allocates an OptionSetElem object to hold the

BOptionItem * and the name of the item. The element is then added to the set or replaces a previous item of the same name. The size of the item

name is recorded.
store the set INIOptionSet.cpp The items are stored on the profile test.ini. The set is traversed

and each item is individually written to the profile. The exportdata method is invoked for the BOptionItem involved. In the case of Item3,

StrOptionItem: :exportdata is invoked
  StrOptionItem.cpp A FlatItem data structure is allocated along with a buffer

to hold the string value. The pointer to this is returned. It will be the

callers responsibility to free the areas.
change the values of the items test.cpp The item values are change via the operator= method
reset the items from the store test.cpp The loadset method is invoked to update the items from the values held in the INI file. This restores them to their original values.
  INIOptionSet.cpp For each item in the option set: the size of the item is obtained

via the PrfQueryProfileItemSize() call; the item is read from the profle; the importData method for that item is invoked to update the item value in

memory
  StrOptionItem.cpp The importData method receives a FlatItem structur. The current string dat is freed, and the new value is kept.
delete some items test.cpp Items 1 and 2 are deleted from the set, and the version of the set on the profile is update via resetSet so that only Item3 remains.
Housekeeping test.cpp The profile data set is closed, and a regular sequential file is opened for the subsequent EA object testing.
Create an EAOptionSet test.cpp The EAOptionSet constructor is invoked, using the INIOptionSet object from above, amd the file handle for the sequential dataset opened above
  EAOptionSet.cpp The constructorsends the set name to the BOptionSet constructor for later use as the applicaion name. The handle of the file is retained.
reset EA's to known values test.cpp The resetSet operation is invoked which eliminates all option

items currently stored as EAs, and replaces them with the current set

contents, in this case, Item3.
Exercise the EAmethods test.cpp Items are added to the set; the set is stored; values are changed and reset in a manner similar to the tests for the INIOptionSet.

There are several things worth keeping in mind. The flow for the above example is designed to test the logic, and not how the objects would be used in a real application. In a real application, the steps for use would be:

  1. The data objects for all of the program options are declared, and initial values are given. All of these items must be derived from BOptionItem. The object may be derived from more than one class.
  2. The INIOptionSet item is declared using either the handle to an INI file just for this application, or the handle to the user ini file (HINI_USERPROFILE).
  3. The loadSet operation is invoked to pick up any defaults established by the user.

If there are file specific options, then the EAOptionSet object is used as well. The EAOptionSet object would be created from the INIOptionSet object, and if there were any non-file related options, those option items would be removed from the EAOptionSet via the delItem operation.

At an appropriate time, the option sets would be written to either the profile or the EAs or both.

Implementation notes

This section discusses some of the less obvious aspects of the set classes. To warrant the appellation of "less obvious," it had to be something I needed some effort to figure out. Which of course means that they may be completely obvious to you.

Profiles

The API for the profile was just a joy to use (compared to EAs), and the documentation makes everything pretty clear.

EAs

The EAs are not at all the same story. I used two books, and the documentation, to puzzle my way through how EAs worked. The books used were The Art of OS/2 2.1 C Programming, by Panov, Salomon, and Panov; the other book was OS/2 2.1 Application Programmers Guide, by Kelly et. al.

The EAOptionSet operations work on all of the EAs at once. That way there are no windows where a a particular EA may change, or the ordinal positions of the EAs change.

The layout of the EA buffer is as follows:

FEA2
The head for each item in the buffer. The cbName contains the length of the name field (NULL terminated) and cbValue contains the length of the data portion. The offset to the next item is sizeof(FEA2) + cbValue + cbName + this position + enough to get you to the next mod 4 offset. The value of the cbValue element is the size of the flat data item + two SHORTs.
ItemType
At FEA2 + cbName + 1 is the EAT value. In our case it is either EAT_BINARY or EAT_ASCII. There are other options available, but the remainder of this discussion would not apply.
Size
The is the size in bytes of the flattened data item

Wrap-up

This article has present a set of classes that can be used to manage an application's set of options items. The basic model is that there exists a set of default values for the options of an application; the user is permitted to modify those options in a persistent manner; and that there may be some sub-set of options that are associated with particular files.

Industrial Strength.

There are several obvious features lacking which are required for an industrial strength implementation. In the case of EAs, there are no checks for exclusive control while writing or reading the set of EAs from the file. In the realm of memory management, there are no safeguards against bad pointers.

One extension worthwhile making is that BOptionItems could have reference counts and perhaps references lists of the option sets where they are members.

Useful Test Tools

There are a couple of REXX scripts included in the zip file. In addition, there are several very useful tools at ftp-os2 such as EABrowse. The REXX script to print EAs only works when the the test program has closed the file, but I have no idea why I could not get it to share read access.