Logo

Introduction (Library 5/31/20)

Over a decade or so writing code on Windows for various version starting with DOS and Win 3.1. Every once in a while I would find a package (Ada language) that I seemed to be using over and over. The most basic thing in MMI is strings. The problem in windows is that there are many, many string packages. MFC uses CString, the C language implements the char for a single character and a zero terminated array of characters to represent a string. Later, when Templates were introduced into C++ the "basic_string" class was created and with it the "string" class. With the advent of Unicode the char (one Byte) became more or less deprecated, but not quite. There are several other representations of a string which are needed less frequently.

So, once one starts down the road of implementing a basic element of programming then other things need building and before long there are several modules. Other features of the OS need interfaces and packages were built and used in one or more applications. I'll try to summarize each package.

Just to be clear, there is no real definition of "package" in c++. I am using this terminology to define one or more classes which implement some functionality.

String Modules

The String Modules are very basic to everything in this library. During the dark age of software development there existed a language called SNOBOL. It allowed serious manipulation of text. It hid all of the details of the operations and attributes of strings and provided very powerful operations. Some of the flavor of the Library String Modules were determined by SNOBOL.

The String Package includes the following classes:

  • String -- defines a string which manages itself
  • Cstring -- An override for CString
  • TokenString
  • ToAnsi -- A little class that transforms a Unicode (16 bit char) String to an Ansi (8 bit char) String
  • ToUniCode -- A little class that transforms an Ansi string to an Unicode string.
  • toString -- A collection of functions to translate non-string objects into a String
  • RegExpr -- A class that use a Regular Expression to implement a match between a string and the regulare expression.
  • LexT -- A template for implementing a lexical analyser for parsing an input stream of characters into tokens.
  • CSVLex -- Comma Separated Values lines are parsed into tokens

More information may be found on the String Modules page.

stdafx.h

Well this is the Visual Studio standard include file prior to 2017. Now it is another include file (i.e. pch.h, so called precompiled header file). Several includes are included at the end of the file. The additions include the includes for the c++ packages for the string template, unicode functions and com utilities. It also includes an author written include files for some typedefs (std.h), the Strings Package and the Variant package. I've turned off the precompiled header option in the compiler for all applications but the stdafx.h (or pch.h) file is a good place to put those header files that must appear in each compilation unit.

MapsT, MapTable, and ACEDao -- The Access Database Interface packages

The package is implemented with one of the older data base interface packages. It implements several classes for reading and writing to tables and records in an MS Access 2016 database. This package implements an interface but is very primative. The packages MapsT and MapTable include this package and all programming to be simplified to tables and records in named tables. Of course, it takes an application (see CodeGen) to search a database and determine all the details of the database and construct c++ packages that are then included in another application (see RWracesDB).

Expandable

Arrays are troublesome in c++. They are fixed size. There are some templates that implement various storage techniques. However, somewhat before I discovered these features I decided to implement an expandable array. The implementation is, of course, a template and there are some serious requirements for a successful use.

Expandable‹Example, 2› examples;
typedef Expandable‹Example, 2› Examples;
Examples examples;

Note the s at the end of the example and Examples. This is to suggest multiple numbers of an Example. An Example must be a typedef, struct or class. It defines what one entity of examples is. The typical way examples is used is to index into a specific example: examples[3] is the fourth entity in the array.

To implement the expansion (which is automatic) the Example class must implement a copy constructor and an operator= (assignement). Furthermore, if the content of the array is a pointer to an object, then the destructor of the of the Example cannot delete (release) the object of the pointer.

The size changes when a request for an entry in the array exceeds the current size. The size is doubled each time it is increased and the contents are copied from the old version to the new version. Just to be clear, the actual address of an entry in this array may change with an entry. Don't save pointers to the entries unless there are to be no more additions.

The current number of actual entries in the array is given by "examples.end()" This is also the index of the next unused entry in the array. The way an example is added to the array is usually done with:

examples[examples.end()] = myNewExample;

myNewExample contains some data which needs to be placed at the end of the array. The function end() gives the index of the first unused entry in the array and the copy operation copies the data. The brackets function figures out if the array is too small and expands if as needed. Just using ordinary array access will achieve the desired goal.

