Welcome to the April, 1996 Issue of the C/370 Newsletter

IBM Corp 1996

                 370 370 370 370 370 370 370 370 370 370
             370 370 370 370 370 370 370 370 370 370 370 370
             370 370 370 370 370 370 370 370 370 370 370 370 370
         370 370 370 370 370 370 370 370 370 370 370 370 370 370
         370 370 370 370 370 370 370 370 370 370 370 370 370 370
         370 370 370 370 370 370 370 370 370 370 370 370 370 370
         370 370 370
         370 370 370
         370 370 370
         370 370 370
         370 370 370 370 370 370 370 370 370 370 370 370 370 370
         370 370 370 370 370 370 370 370 370 370 370 370 370 370
         370 370 370 370 370 370 370 370 370 370 370 370 370 370
             370 370 370 370 370 370 370 370 370 370 370 370 370
             370 370 370 370 370 370 370 370 370 370 370 370
                  370 370 370 370 370 370 370 370 370 370

C/370* Compiler News

April 1996 Volume 4 Number 2


Table of Contents

Welcome to the April, 1996 Issue of the C/370 Newsletter

  • In This Issue
  • IBM VisualAge for C++ for Windows, Version 3.5
    Using the Collection Class Libraries
    What You Have Learned
    You ask, we answer
    A word from your editor
    Coming Soon
  • Reader's Comment Form

  • Welcome to the April, 1996 Issue of the C/370 Newsletter

    In This Issue

    ( )

    IBM VisualAge for C++ for Windows, Version 3.5

    IBM VisualAge for C++ for Windows, Version 3.5 brings the same set of tools and features of the award winning VisualAge for C++ for OS/2 to the Windows platforms. A consistent set of tools across multiple environments ensures that developers' skills are portable from platform to platform. Now, mission-critical, object-oriented applications are within easy reach using VisualAge for C++'s
    powerful "construction from parts" application paradigm.

    IBM VisualAge for C++ for Windows delivers:

    Construction From Parts

    With IBM VisualAge for C++ for Windows, Version 3.5, application construction has never been easier. Even the most complex applications can be constructed from the large set of pre-defined parts from the IBM Open Class Library. The IBM Open Class Library contains over 600 pre-defined and pre-tested software building blocks, along with over 5000 methods (functions) that are tightly integrated into the VisualAge for C++ toolset. You can also create your own parts and import them into the Visual Application Builder. These parts can then be assembled quickly and easily with the Visual Application Builder, and your application generated with the click of a button - reuse is now real world!

    The Visual Application Builder generates source code consistent with the ANSI C++ draft standard that is compiled into a highly optimized application. No performance compromise and a royalty-free runtime environment.

    Object-Oriented Access to Relational Data

    Now, make quick work of bringing existing relational data into the object world. The Data Access Class Builder visually maps DB2*, Sybase** and Oracle** relational database tables into objects with a single click. All of the C++ and SQL code generation is done for you - simply import these data objects into the Visual Application Builder and construct your application. An underlying set of class libraries handles the complexities of data access and provides both C++ and SOM* interfaces.

    IBM Open Class Library

    IBM Open Class Library is a comprehensive set of C++ class libraries that gives you an extensive foundation of building blocks for creating your applications. While Open Class handles the complexity of low-level APIs, you can focus on your application. Because Open Class provides the foundation parts used in the Visual Application Builder, you become productive immediately by using real objects directly.

    Open Class also provides the portability layer onto which applications can be targeted towards many different operating environments. An application written to Open Class can be easily ported to other operating system environments, and when re-compiled for the target environment, will adopt the "look and feel" of the target system. IBM Open Class also provides a Compound Document Framework which enables the user to develop an OLE 2 container or server application. The Framework, which is the first injection of Taligent Technology into IBM Open Class, provides an abstraction from OLE 2 into higher level classes as well as a base for a part architecture that is consistent across both OLE 2 and OpenDOC. The Framework reduces the coding of a OLE 2 component from a very large number of low level OLE 2 API calls to a very small number of Compound Document Framework over-rides. In other words the Framework provides most of the default behaviour that an OLE 2 container or server needs. The Framework also provides the basis for a portability layer to exploit the OpenDoc run-time environment.

    With IBM Open Class, you have the flexibility of a consistent programming interface across a wide range of platforms including: OS/2* Warp*, MVS/ESA*, AIX*, OS/400*, Solaris**, Windows NT, Windows 95 and Windows 3.1 / Windows 3.11. With IBM's C++ environments on these platforms, you can code your application once and with minor system specific changes, deploy it anywhere in your enterprise.

    Powerful C++ Tools!


    IBM VisualAge for C++ for Windows sets the pace with a complete set of integrated tools built for the C++ developer.

    Get Started Fast With WorkFrame and Project Smarts!

    WorkFrame gives you a productive place to create and manage your C++ code. With "Build Smarts", you build code in either debug, browse or optimized modes - without having to manage compiler options manually. With WorkFrame, you move naturally from tool to tool. In addition, "Project Smarts" gives you automatically configured skeleton applications to start your coding immediately.

    Fast, Easy Access with the C++ Browser

    Explore and understand your code and class libraries quickly with the IBM VisualAge for C++ for Windows browser. Use the QuickBrowse feature to get information on C++ code even before you compile - exactly when you need it.

    Quality or Speed? Pick two.

    Get your application up to speed with two powerful tools.
    With the VisualAge debugger's intuitive user interface, you can debug at the source level, set breakpoints, handle advanced C++ functions such as templates and exceptions, and isolate difficult memory management bugs. Use the Performance Analyzer to fine tune your application's performance. Through graphical representations of trace information, you discover the hotspots and bottlenecks in your programs.

    Easy Editing

    Editing source code is a snap using the syntax - highlighting editor. Because editors are a personal choice, this one can be customized to your way of working.

    Superior C++ Code

    The 32-bit C/C++ compiler delivers rock solid code ready to meet the demands of your business and generates applications which are highly optimized for Windows. In addition, code can be optimized for any Intel processor from the i386** to the Pentium**, and even the newest chip, the Pentium Pro**.

    Easily Create SOM Objects
    IBM's System Object Model* (SOM) directly from familiar C++ syntax simply by turning on a compiler option. The compiler will also generate the corresponding OMG CORBA-compliant Interface Definition Language (IDL) for inter-language or Distributed SOM applications. In addition, you can browse and debug SOM objects with the IBM VisualAge for C++ for Windows tools. SOM objects can be imported to the visual builder so distributed object applications can be assembled quickly.

    For more information visit our homepage on the Web at the following URL: http://www.software.ibm.com/ad/visualage_c++/.


    Using the Collection Class Libraries

    In previous issues of this newsletter (October, 1995 and January, 1996) we developed a program that builds a simple collection of elements and adds some elements to the collection. Our source code, excluding bike.h, looked like the following when we had finished.

    step.c & bike.h

     // step.C
    
     #include <iostream.h>
     #include <ibag.h>
     #include "bike.h"
     typedef IBag<Bicycle> MyCollectionType;
     MyCollectionType MyCollection;
    
     void addItem() { /* ... */ }
     IBoolean printItem(Bicycle const& bike,void*/*Not used*/)
      { /* ... */ }
     void listItems() { /* ... */ }
     void removeItem() { /* ... */ }
     void showStock() { /* ... */ }
    
     bike.h
    
     #include <istring.h>          // access to IString class
     #include <iostream.h> // access to iostream class
    
     class Bicycle {
      public:
      IString Make;
      IString Model;
      int Type;
      int Price;
      Bicycle() : Make(""), Model(""), Type(0), Price(0) {}
      Bicycle(IString mk, IString md, int tp, int pr) :
      Make(mk), Model(md), Type(tp), Price(pr) {}
      IBoolean operator== (Bicycle const& b) const;
      IBoolean operator< (Bicycle const& b) const;
     };
     istream& operator>> (istream& is, Bicycle& bike);
     ostream& operator<< (ostream& os, Bicycle bike);
    

    bike.c

     #include <istring.h>
     #include "bike.h"
     enum bikeTypes { Racing, Touring, MountainBike };
     IString btype[3] ={ "Racing", "Touring", "Mountain Bike"};
    
     IBoolean Bicycle::operator== (Bicycle const& b) const
        {return ((Model==b.Model)&&(Make==b.Make));}
    
     IBoolean Bicycle::operator< (Bicycle const& b) const
         {return ((Make<b.Make)||(Make==b.Make && Model<b.Model));}
    
     istream& operator>> (istream& is, Bicycle& bike) {
        char make[40], model[40];
        char typeChoice;
        float price;
        int type=-1;
        cin.ignore(1,'\n'); // ignore linefeed from previous input
        cout << "\nManufacturer: ";
        cin.getline(make, 40, '\n');
        cout << "Model: ";
        cin.getline(model, 40, '\n');
        while (type == -1) {
           cout << "Racing,Touring or Mountain Bike (R/T/M):";
           cin >> typeChoice;
             while (cin.fail()) {
               cin.clear();
               cin.ignore(1000,'\n');
               cerr << "Racing,Touring or Mountain Bike (R/T/M):";
               cin >> typeChoice;
             }
             switch (typeChoice) {
      { /* 7 case statements see previous example */ }
             }
          }
          cout << "Price ($$.$$): ";
          cin >> price;
          while (cin.fail()) {
             cin.clear();
             cin.ignore(1000,'\n');
             cerr << "Enter a numeric value: ";
             cin >> price;
          }
          price*=100;
          bike=Bicycle(make,model,type,price);
          return is;
       }
    
     ostream& operator<< (ostream& os, Bicycle bike) {
       return os << bike.Make
                 << "\t" << bike.Model
                 << "\t" << btype[bike.Type]
                 << "\t" << float(bike.Price)/100;
     }
    

    Step 4

    : Changing the Collection

    When you design an actual application using the Collection Classes, you should choose the collection best suited to your program at the design stage. Nevertheless, requirements may change, and if you have followed the techniques used in this step such as specifying the collection type with a typedef, you can change the collection type without having to rewrite the entire application. Only minor changes are required to existing code, and a few simple element or key-type functions may need to be added or changed.

    Requirements

    The program should be changed so that two bicycles of the same model and make can have different type and price information. When users ask to delete a bicycle, they should not have to enter the bicycle and price information; instead, a list of all bicycles of the specified make and model should be displayed, and the user should be able to select which bicycle to remove from the collection. The showStock() function should also be implemented, so that it shows the number of a given make and model of bicycle currently in the collection.

    Setup

    Copy the files bike.h, bike.C, step.C, and main.C from the Step3 dataset to the Step4 dataset, and then change your current dataset to the Step4 dataset.

    Implementation

    The collection must have the following characteristics:

    You can use The C/C++ for MVS/ESA Class Library Reference (SC09-2001) to determine what collection best meets the requirements listed above. Begin by applying one requirement to the figure to narrow down the number of possible collections. Apply a second requirement to the remainder, and continue until you have found all valid collections. In this example, there is one valid collection, selected as follows:

    A relation differs from a bag in that it is instantiated using a key type as well as the element type, and requires the following additional functions which are described below:

        Element type: Key access
    
        Key type:    Equality test and hash function.
    

    Changing the Collection Type Definition

    Before you redefine the functions in step.C, you need to change the include file and typedef for the collection type so that they use relation instead of bag:

             // step.C
             #include <irel.h> // was ibag.h
             //...
             typedef IRelation<Bicycle,IString> MyCollectionType;
             // was typedef IBag<Bicycle> MyCollectionType;
    

    Notice that IRelation takes two template arguments, an element type and a key type. All collections that have a key must be defined with a template argument for key type as well as one for element type.

    Ordering Relation

    A relation does not require an operator for ordering relation (operator<). You defined this operator when the collection was implemented as a bag. You should comment it out or remove it for this implementation. This function is declared in bike.h and defined in bike.C.

    Implementing a Key

    The key consists of the make and model of the bicycle. You can use an IString to implement the key. Because the return value of the key() function must be a const reference, and because the key() function cannot change the element, the key must be determined before the key() function is called. The logical place to do this is in the element constructor (in bike.h), because the overhead of generating the key only occurs once per element. You can add a key data member to the collection, and have it initialized when the copy constructor is called. In the example below, the key is named MMKey (which stands for Make/Model Key):

             // in bike.h:
             class Bicycle {
                   IString MMKey; // add a private data member for the key
                public:
                   // public data members and member functions
                   Bicycle(IString mk, IString md, int tp, int pr);
                   // ...
             };
    
             // in bike.C:
             Bicycle::Bicycle(IString mk, IString md, int tp, int pr) .
                     Make(mk), Model(md), Type(tp), Price(pr),
                     MMKey(mk+md) {}
    

    Defining Key Access

    The key access function must be defined outside of the element class. It has one argument, whose type is the element type. The key access function must call a member function that returns the key, in this case a function named getKey(). (The actual name does not matter.) The member function accesses the private data member MMKey.

             // in bike.h:
             class Bicycle {
                   IString MMKey;
                public: // ... data members and member functions
                   IString const& getKey() const;
                };
    
             inline IString const& key (Bicycle const& bike)
                { return bike.getKey(); }
    
             // in bike.C:
             IString const& Bicycle::getKey() const { return MMKey; } .

    The key access function must be declared with the name key(), with a const reference to the key as its return value, and a const reference to the element as its argument.

    Equality Test

    Equality for elements should be defined such that the key (that is, the make and model), the type, and the price are the same for two bicycles. The operator== function in bike.C can be redefined as follows:

             IBoolean Bicycle::operator== (Bicycle const&b) const {
                        return (MMKey==b.MMKey && Type==b.Type && Price==b.Price);
                 }
    

    The hash function provides a shortcut for Collection Class search functions to find matches to a key. The search functions first call the hash function on a key for which they need to locate an element. They use the hash value returned to look for matches to that hash in a hash table. They then use the full key to determine which of the hash function's matches have the correct key. The hash key-type function is not a member function of the element's class. It is called by the searching function, with a key argument (the key on which to derive the hash) and an unsigned long (the maximum hash value). The return value is the hash, and it cannot exceed the maximum hash value. The hash function should be defined in step.C and must have the following return type and parameters:

             unsigned long hash ( IString const& keyName, unsigned long hashInput)
    

    You can define the hash using the hashing function provided in
    istdops.h for char* values:

             unsigned long hash (IString const &aKey, unsigned long hashInput) {
                 return hash( (const char*)aKey, hashInput);
                 }
    

    Using Cursors to Remove Items

    A Collection Class cursor (not related to the cursor used to move about a cursor screen) is a reference to an element in a collection.

    The removeItem() function must be redefined so that it requests the make and model of bicycle to remove, lists all matching bicycles, and lets the user choose which match to remove. Once matching bicycles have been displayed, a cursor can be used to locate the bicycle the user wishes to delete. The cursor is defined as follows, immediately after the collection MyCollection is declared, in step.C:

             MyCollectionType::Cursor thisOne (MyCollection); .

    After the user enters a make and model to search for, the removeItem () function should iterate through all elements that match the key, by using locateElementWithKey() to find the first matching element, and locateNextElementWithKey() to find all subsequent matching elements. Both these functions require a cursor as their second argument, and the cursor points to the located element when the functions return. The first part of removeItem() can be redefined as follows:

             void removeItem() {
                Bicycle tbike;
                int choice, cursct=1;
                cout << "\nRemove an item";
                cin >> tbike;
                if (MyCollection.numberOfElementsWithKey(tbike.getKey()) > 0) {
                   MyCollection.locateElementWithKey(tbike.getKey(), thisOne);
                   cout<<cursct<<". "<<MyCollection.elementAt(thisOne)<<endl;
                   for ( cursct=2;
                         MyCollection.locateNextElementWithKey(
                                           tbike.getKey(), thisOne);
                         cursct++)
                         {  cout << cursct << ". "
                                 << MyCollection.elementAt(thisOne) << endl; }
                //... Remainder to be defined later
                }
    

    In the above fragment, the user is asked for a bicycle make and model to remove. If any elements match the make and model (this is determined by testing the numberOfElementsWithKey() function for a nonzero return), all such elements are located by key. The locateElementWithKey() function sets its cursor to point to the first matching element, and the locateNextElementWithKey() function advances the cursor from the current match to the next match in the collection. The elements are accessed for output using the elementAt() function, which returns a reference to the element pointed to by the cursor argument.

    Once the matching elements have been displayed with a number beside each one, the program should ask the user to enter a number matching the number of the element to remove. The matching elements can then be iterated over again until the number of elements iterated over matches the user's selection, and the element pointed to by the cursor is then deleted. The following code excerpt is the second part of the removeItem() function:

                   cout << "\nEnter item to remove, or 0 to return: ";
                   cin >> choice;
                   if (choice<=0 || choice > cursct) return;
                   MyCollection.locateElementWithKey(tbike.getKey(),thisOne);
                                // locate the first matching element again
                   for ( cursct=2;
                         cursct<=choice &&
                         MyCollection.locateNextElementWithKey    // check for val
                                      (tbike.getKey(), thisOne);  // next match
                         cursct++)
                         ; // null loop - header contains the code to be executed
                   MyCollection.removeAt(thisOne);
                   }
                else
                   cerr << "No bicycles of this make and model were found.\n";
    
               // The closing brace below was already part of removeItem().
               // Do not duplicate it.
                }
    

    Note: The locateNextElementWithKey() function invalidates the cursor if it cannot find a next element with the key provided. An invalidated cursor does not point to any element of the collection. Some flat collection member functions that use cursors require that the cursor be valid (locateNextElementWithKey() is one such function). Before you use a cursor with such a function, you need to validate the cursor by using a function that takes a cursor as argument but does not require a valid cursor on entry. locateElementWithKey() is one such function.

    In both excerpts of removeItem() above, the elements with matching keys are iterated over by code in the header of the loop. In the second case, the loop has no body. You can use this coding style because all the locate... functions have a return type of IBoolean, which can be used in condition tests such as those in loop control expressions.

    Showing Stock Information

    showStock() must be rewritten so that, for a given make and model, it displays the number of matching elements in the collection. The numberOfElementsWithKey() function can be used:

             void showStock() {
                Bicycle tbike;
                int count;
                cout << "Stock information for a model";
                cin >> tbike;
                count=MyCollection.numberOfElementsWithKey(tbike.getKey());
                if (count!=1)
                   cout << "Currently there are " << count << "bicycles";
                else
                   cout << "Currently there is 1 bicycle ";
                cout << "of this make and model in stock." << endl;
                }
    

    Changing the Input Operator and addItem()

    As the program now stands, the input operator requests input for all data members of Bicycle, including type and price information. This means that, when you select an item to remove or to show stock information on, you must specify type and price information even though this information is ignored. Therefore you need to move the request for type and price information out of the operator>> definition in bike.C and into addItem(), so that the user only needs to enter type and price information when an item is being added to the collection. You also need to add the enumeration bikeTypes to step.C so that addItem() has access to them.

    See the "Source Files" section below for the changes required to addItem() and operator>>.

    Source Files for Step 4

    The main program in main.C has not been changed. The following excerpts show the layout of code between step.C and bike.h. Function bodies that remain unchanged from the preceding step have been replaced by ellipses.

    bike.h & bike.c

      //bike.h
     #include <istring.h>        // access to IString class
     #include <iostream.h>       // access to iostream class
    
     class Bicycle {
           IString MMKey;
           public:
              IString Make;
              IString Model;
              int Type;
              int Price;
              Bicycle();
              Bicycle(IString mk, IString md, int tp, int pr);
              IBoolean operator== (Bicycle const& b) const;
        //    IBoolean operator< (Bicycle const& b) const;
              IString const& getKey() const;
           };
    
        inline IString const& key (Bicycle const& bike)
           { return bike.getKey(); }
    
        istream& operator>> (istream& is, Bicycle& bike);
        ostream& operator<< (ostream& os, Bicycle bike);
    
    -----------------------------------------------------
        bike.C
    
    #include <istring.h>
    #include "bike.h"
    enum bikeTypes { Racing, Touring, MountainBike };
    IString btype[3] ={ "Racing", "Touring", "Mountain Bike"};
    
    Bicycle::Bicycle() .: Make(""), Model(""), Type(0), Price(0) {}
    Bicycle::Bicycle(IString mk, IString md, int tp, int pr) {
     Make=mk;
     Model=md;
     Type=tp;
     Price=pr;
     MMKey=Make+Model; }
      // Comment out the ordering relation operator
      // IBoolean Bicycle.::operator< (Bicycle const& b) const
      // {return ((Make<b.Make)||(Make==b.Make && Model<b.Model));}
    IBoolean Bicycle::operator== (Bicycle const&b) const {
     return (MMKey==b.MMKey && Type==b.Type && Price==b.Price);
     }
    IString const& Bicycle::getKey() const { return MMKey; }
     istream& operator>> (istream& is, Bicycle& bike) {
     char make[40], model[40];
     char typeChoice;
     float price=0;
     int type=-1;
     cin.ignore(1,'\n'); // ignore linefeed from previous input
     cout << "\nManufacturer: ";
     cin.getline(make, 40, '\n');
     cout << "Model: ";
     cin.getline(model, 40, '\n');
     bike=Bicycle(make,model,type,price);
     return is;
    }
    //unchanged
    ostream& operator<< (ostream& os, Bicycle bike) {/* ... */}
    

    step.c

     #include <iostream.h>
     #include <irel.h> // was ibag.h
     #include "bike.h"
     enum bikeTypes { Racing, Touring, MountainBike };
     typedef IRelation<Bicycle,IString> MyCollectionType;
     MyCollectionType MyCollection;
     MyCollectionType::Cursor thisOne (MyCollection);
     IBoolean printItem (Bicycle const& bike, void* /*Not used*/)
       { /* ... */ }
     void addItem() {
        Bicycle tbike;
        char typeChoice;
        float price;
        int type=-1;
        cout << "Enter item: ";
        cin >> tbike;
        while (type == -1) {
           cout << "Racing, Touring or Mountain Bike (R/T/M):";
           cin >> typeChoice;
           while (cin.fail()) {
              cin.clear();
              cin.ignore(1000,'\n');
              cerr << "Racing, Touring or Mountain Bike (R/T/M):";
              cin >> typeChoice;
              }
           switch (typeChoice) {
     { /* 7 case statements see previous example */ }
           }
        }
        cout << "Price ($$.$$): ";
        cin >> price;
        price*=100;
        tbike.Type=type;
        tbike.Price=price;
        MyCollection.add(tbike);
     }
     void listItems() {/* ... */ }
     void removeItem() {
     Bicycle tbike;
     int choice, cursct=1;
     cout << "\nRemove an item";
     cin >> tbike;
     if (MyCollection.numberOfElementsWithKey(tbike.getKey())>0) {
      MyCollection.locateElementWithKey(tbike.getKey(), thisOne);
      cout<<cursct<<". "<<MyCollection.elementAt(thisOne)<<'\n';
      for ( cursct=2;
            MyCollection.locateNextElementWithKey(
                              tbike.getKey(), thisOne);
            cursct++)
            {cout << cursct << ". "
                  << MyCollection.elementAt(thisOne) << '\n'; }
      cout << "\nEnter item to remove, or 0 to return: ";
      cin >> choice;
      if (choice<=0 || choice > cursct) return;
      MyCollection.locateElementWithKey(tbike.getKey(),thisOne);
                   // locate the first matching element again
      for ( cursct=2;
         cursct<=choice &&
         MyCollection.locateNextElementWithKey  //check for val
                      (tbike.getKey(), thisOne);//next match
         cursct++)
         ; // null loop-header contains the code to be executed
      MyCollection.removeAt(thisOne);
      }
        else
         cerr<<"No bicycles of this make and model were found.\n";
        }
     void showStock() {
        Bicycle tbike;
     { /* showStock function from previous example */ }
         }
    

    Running the Program

    You can enter multiple bicycles of the same make and model, with different price or type information, and all such models will appear when you select the "List items" option. When you ask for stock information, the program displays the number of elements in the collection that match the make and model information you specify. When you remove an item, the program asks you for the make and model, displays a list of matching items, and lets you specify which item to remove. The program removes that item.

    Step 5

    : Changing the Implementation Variant

    You should pursue changing the default implementation to an implementation variant only after the program is functionally complete and has been fully debugged. The purpose of changing to a nondefault implementation variant is to improve performance. This step shows you how to change the code defined in "Step 3: Changing the Element Type" so that it is functionally equivalent, but uses IBagOnSortedDilutedSequence rather than IBag. The step assumes that you have done some analysis of your code, and have determined that this implementation variant may provide better performance. In the case of a full-fledged application, once you change the implementation variant, you should compile the program and time it against the original implementation to determine whether there is a worthwhile gain in performance.

    Requirements

    The only implementation variant for a relation is the variant that allows you to use a generic operations class.

    If the collection were still a bag, a number of implementation variants would be available. In the third step, you used the default implementation variant for a bag. Other implementation variants are:

    For this step, you will use the code from the third step as a starting point, and change the default Bag implementation.

    Setup

    Copy the files bike.h, bike.C, step.C, and main.C from the Step3 directory (not the Step4 directory) to the Step5 directory, and then change your current directory to the Step5 directory.

    Implementation

    To change the default implementation of a collection to another implementation variant, you need to change the Collection Class file that you include, the collection typedef, and potentially the element and key functions.

    Implementation Variant Header Files

    To determine the correct header file to include, consult the "Class Implementation Variants" section of the chapter on Bag in the Class Library Reference. The header file to include for IBagOnSortedDilutedSequence is shown as ibagsds.h. You therefore change the header file to include as follows:

             // in step.C
             // old:
             /* #include <ibag.h> */
             // new:
             #include <ibagsds.h>
    

    Templates for Implementation Variants

    To determine the correct template to instantiate for the collection typedef, see the implementation variant in the appropriate collection chapter. In this case, you would look for "Bag on Sorted Diluted Sequence" in Bag. The collection is shown there as:

             IBagOnSortedDilutedSequence  <Element>
             IGBagOnSortedDilutedSequence <Element, ECOps>
    

    Because you are not defining a generic operations class, you need to use the first implementation variant. You therefore change the typedef for the collection as follows:

             // old: typedef IBag <Bicycle> MyCollectionType;
             // new:
             typedef IBagOnSortedDilutedSequence <Bicycle> MyCollectionType;
    

    Element Type Functions

    To determine the required element type functions, see the "Element Type" section for the implementation variant. In the case of IBagOnSortedDilutedSequence, the only element type function listed that was not listed for a Bag is the default constructor, which is already defined in Bicycle for other reasons. If other functions are required for a given implementation variant you choose to use in an application, use the information on implementing a hash function in Step 4 for hints on where to place and how to code such functions.

    No further changes are required. For this step, the only implementation variant that would require additional element type functions is IBagOnHashKeySet, and the required additional function is a hash function, which is already described in "Step 4: Changing the Collection".

    Running the Program

    The program should have the same behavior, for a given set of inputs, as the program from "Step 3: Changing the Element Type". In a complex application, a change in performance might occur, but in all cases the behavior of a correctly coded program should be identical for different implementation variants of the same collection class.


    What You Have Learned

    Once a C++ program using the Collection Classes is functionally complete and error-free, you can focus on performance. The key to good performance of Collection Classes programs is to select the appropriate implementation variant of a given collection. Although this step did not explain which implementation variant to choose (since this is largely dependent on the class type being used in the collection and on other factors beyond the scope of the steps), it showed you how to change the implementation variant once the appropriate variant has been selected. See "Features of Provided Implementation Variants" in the C/C++ for MVS/ESA Class Library User's Guide (SC09-2000) for guidance on what implementation variants to select for a given application.

    Using the Default Classes

    When you are learning to use a particular collection, you should first use the default class of that collection, so that you can gain a fundamental understanding of the collection before you approach the implementation variants of the collection.

    For more information on using the C++ Class Libraries please refer to the C/C++ for MVS/ESA Class Library User's Guide (SC09-2000) and the C/C++ for MVS/ESA Class Library Reference (SC09-2001).


    You ask, we answer

    These questions are culled from a variety of sources: from various question and answer databases, from customer calls, and from our own experiences. If you have a question you'd like answered, please send it to IBMMAIL(CAIBMRXZ) or inetc370@vnet.ibm.com. We'll answer your question and perhaps print your question and answer in the next newsletter.

    Question - Debuggers

    Do you know of any Debuggers (interactive or screen based) that can be used in developing MVS C++ programs? Is there a manual somewhere I can read?

    Answer

    One component of C/C++ for MVS/ESA is the Debug Tool. It is available with the full function version of the product. The functionality in the Debug Tool (an MFI debugger) is the same as that available in the OS/2-based CODE/370 product (except that CODE has a workstation component).

    Look for a dataset with the final node "SEQAMOD" to determine if you have the debugger installed. If so, compile your program with the "TEST" option, then execute with the "TEST" option, and you should see the debugger alive and well. You can also refer to the Debug Tool User's Guide and reference (SC09-1839) for more information.

    Question - Source truncation

    We're getting Syntax errors on C++ source and it looks like the compiler is truncating lines at 72 columns. Maybe it's a question of the data set profile, but is there a limit on the width of a line? Is there some compile option we can set to say look beyond 72 characters?

    Answer

    NOSEQuence, NOMARgins are the options you want. Uppercase portion is minimum
    abbreviation.

    Question - C/370 - Assembler

    I have C calling ASSEMBLER calling C working. However, I am going to have write a C 'stub' program for ASSEMBLER to call my C functions. The stub will use all capital/8-letter names for the functions called by ASSEMBLER. The stub will then call the actual C functions using mixed case/long names.

    I assume I will need to turn on the LONGNAME option to compile the stub. This in turn means I will need to use the pre-linker. As you know, I was having trouble with LONGNAME and pre-linker issues earlier on. Can you tell me the steps I need to take to successfully compile the stub and then link it with both C and ASSEMBLER object modules?

    Answer

    I'm not sure why you would want to add the extra layer of C stubs.

             #pragma map(YourVeryLongName,"REG8CHNM")
    
    will change the external reference from your C code ("YourVeryLongName") to "REG8CHNM" for assembler to call. Any internal calls in your program will continue to work.

    Question - C/370 and IMS/ESA*

    We are currently supporting a C/370 system that needs to be enhanced. This C/370 system needs to be able to make multiple calls to an IMS DB and make updates when applicable.

    1. What type of interface could be used by the C/370 program to query an IMS DB?

    2. Is it possible to make direct calls from within the C/370 program to an IMS DB?

    3. Since multiple DB calls may need to be made, is it possible to establish a common feedback area, (ie: Get Main) between the C/370 and IMS system?

    Answer

    The ctdli() function is used to invoke IMS facilities.

    You need to refer to the ctdli() function in the Library Reference. And look at the IMS chapter in the Programming Guide for 'how to' info. This chapter also covers error handling and other considerations.

    Question - PDS input to C++

    Do you know of an example in the 'CBC.SCBC3SAM' library that shows input by multiple PDS members? We need to know how to set up JCL which will input PDS members and output PDS members to an OBJ, then do the Pre-Link etcetera on the members in the OBJ. All the examples I've seen so far input only one member at a time to the CBCC proc.

    Answer

    To compile a PDS, use the same proc, just leave off the member names for both the SYSIN and SYSLIN dd's. eg. If you have your source in XXX.SOURCE, and intend your object to go into XXX.OBJ, then INFILE=XXX.SOURCE, and OUTFILE=XXX.OBJ

    Remember, this will compile the ENTIRE PDS. Make sure that this PDS contains ONLY members that you want to (re-)compile.

    The linkage editor does not support the "link an entire PDS" philosophy, so separate links are required.

    Question - realloc function changed

    The realloc function doesn't work as it previously did. In fact, now, the realloc function seems to try to reallocate storage in another place than the current one. So depending on storage mapping the function could fail because of no more storage available? Comments?

    Answer

    This is correct as specified in SC09-1359-03 IBM SAA* AD/CYCLE* C/370 Release 2 Migration Guide P.71. When the realloc() function is used with LE/370 a new area is always obtained and the data is copied. This is different from IBM C/370 V1 and V2 where, if the new size was equal to or less than the original size, the same area was used.

    Question - hdrs: stropts and syslog

    We are currently running C/370 Compiler, V2R1M0. We are attempting to port code from a SUN workstation to the mainframe. The C/370 compile fails to resolve HDRS: STROPTS, SYSLOG. I could not find them in the SEDCHDRS library. Could you provide me with a list of product and release levels which we need to attain this support.

    Answer

    SYSLOG.H and STROPTS.H have been available for some time in the AIX world. For the MVS world, those two header files are included in C/C++ for MVS/ESA Version 3 Release 2 (5655-121).

    Question - U4000 abend in system() call

    When I try to rename an MVS dataset using the system() call, I get ABEND=S000 U4000 REASON=00000000. The program is compiled with C/370 V2R1, and it looks like this:

      #include <stdio.h>
      #include <stdefs.h>
    
      void  main (int argc, char **argv)
      {
        char cmd[256];
        int  rc = 0;
    
        if (argc != 3) {
          printf("Usage: RENAME name1 name2\n");
          rc = 4;
        } else {
          sprintf(cmd, "RENAME %s %s", argv[1], argv[2]);
          printf("%s\n", cmd);
          rc = system(cmd);
          printf("\nRc=%d\n", rc);
        }
    
        exit(rc);
      }  /* main */
    
    And this is the JCL:
      //RUNTEST   EXEC PGM=TEST,REGION=1024K,TIME=1440,
      //          PARM='''I044D.CHALOG'' ''I044D.CHALOG.NEW'''
      //STEPLIB   DD   DISP=SHR,DSN=I044D.LOAD
      //SYSPRINT  DD   SYSOUT=*,DCB=(RECFM=F,LRECL=133)
    
    The program correctly prints this line to sysprint, and then abends: RENAME 'I044D.CHALOG' 'I044D.CHALOG.NEW' Any ideas as to what is wrong?

    Answer

    The RENAME command you specified requires a TSO environment, which does not exist in this case. Either CALL from IKJEFT01 (ie. TSO) or use the ANSI C rename function:

       rename(const char* oldname, const char* newname);
    

    Question - fread() Access Method

    I am reading a variable-blocked data set having opened the file using fopen( ddstring, "rb, type=record" ). I assumed that subsequent fread( area, 1, MAXLRECL, fileptr ); instructions would use QSAM to deliver a single variable-length record to my "area". Instead, the fread() delivers the entire block, including BDW, using BSAM. Is there support in the compiler for deblocking RECFM=VB data sets using QSAM, or do you have to do your own deblocking using BDWs and RDWs?

    Answer

    By any chance do you specify RECFM=U somewhere on your allocation ? (dynalloc or JCL DD ). The library will read the whole block including RDW into your buffer if it thinks that you are opening a recfm = U file. Use fldata to check. If it thinks you are opening a VB file it will copy your record (minus RDW_ into your buffer).

    The access method will still be B*AM.

    If you want to use QSAM (and have LE 1.3 or above) and PROMISE not to reposition other than rewind() you can open

              "rb,type=record,noseek"
    

    Question - C/C++ String Handling

    Does anyone have any experience with developing MVS batch applications in C or C++ that do a lot of text manipulation? Have you developed or purchased any string classes or string handling functions that greatly improve on the C library "strxxx" functions performance and/or provide some type of string range checking to ensure strings are not exceeding their bounds?

    We would like to develop such applications but are greatly concerned with the performance of the C "strxxx" functions and its lack of bounds checking. The C++ string class would seem to take care of the bounds checking problem but I'm under the impression that the C++ library string class uses the C "strxxx" functions which would result in poor performance. Is there a better string class out there?

    Answer

    If I can assume you're using IBM C/C++ on S/370* ...

    1. If you want to use the standard "C"-type strxxx, consider using the built-in version. That is, include the 'string.h' header, and the compiler will generate instream code, rather than a function call. (available for strcat/chr/cmp/cpy/len/rchr and memchr/cmp/cpy/set) This will help with your performance, but not the bounds checking (keep in mind that bounds checking results in higher overhead, and consequently lower performance).

    2. C++ provides the Application Support class library, which includes a number of text manipulation classes. This will get you around the bounds checking, but not the performance.

    3. You may want to consider the use of both of the above together. You could also look into the INLINE option and/or pragma inline(xxxx) to control inlining of code. This may impact the code generated for the class libraries as well (depending on the INLINE limits you set).

    Question - "termio.h" header file

    We installed OpenEdition*/MVS and C/C++ for MVS/ESA and want to move programs running on another vender's O/S to OpenEdition. But some programs couldn't be compiled because there wasn't a header file named "termio.h". From the manuals for C/C++ for MVS/ESA, I can find "termios.h" which is POSIX terminal I/O interface header file. Is there "termio.h" in C environment of MVS/ESA? Or can I use other header files included in C/C++ for MVS/ESA as "termio.h"?

    Answer

    There is no termio.h provided by OpenEdition MVS. We provide termios.h as required by the POSIX.1 and XPG4 standards. The book "Porting UNIX Software" by Greg Lehey (O'Reilly & Associates, ISBN 1-56592-126-7) discusses the differences and has a whole chapter devoted to terminal I/O. I believe it is possible to change code to use the termios interfaces but it's difficult to know how much work this would be considering we don't know how heavily you rely on the termio interfaces.

    Question - C/370's Environment

    Is there any IBM Documentation on C/370's environment and the general expectations of a routine coded in C/370.

    Answer

    C/370's environment is documented along with the other LE/370-based languages, in the LE/370 documentation, including an entire book (250 pages) dedicated to writing ILC's titled "Writing Interlanguage Communications Applications", SC26-8351.

    Question - c date function

    Is there anyone with a C370 function that will give the system date in many formats including this format 00/00/00. I would appreciate a copy of it.

    Answer

    You can use the strftime() library function to get each piece of the tm structure or you can start with:

     #include <string.h>
    
     char * datefmt(char *);
     main()
     {
      char xxx[30]; /*needn't be 30-leaving extra room*/
      printf(">%s<\n",datefmt(xxx));
     }
    
     char * datefmt(char *res)
     {
      time_t ltime;
      struct tm *gmt;
    
      time(<ime);
      gmt=gmtime(<ime);
    
      if (gmt->tm_year > 99)
         gmt->tm_year -= 100;
    
      sprintf(res,"%2.2d/%2.2d/%2.2d",gmt->tm_mon+1,gmt->tm_mday,
              gmt->tm_year);
    
      return res;
     }
    

    If you want to generate the specified format only. Extend that to include some specification (via parm) to say which format you want.

    The only line that needs to be replicated for different formats is the sprintf call - it is "printing" some elements of the "tm" structure returned by gmtime(). The 2.2 specification will force the month, day and year values to be 2-digits (padded on left with zeroes as required). Please note that this is NOT a "year-2000" compliant format. It will continue to work into the next century, since year is adjusted for, but the 2-digit year should not be used in any calculation that may involve dates from the previous century. You may also want to have a look at the "tm" structure itself - documented in "time.h".

    Question - Linking problem

    I have a problem linking on MVS an C object module we compiled on VM. We have a message IEW2554E on MVS when we tried to link the object on MVS. That means that MVS linker doesn't recognize a "type identifier". This type seems to be "XSD".

    We also tried to compile a FORTRAN program on VM and link it on MVS. It work correctly. Is the C compiler the problem?

    Answer

    It sounds like you might have compiled using the LONGNAMES or RENT option (that is where the XSD cards are coming from). You can confirm this by the existence of a ESD card with @@DOPLNK on it in your text deck.

    You will need to prelink your code before putting it through the linker. You should be able to prelink on VM, but I would recommend both prelinking and linking on the MVS side.

    Question - Unbalanced Binary Tree

    I am building a binary tree which is 'unbalanced/lopsided' because of the data. Is there a C utility which will reorganize my data into a more balanced tree and this reduce search times.

    Answer

    Sorry but there is nothing supplied by C/370 that will do this. There is, however, a C++ class library that supports trees as a type of collection. You might want to have a look at C++ for this.

    Question - Module IOSTREAM not found

    C/C++ modules are compiled at sister site and shipped to this site for run-time only usage. When this site attempts to run, program fails because module IOSTREAM not found.... MSGEZDC5205S DLL module not found.

    Answer

    You need to redistribute the IBM-supplied DLLs that come with C/C++ MVS with your application. You can do this with the DLL Rename Utility that comes with C/C++ MVS. This is documented in Chapter 10. DLL Rename Utility in the C++/MVS User's Guide (SC09-1993). Depending on how they do this, prelinking and/or linking may be require the second site.

    Question - Compile/Prelink/Link Basics for C/MVS

    As will be apparent from my questions, I'm a C novice. I'm trying to set up JCL in my environment to compile C programs, then prelink/link them together.

    In the EDCCPLG proc, what is the function of the SYSIN2 DD? Can this be used somehow to allow the specification of additional object mods to be prelinked? If so, how?

    I looked in the User's Guide, but could not find any info on this. If there is another reference, I'd be grateful for a pointer to it.

    Answer

    What you want to do is invoke EDCC multiple times (for every source file that you have), then invoke EDCCPL (prelink and link edit) all your C objects to create a load module.

    SYSIN2 DD is used to include additional control information to your prelink step. You have a number of options

    Question - Performance of the C "strxxx" functions

    The IBM C/370 Programming Guide (under Optimizing Code) states that the corresponding "memxxx" functions are more efficient than the "strxxx" functions and that a good way to improve performance is to use fixed buffer sizes, maintain the string lengths for these buffers, and use the "memxxx" functions instead.

    We have, however, done some rudimentary performance comparisions between PL/I character string assignments and C "memxxx" character string assignments. For moving characters (approximately 32kb) from one location to another, PL/I outperformed the C "memcpy" function. Not by a lot, but enough for us to want to do some additional testing before deciding which language to use for developing applications that do a lot of text manipulation.

    At the very least, we will probably use the "memxxx" functions instead of the "strxxx" functions and may decide to create our own string class using "memxxx" functions instead of the corresponding "strxxx" functions. Comments?

    Answer

    mem* is faster than str* because there is no need to search for the terminating null character. If you have the logical string assist (LSA ) feature on your MVS box, and HWOPTS(STRING) specified as a compile parm, the builtin str* functions will be *closer* to the builtin mem* functions in speed, because the null character search is done at the hardware level. If you know the length of the string, use the memcpy builtin. If you don't know the length, strcpy *might* be better . Hardcoded lengths will yield the fastest code. Putting that length into a variable may yield slower code, as there will be a need at runtime to determine the actual size (unless the optimizer can figure out what the length is at compile time). You can verify this for yourself by turning on the SOURCE and LIST options at compile time and look at the generated assembler code.

    Please ensure that you are using the builtin versions of the functions (ie. ensure that string.h is included, and that you haven't forced the library call with parenthesis around the function name).

    Compare "like" things: with and without optimization, with and without string.h, with and without hardcoded/initialized variables, etc. both in C and PL/I. If you have followed the above, and the PL/I routines are faster, and performance is critical, that may be the best way for you to go in this instance.

    Question - Pointers and pragmas

    Pointers and pragmas

    External to my C program is a pointer to an assembler routine. This routine requires an OS format plist. I have

      extern void (*asmrtn)(int);
      asmrtn(i);
    
    and the compiler is happy. When I add
      #pragma linkage(asmrtn,OS)
    
    the compiler complains that only functions or typedefs may appear on #pragma.

    I must be missing the obvious. Clues?

    Answer

    Try:

        typedef void MYASM(int);
        #pragma linkage(MYASM, OS)
        extern MYASM* asmrtn;
    

    Question - C/370-use of union

    This part of souce code can be compiled with any compiler I know (IBM C++;OS2, Borland**,DOS , C AIX and so on) but not with C/370. Can anyone tell me what's wrong ?

    struct PRTI_ST
       {
          PSZ    pszAmiProL;/*result string                     */
          ULONG  ulHelp;    /*help long                         */
          PMLIST pListAct;  /*holds actual print request printed*/
          PSZ    pszPos;    /*holds actual position inside the  */
                            /*list element data string          */
          BOOL   bWalkStop; /*indicates STOP after end of the   */
                            /*print request stream              */
       };
    
    typedef struct PRTI_ST PRTI;
    typedef struct PRTI_ST *PPRTI;
    
    struct PRTF_ST
       {
          PSZ    pszAmiProL;/*result string                    */
          ULONG  ulHelp;    /*help long                        */
          FILE   *fp;       /*filepointer of the stream to read*/
          PSZ    pszFileN;  /*filename                         */
       };
    
     typedef struct PRTF_ST PRTF;
     typedef struct PRTF_ST *PPRTF;
    
     struct AMI_PRINT_ST
            {
          PMLIST       pListPrt;   /* holds global Print lists*/
          FN_READ_LINE fnReadLine; /* either fgets or         */
                                   /* CAmiReadLine            */
           BOOL        bUnused;    /* not inside a open/close */
           union x
           {
              PRTF F;
              PRTI I;
           };
    A...........a................................................
    =====> a - EDC0069 A member declaration must specify a name.
           };
    
    typedef struct AMI_PRINT_ST AMI_PRINT;
    typedef struct AMI_PRINT_ST *PAMI_PRINT;
    

    Answer

    Your structure is illegal C because the union specifier is not followed by a declarator. In C++ you can declare nested structs, classes and unions. Were the other compilers compiling this as C++ code? Many other C compilers allow "unnamed unions" as shown in the example. This shortens the length of references to things inside the union. It is a departure from ANSI-C.

    Question - System functions question

    I read in the last C370 Newsletter that you didn't support an in-line assembler. We have just received the latest MVS C/C++ and I would like to convert some of our C programs written using another vendors compiler. The other vendor supports in-line assembler and allows you to issue things like SVCs and issue instructions that aren't directly supported by the compiler, e.g. SPKA. Is there anyway to do this other than write an assembler routine to do the required function? Are there any samples out there other than the dynamic WTO -- I need to do things like issue SMFWTM, issue an SPKA instruction, etc.

    Answer

    I'm afraid you're stuck with using assembler for this. The best way is to code using the C-supplied macros EDCPRLG and EDCEPIL, and declaring your assembler code to have "OS" linkage.

    I supplied the WTO example because it was slightly trickier than "standard" assembler calls. The SMFWTM macro is only doing a selection of type of call to the SVC. You can find out the address of the piece of storage you want to pass to the SVC, then call the assembler code, like I did with WTO, storing the parm in a piece of storage in the asm routine, and loading its address directly into R1. See a "Word from your editor" on the next page to find how you can get the January 96 issue and the WTO code.


    A word from your editor

    If you did not get a copy of our previous editions, just let us know and we will send them to you.

    You can download previous editions for online viewing from our anonymous ftp server (ftp.software.ibm.com). Directory /s390/c370 contains directories /news, /oldnews and /samples. /oldnews is broken down by year and /samples is broken into individual samples (eg. /codepage, /debuggable_malloc, /dynamic_WTO.).

    If you haven't already sent for your free subscription to this newsletter, now is the time to mail it in. If you prefer, mail or fax your business card to the address/phone number on the Reader's Comment Form. You can also send a message containing your name, full mailing address and phone number, in case we have to talk to you to inetc370@vnet.ibm.com or IBMMAIL(CAIBMRXZ).

    Please keep those cards, letters, faxes and messages coming in to us so that we can continue to provide this service. We really thank the many of you who have already sent your comments and subscriptions in. It's a great help if you include your phone number with any correspondence. Thanks!!


    Coming Soon

    In future issues, we'll have some performance tips and answer some more of your questions. Thanks for reading; please let us know what you think of this newsletter.

    This newsletter was produced by the IBM Software Solutions Toronto Laboratory. For further information on any of the products mentioned, please contact your local IBM office, or an authorized IBM Business Partner.

    Numerous product references in this publication are registered trademarks or trademarks of International Business Machines Corporation , unless otherwise indicated. IBM Canada Ltd., a related company is a licensee.

    This newsletter was created and marked for processing using IBM BookMaster * (Program Number 5688-015) and IBM Document Composition Facility (DCF)(Program Number 5748-XX9). The final copy was printed on an IBM 3825 Page Printer, an Advanced Function Printer.

    This newsletter is © Copyright IBM Corporation 1996. In Canada - © Copyright IBM Canada Ltd. 1996.


    C/370 Compiler News Volume 4 Number 2 April/96 Issue

    Reader's Comment Form

    1. Did you find this newsletter useful?

    2. Is there any way you think we could improve this newsletter?

    3. Is there any C/370 compiler-related subject you would like to see addressed in this newsletter?

    ____________________________________________________________________

    ____________________________________________________________________

    ____________________________________________________________________

    ____________________________________________________________________

    ____________________________________________________________________

    ____________________________________________________________________

    ____________________________________________________________________

    ____________________________________________________________________

    ____________________________________________________________________

    ____________________________________________________________________

    Please note:

    Thank you for your cooperation and help. You can either mail this form to us, hand it into an IBM office for forwarding or send a message containing your full mailing address and phone number to IBMMAIL(CAIBMRXZ) or inetc370 @ vnet.ibm.com.

    You can also fax the form to us. Our fax number is 416-448-6057. Please mark your fax for the attention of Gord Sinclair. Thanks.

    C/370 Compiler News Volume 4 Number 2 April/96 Issue

    Reader's Comment Form

    Fold here and tape.........fold here and tape.........fold here


        Gordon Sinclair
        Software Solutions Toronto Laboratory
        IBM Canada Ltd
        23/604/844/TOR
        844 Don Mills Road
        North York
        Ontario, Canada
        M3C 1V7
    


    Fold here and tape.........fold here and tape.........fold here
    Footnotes:

    ( ) Products marked * are trademarks or registered trademarks of International Business Machines Corporation, unless otherwise indicated. Company, product or service names marked ** may be trademarks or service marks of others. IBM's Visual Age products and services are not associated with or sponsored by Visual Edge Software, Ltd.