An implementation note: When the actual storage is increased in size (doubled) the constructor for every element in the new array is called. When the storage is released the destructor for every element of the array is called. This is true when an expansion takes place as well as when the object is released implicitly or explicitly. During expansion the old version of the array is returned to the heap but just before the storage is released the destructor is called to release any objects in the elements of the array.

CDoc Package

With advent of Unicode I was faced with a dillema concerning the content of files and the character size in an application. It led me to develop a strategy of quickly pushing the details of the transformation of the characters to some packages that can be easily added to a new application. This led to a sub class of CDocument which would eventually lead to some standard IO funtions with little change to the overall organization of a Document in the MFC single doc application.

When VS17 creates a doc class if makes the doc class a sub-class of CDocument. Then it provides functions in the doc class so that the doc class can perform input/output functions. The programmer must replace CDocument with CDoc so that input/output between the application and a file may ignore the character size issue.

CDoc replaces 4 functions of CDocument with its own brand of IO:

  • OnOpenDocument -- Opens a file for reading and calls serialize
  • OnSaveDocument -- Opens a file for writing and calls serialize
  • DoFileSave -- Opens a file for writing and calls serialize
  • serialze -- A virtual function (i.e. must be implemented in sub-class) with a new Archive argument

As you can see, the MFC CArchive class has been replaced with a home-grown class, Archive. So the Doc class must delete the C in CArchive to get the benefit of CDoc. Once that is done, then the Archive class is available for further implementation.

virtual void serialize(Archive& ar);

The Archive package is strictly a front end for the FileIO package. The Archive package provides basic read/write operations as well as attributes concerning being opened, loading or storing. It also provides the "‹‹" operator for Unicode characters and strings (which I haven´t expanded to include other types). All the hard work is performed in the FileIO package. It performs buffered IO operations along with the translation from 8 bit to 16 bit characters. It is complete enough to be used independently of the CDoc and Archive.

CScrView

When VS17 produces a one document, one window application the View package is populated with a few functions that provide the functionality to draw on the window and to provide basic printing functions. Over the years I developed a package (called DisplayView if I recall correctly) that did the bulk of the job using a linked list of display entities (attributes and text) as the basis of what was to appear on the window. It also allowed the window to be printed in the standard MFC way (Setup, Preview and Print). When I started writing this document I realized that CDoc was a subclass of CDocument and the DisplayView functions could be turned into a subclass of CScrollView. That has happened and CScrView is the result.

This class only works with one document/one View applications as far as I know. Right after creating the application (MFC, One Document, View based on CScrollView) most of the guts of the AppView.h and AppView.cpp is deleted and all mention of CScrollView is replaced with CScrView. Then anything sent to NotePad is displayed on the view (usually have to execute an invalidate() to get the view repainted). Furthermore, the print commands just print whatever is in the view (even the hidden sections). I suggest looking at MakeApp or TestApp in the MakeApp solution for an example of a view.

There is one function that may be called explicitly by the user.

void setFont( TCchar* f, int points = 120)

The font size is code in tenths of a point. Should the user actually ask for a 12 point font by using setFont(Arial, 12) the function bumps it by 10 (if the fontSize argument is less than 70 it is multiplied by 10).

All the other functions are utilized either by MFC or functions defined elsewhere.

CApp

Once a program learns a new trick it is applied everywhere! CApp supplies some much needed capability to the VS17 generated Application (Main Program in C terms, Main Object in c++ terms). For example, the constructor for CApp will create a "application identifier" from the resource (.rc) file using the CompanyName. ProductName and Version which are defined in the rc file.

MFC allows one to write into the Title Bar of the window. However in a Doc/View application the window is the MainFrame. furthermore, MFC seems determined to write into the title bar itself. So a kludge has been devised involving a CMainFrm subclass of CFrameWndEx that allows one to write the title in one call or two calls:

setAppName(‹appName›); setTitle(‹title›);

If the setTitle function is used alone then the ‹title› appears on the title bar of the main window of the app. If both functions are utilized, the title bar shows :‹appName› -- ‹title›".

CApp facilitates calling the functions in CMainFrm by finding the pointer to the mainframe and calling the corresponding function.

Three other functions are very useful in MFC applications. CApp contains:

CDocument* getDoc();
CView*     getView();
void       invalidate();

Access to these functions is provide in the main App by some simple functions:

MakeAppDoc*  doc()  {return (MakeAppDoc*)  CApp::getDoc();}
MakeAppView* view() {return (MakeAppView*) CApp::getView();}

And finally to make them visible through the entire application the following inline functions are usually defined:

inline void invalidate() {theApp.invalidate();}
inline MakeAppDoc* doc() {return theApp.doc();}
inline MakeAppView* view() {return theApp.view();}

The only reason for the functions in the App package is to translate the MFC pointers to the app's subclasses (e.g. MapkAppDoc* and MakeAppView*).

CMainFrm

CMainFrm adds only the functions:

void setAppName(TCchar* name);
void setTitle(  TCchar* rightPart);

NotePad, Note, DisplayDev, Display, FontMgr, and Node

CScrView uses the DisplayDev, Display and FontMgr packages to display in the view window and print the content of the view window. In the Document package and other places in the program the NotePad provides a set of operations to write to the view. NotePad is instantiated once, the object is named "notePad". notePad implements many streaming output operators (i.e. "‹‹"). Furthermore, there are tab, font and some other stream modifiers.

NotePad is implemented with a linked list. The Note object contains everything there is to know about output of one text sequence and any relevant modifiers. The Note object includes the Node Package. The Node package implements a single pointer linked list using C like code (since it was implemented first back in the "C" days).

GetPathDlg, FileIO, filename, filePaths, filesrch and SrchFile

These packages provide various services with respect to the file system. Some are quite old written in the "C" days. Others utilize c++ to advantage. One of the packages uses the "match" package, a regular expression search algorithm.

Archive -- A Replacement for CArchive

The document class, CDoc, that is subclassed from CDocument implements a feature of CDocument that I had trouble with. CDocument was written back in the day when ASCII characters were always 8 bits (earlier they had been 6 bits, then 7 bits and finally someone notice powers of 2 in computing and characters became 8 bits and internal word size became powers of 2, i.e. 16, 32, 64 bits). With the advent of computing world wide, character size had to change. The Unicode character set is used in this library (mostly because VS17 seems to favor it). However garden variety files still use 8 bit characters.

Since there must be a translation between ANSI, 8 bit characters, and Unicode, 16 bit characters when reading and writing to files I decided that it would be done at the actual reading and writing of data from/to the file. This required modifying the serialize function in the CDocument module in an application. The easiest way to do that was replace the CArchive package. Thus Archive was invented.

The CDoc subclass of CDocument implements the virtual functions for OnNewDocument, OnNewDocument, OnNewDocument and serialize. The three former functions open or creates the files and calls serialize with an Archive object all ready to read or write from/to the file. Archive presents a streaming output interface as well as traditional read and write functions. There are some functions to support tabbing in the file by counting characters.

The library includes on package that implements use of Archive to do input/output, namely the NotePad package. Here is the code in the TestAppDoc module (see MakeApp application) for utilizing the NotePad archive functions:

void TestAppDoc::serialize(Archive& ar) {
  if (ar.isStoring()) {notePad.archive(ar); return;}
Tchar ch;
String s;
  while (ar.read(ch)) {
    if (ch == _T('\n')) {notePad << s << nCrlf; s.clear(); continue;}
    if (ch == _T('\r')) continue;
    s += ch;
    }
  if (!s.isEmpty()) notePad << s << nCrlf;
  invalidate();
  }

This particular code segment treates the file as just a sequence of lines. However very often the file is parsed in some way upon input from the file. If the notePad object is the destination for the results of parsing the input, then the notePad streaming input functions are useful for storing the data. At other times some other data structure is used for storing the input and the serialize function is used to implement the transfer from the file to the data sturcture.

LexT, Csv and Token Packages

An input stream is often needed to be split up into discernable units. Compilers usually call these units "tokens". LexT is a template for breaking an input stream into tokens. A Token is a string, sometimes a value and a code (enum value) that identifies the type of token. LexT requires the user to classes to determine input and output and also requires some information about some processing details. These packages are not being used in any applications at the time of writing.

match Package

Regular Expressions can express some interesting patterns. The match.h header file lists the special characters in the pattern. The pattern is built in an object and then that object is used to match (or not match) any string that is presented to it.