Welcome to the January, 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

January 1996 Volume 4 Number 1


Table of Contents

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

  • In This Issue
  • C/C++ for MVS/ESA Version 3 Release 2 is Here
    IBM(*) C for VM/ESA Version 3 Release 1
    C/370 Compiler News and Sample Code Available Online
    Using the Collection Class Libraries
    Come and talk to us at SHARE, the IBM Technical Interchange, and GUIDE
    You ask, we answer
    A word from your editor
    Coming Soon
  • Reader's Comment Form

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

    In This Issue

    ( )

    C/C++ for MVS/ESA Version 3 Release 2 is Here

    In Version 3 Release 2, C/C++ for MVS/ESA has been enhanced to become an integral part of the MVS/ESA OpenEdition(*)'s conformance to X/Open's(**) XPG4 (**) Base Profile Branding requirements. You can now port a wide variety of applications from UNIX(**) to take advantage of the robustness, reliability and scalability of the MVS system.

    In addition, C/C++ for MVS/ESA Version 3 Release 2:


    IBM(*) C for VM/ESA Version 3 Release 1

    IBM C for VM/ESA Version 3 Release 1 features a standards-conforming, language-centered development environment supporting POSIX(**) standards in the VM/ESA environment. IBM C for VM/ESA Version 3 Release 1 significantly enhances portability and interoperability of applications among different platforms by supporting OpenEdition for VM/ESA. OpenEdition for VM/ESA is the implementation for VM/ESA of the following IEEE POSIX standards:

    VM/ESA implements the spawn() function instead of the fork() function. The spawn() function provides a fast, low-overhead mechanism for creating a new POSIX process to run a new program.

    In addition, IEEE POSIX 1003.2 Shell and Utilities support is provided by the OpenEdition Shell and Utilities Feature for VM/ESA, a separate priced feature under VM/ESA Version 2 (5654-030). The OpenEdition Shell and Utilities Feature provides a UNIX(**)-like development environment for users of IBM C for VM/ESA Version 3 Release 1.

    IBM C for VM/ESA Version 3 Release 1 will continue to support, as a runtime environment, the Language Environment(*) for MVS & VM Release 5 program product. In addition, the C compiler will support the CEL (Common Execution Library) packaged with the VM/ESA Version 2 operating system.

    VM/ESA OpenEdition Services support in conjunction with IBM C for VM/ESA Version 3 Release 1:

    IBM C for VM/ESA Version 3 Release 1 supports creating and running applications in the OpenEdition VM environment, which offers the following advantages:

    C/370 Compiler News and Sample Code Available Online

    We now have an anonymous ftp server (ftp.software.ibm.com) setup so that you can download this newsletter (and backlevels) for online viewing, as well as download sample source code. All previous newsletters have been converted to HTML format.

    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). Other samples will be added over time, and a reference to the appropriate directory will be included in this newsletter.

    All files should be downloaded as binary. The newsletters don't need to be converted since they began in ASCII. The samples SHOULD NOT be converted since they may contain characters that do not map well between ASCII and EBCDIC - they are stored on this ASCII server already in their EBCDIC form.


    Using the Collection Class Libraries

    In the previous issue of this newsletter (October, 1995) we started to develop 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.

    main.C

     #include <iostream.h>
     #include <stdlib.h> // for use of exit() function
     void addItem(), listItems(), showStock(), removeItem();
    
     void main() {
     enum Choices { Add, List, Stock, Remove, Exit };
     int menuChoice=0;
     char* menu[5] = {"Add an item",
                                "List items",
                                "Show stock information",
                                "Remove an item",
                                "Exit" };
      while (menuChoice!=5) {
        cout << "\n\n\nSimple Stock Management System\n\n";
        for (int i=0;i<5;i++)
          cout << i+1 << ". " << menu[i]        << '\n';
          cout << "\nEnter a selection (1-5): ";
          cin >> menuChoice;
          while (cin.fail()) {
             // get input again if nonnumeric was entered
             cin.clear();
             cin.ignore(1000,'\n');
             cerr << "Enter a selection between 1&5!\n";
             cin >> menuChoice;
           }
           switch (menuChoice) {
             case 1: addItem(); break;
             case 2: listItems(); break;
             case 3: showStock(); break;
             case 4: removeItem(); break;
             case 5: exit(0);
             default: cerr << "Enter a selection between 1&5!\n";
        }
      }
     }
    
    Step.C
     #include <iostream.h>
     #include <ibag.h>
     #include "bike.h"
    
     void addItem() {
        Bicycle tbike;
        cout << "Enter item: ";
        cin >> tbike;
        while (cin.fail()) {
           cin.clear();
           cin.ignore(1000,'\n');
           cerr << "Input error, please re-enter: ";
           cin >> tbike;
        }
        MyCollection.add(tbike);
     }
    
     IBoolean printItem (Bicycle const& bike, void*/*Not used*/) {
        cout << bike << endl;
        return True;
     }
    
     void listItems() {
        MyCollection.allElementsDo( printItem );
     }
    
     void removeItem() {
        Bicycle tbike;
        cout << "Enter item to remove: ";
        cin >> tbike;
        while (cin.fail()) {
           cin.clear();
           cin.ignore(1000,'\n');
           cerr << "Input error, please re-enter: ";
           cin >> tbike;
        }
        if (!MyCollection.remove(tbike))
           cerr << "Item not found!\n";
     }
    
     void showStock() {
        cerr << "Function not implemented yet!\n";
     }
    

    Step 3

    : Changing the Element Type

    Now that you have a working program that allows you to add, list, or remove elements from a collection, you are ready to change the element type to something more complex than an integer.

    Requirements

    The element type must be changed from the built-in integer type to a class type with the following data members:

    1. A string representing the manufacturer or make of the bicycle
    2. A string representing the model of the bicycle
    3. An integer representing the type of bicycle: racing, touring, or mountain bike
    4. An integer representing the price of the bicycle

    Setup

    Copy the files bike.h, step.C, and main.C from the Step2 dataset to the Step3 dataset, and then change your current dataset to the Step3 dataset. Create a new file bike.C which will contain function definitions for functions declared in bike.h.

    Implementation

    First move the typedef definition for the collection and the #include statement for ibag.h from bike.h to step.C, where they are actually made use of.

    You can use the IString class of the Application Support Class Library to handle the strings for make and model. This class includes operators for element equality, ordering relation, and addition (concatenation), all of which will be used in this or later steps.

    Defining the Element Type

    In keeping with good object-oriented programming practice, you should separate the member function definitions from the class definition, by placing the class definition in bike.h and the definitions of member functions in bike.C. You should compile each .C file separately, and link them together.

    Class Data Members

    The following code defines the data members of Bicycle. You should replace the typedef for the element with the declaration for class Bicycle. Two header files are also included because they are required by members of the class. Place the following code in 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;
             // ... Member functions to be declared later and defined in bike.C
             };
    

    The following code defines an enumerator (used to determine the type of bicycle) and an array of IString objects (used to display the types of bicycle). Place it in bike.C:

             enum bikeTypes { Racing, Touring, MountainBike };
             IString btype[3] ={ "Racing", "Touring", "Mountain Bike"};
    

    Selecting What Element Type Functions to Implement

    When you implement the element type as a user-defined type (a class), you must define certain element functions, and in some cases key-type functions, for that element. These functions are used by Collection Class functions to locate, add, copy, remove, sort, or order elements within their collection, and to determine whether two elements of a collection are equal. For example, you may need to define element equality through an operator==, so that Collection Class functions can determine whether an element you try to add to the collection is identical to an element already present in the collection. Provided you use the correct return type and calling arguments, there is no right or wrong way to code many of these functions. An equality function for elements consisting of two int data members, for example, could return True (meaning that two elements are equal) if the difference between the two data members is the same for both elements. In this case, the objects (3,8) and (4,9) would be equal.

    To determine what element and key-type functions you need to implement for a given collection, you should consult the appropriate collection's chapter in the Class Library Reference. For this step, the collection is a bag. When you are first developing a program, you should use the default implementation of the collection, which is always the first implementation variant listed under the chapter's "Template Arguments and Required Functions" section. For each implementation variant, a list of required functions is provided, and you must either implement these functions for your element class, or determine that they are automatically generated by the compiler. In the case of the default implementation of a Bag, the following required functions are shown, under the heading "Element Type":

    You also need to implement input and output operators and a default constructor (used by the input operator and other functions).

    Default Constructor

    The default constructor should initialize all data members to blank strings or zero integers:

             // in bike.h, within class declaration
             Bicycle() : Make(""), Model(""), Type(0), Price(0) {}
    

    Assignment Operator and Destructor

    There is no need to define these explicitly. The compiler generates a default assignment operator and destructor that are suitable for the program.

    Copy Constructor

    This function is used by the Collection Classes and by the input operator. Declare and define it as follows:

             // in bike.h:
             Bicycle(IString mk, IString md, int tp, int pr) :
                Make(mk), Model(md), Type(tp), Price(pr) {}
    

    Equality Test

    The equality test (operator==) should return True if two bicycles have the same make and model, and False if not:

             // in bike.h:
             IBoolean operator== (Bicycle const& b) const;
    
             // in bike.C:
             IBoolean Bicycle::operator== (Bicycle const& b) const;
                  { return ((Model==b.Mode) && (Make==b.Make)); }
    
    

    Ordering Relation

    The ordering relation (operator<) should indicate whether the first bicycle would appear before or after the second bicycle in an alphabetically sorted list:

             // in bike.h:
             IBoolean operator< (Bicycle const& b) const;
    
             // in bike.C:
             IBoolean Bicycle::operator< (Bicycle const& b) const;
                 { return ((Make<b.Make) || (Make==b.Make && Model<b.Model)); }
    

    You can use the < and == operators for IString objects because they are defined for the IString class to indicate alphanumeric sorting order.

    Input Operator

    This operator is required by the addItem() and removeItem() functions defined previously. Both this and the output operator are declared outside the class definition, at the bottom of bike.h, and they are defined in bike.C. The input operator stores the alphanumeric data members of Bicycle in char arrays to avoid the overhead of constructing temporary IString objects.

             // in bike.h:
             istream& operator>> (istream& is, Bicycle& bike);
    
             // in bike.C:
             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): ";
                   while (cin.fail()) {
                      cin.clear();
                      cin.ignore(1000,'\n');
                      cerr << "Racing, Touring, or Mountain Bike (R/T/M): ";
                      cin >> typeChoice;
                   }
                   switch (typeChoice) {
                      case 'r':
                      case 'R': { type=Racing; break; }
                      case 't':
                      case 'T': { type=Touring; break; }
                      case 'm':
                      case 'M': { type=MountainBike; break; }
                      default:  { cerr << "Incorrect type, please re-enter\n"; }
                   }
                }
                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;
             }
    

    Output Operator

    The output operator is required by the listItems() function, and may later be required by other functions. It should display the make, model, type, and price of a bicycle:

             // in bike.h:
             ostream& operator<< (ostream& os, Bicycle bike);
    
             // in bike.C:
             ostream& operator<< (ostream& os, Bicycle bike) {
                return os << bike.Make
                          << "\t" << bike.Model
                          << "\t" << btype[bike.Type]
                          << "\t" << float(bike.Price)/100;
             }
    

    The program should now be placed in the following files. Some function bodies have been replaced with ellipses for brevity. main.C remains unchanged and is not shown.

    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() { /* ... */ }
    
    
     #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;
     }
    

    Running the Program

    Compile and link bike.C, main.C and step.C, and then run the program. If you add two bicycles with the same make and model, but different types or prices, the second bicycle's entry will be identical to the first when the bicycles are listed. The reason is that element equality is defined only in terms of the make and model. When you add what the collection considers to be an equal element, the existing element is duplicated by the add() function. When you remove an item, the input operator asks you to enter all fields for the item to remove. Again, because element equality is defined only for the make and model fields, the information you provide for bicycle type and price is not used in determining which element to remove. If you define a bicycle:

             Smithson    37Q    Racing   $270.00
    

    You can remove that bicycle's entry by removing:

             Smithson    37Q    Mountain Bike   $399.99
    

    These limitations will be corrected in the next newsletter.

    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).


    Come and talk to us at SHARE, the IBM Technical Interchange, and GUIDE

    We will be presenting at the SHARE user conference in Anaheim, California, March 3-8, the IBM Technical Interchange in Opryland USA, Nashville, Tennessee, April 22-26, 1996 and GUIDE in Washington DC, June 9-13. If you are going to these conferences, please drop in and see us. If you weren't sure about attending, consider it - there is a lot of useful information available, all at a very reasonable cost.


    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- Prelinkage Utility RENAME control statement

    I'm trying to link together two object modules that have long names in them.

    One of these modules uses the name XXXXXXXXX at the call site, the other uses the name xxxxxxxxx at the label definition. I've tried each of the following:

     1) RENAME XXXXXXXXX xxxxxxxxx
    
     2) RENAME xxxxxxxxx XXXXXXXXX
    
     3) RENAME XXXXXXXXX X9
        RENAME xxxxxxxxx X9
    

    1) and 2) don't work because the target of the rename is longer than 8 characters. 3) doesn't work because I'm not allowed to rename two different names to the same target name.

    Changing the name in the source at either the call site or the entry point definition is not an option.

    Editing the text deck at the call site will work (I can even automate this in my build process) but is not my favourite way of solving this problem.

    Any suggestions?

    Answer

    You are right - the prelinker will not map 2 Lnames to the same Sname.

    #pragma map would be your best alternative - barring that, you might be able to rename one to a dummy name and create a stub routine that calls the real function.

      RENAME fn1 FN1 <--- the real entry point
      RENAME fn2 FN2
    
      <rc> FN2(<parms>) {
        return(FN1(<parms>))
      }
    
    

    Try it out - the overhead of the function should be ok. For the example:

      #pragma noinline(FN1)
      int FN1(int x, int y, int z) {
        return(x+y+z);
      }
      #pragma noinline(FN2)
      int FN2(int x, int y, int z) {
        return(FN2(x, y, z));
      }
    

    FN2() turns into 2 instructions of code (and no prologue or epilogue is generated):

    
        L   r15,=V(FN1)
        BR  r15
    

    You will need the latest (V3R1M0) C compiler to get this behaviour.

    Question- strlwr(), strupr()

    Can you explain to me why the functions strlwr - Convert string to lowercase strupr - Convert string to uppercase (knowing C on OS2) are not available on MVS. What is the alternative?

    Answer

    These are OS/2(*) extensions. You will need to code your own - luckily these routines are pretty straight forward. Use the toupper() and tolower() functions to implement, e.g.

        #include <ctype.h>
        char* strlwr(char* s) {
          int i, l;
          l = strlen(s);
          for (i=0; i<l; ++i) {
            s[i]= tolower(s[i]);
          }
          return(s);
        }
    

    Question- Boundary Alignment

    I need to get a 34 byte character variable aligned on a double word boundary.

    Is there a correct/simple way?

    Answer

    You can union it with a double, e.g.

      union AlignedUnion {
        double alignmentDummy;
        char info[34];
      } Aligned;
    
    then use some macro magic if you don't want to 'know' it is a union, e.g.
      #define Info (Aligned.info)
    
      /* then you can reference the field as follows: */
    
      Info[3] = 'a';
      char* x = &Info;
    

    Question- When is eof triggered

    I have a question about a problem I am having with a program I am writing.

    The section of code looks like:

    Readdsn()
    {     char buffer[rec_len];
          char *bufptr;
          bufptr = buffer;
          while (!feof(stream)) {
     *** PROBLEM ***
              fread(bufptr,rec_len,1,stream);
              if ((strchr(bufptr, '>')) !=NULL)
                  if (strstr(bufptr, date))
                      parselne(bufptr);
    }
    

    Basically it looks at a MVS sequential dataset defined by "stream" using an fopen() with type=record, while not eof, reads the stream into the buffer, searches for a '>' character and if it finds it, searches the buffer for a matching date and if it finds it, passes the buffer to the parselne() where the data is place in variables for reporting purposes.

    The problem lies in the fact that when the program is run, it reads the last line, i.e. record - twice. I understand the feof() does not return a zero until after the eof has been passed, but that doesn't explain why the while is executed again.

    I have tried using if statements to stop the call to the parselne() function before the redundant call.

    Answer

    The problem basically is that eof is not set for a file until you attempt to read past the end of it (ie. attempting to read the record AFTER the last record).

    Example: 2 records in file ...

    Loop entered, feof is not on (file hasn't been read yet at all). fread() happens, returning record 1. Record 1 is processed. Since feof(stream) is not set yet, re-enter loop. Record 2 is read, and processed. Again, feof is not set yet (since you managed to read a record successfully), so loop continues. fread() is hit for the third time ... this time, however, there are no more records to be processed. Since you have no conditional code following the fread(), the rest of the code thinks you just read a record, so processes it. Actually, it processes "buffer" via "bufptr", and since the failed fread() attempt doesn't alter "buffer", it appears that the same (ie. last) record was read twice.

    Standard practice is to "pre-read" the file before the loop is entered, then "read" the file at the end of the loop. This way, you will not only process as you would expect to in this case, but the code would also work in a "empty dataset" scenario. If the original code were used on an empty dataset, you would have processed one "record" ... whatever junk is in "buffer" ... which may just contain a ">", and possibly even something that might sort of look like a date.

    Net, add a call to fread(), exactly as you had it, but place it before the loop. Repeat the same fread() call at the end of the loop. You will have one read outside the loop, and one inside the loop.

    Question- Performance Data

    We would like better performance profile information than we are getting. One idea we had was to hook the entry and exit points of every function call. It seems that you already do something like this when you compile with the test options. Is there such a thing as a user definable entry exit function that we could invoke? We would use such a function to provide us function invocation counts and call traces (with parameters).

    Answer

    If you are running Debug Tool (available with C/C++ for MVS/ESA) you can. You simply set 'AT GLOBAL ENTRY' or 'AT GLOBAL EXIT' to perform the function(s) you want. This is not a good practice to run as a daily operation, but certainly good for tuning. Remember to remove the test hooks when you go to production (ie. recompile without 'test')

    Question- C Main calling C++ Functions

    I have a C main program calling a C++ function. I've compiled each, and used the prelinked with the C++ function, and linkedit the 2 together so that there are no unresolved references. But execution abends with an 0C6 system abend, the external error was a user abend 4039.

    Is there anything different I need to do to call c++ from c code? Either in source code, or linking?

    Answer

    Make sure you use extern "C" linkage on the C++ function you want to call from C, e.g.

    C++ File:
    
      extern "C" {
        int fred(int a, int b);
      }
    
      int fred(int a, int b) {
        return(a+b);
      }
    
    C File:
    
      int fred(int, int);
      main() {
        x = fred(1,2);
        return(x);
      }
    

    Also, make sure you compile your C source with LONGNAME and RENT. They aren't hard requirements, but if you don't, you have to be more careful about function calling and sharing global data.

    Question- IThread class for MVS?

    I have a piece of OS/2 code, that uses the IThread Class and its methods - now I have to port this code to MVS. I could not find there any IThread class or similar class. Is there any way to get this code running under MVS without too much source code changes?

    Answer

    In order to do this you have get our newest release of C/C++. C/C++ for MVS/ESA 3.2 is available and will fully support this under Open Edition.

    Question- name mangling

    How many significant positions does the C++ compiler/Prelinker support for mangled names ? We currently port OS/2 C++ code to MVS and some functions will result in mangled names > 256 characters.

    Answer

    We support 1024 characters in mangled names.

    Question- ftime function

    does anyone know whether 'ftime' exists for C++ in MVS? I'm trying to port some Borland code that uses this, which imbeds a system header file called "timeb.h".

    Answer

    You need to get the latest level of LE for this - you have LE 1.4, but you need LE 1.5.

    Question- Nesting

    I am trying to compile a C program but cannot get past the following error:

     SEVERE ERROR EDC0242 DD:USERLIB(CPROG).:1
     Nesting cannot exceed the maximum limit 16.
    

    I would like to know exactly what this error means. I have checked the number of libraries that are being allocated to USERLIB and it is less than 16.

    What exactly does 'nesting' mean?

    Answer

    Nesting level is the depth of include files that are included from other include files. If you exceed the depth of include files you get the error message you saw.

    e.g. if your compile unit says #include "myfile.h" and myfile.h does not #include any other files then you have a nestinc level of 1.

    A nestinc level of 16 means:

     the file your are compiling #includes a file  (level 1)
                      which      #includes a file  (level 2)
                      which      #includes a file  (level 3)
                      which      #includes a file  (level 4)
                      which      #includes a file  (level 5)
                      which      #includes a file  (level 6)
                      which      #includes a file  (level 7)
                      which      #includes a file  (level 8)
                      which      #includes a file  (level 9)
                      which      #includes a file  (level 10)
                      which      #includes a file  (level 11)
                      which      #includes a file  (level 12)
                      which      #includes a file  (level 13)
                      which      #includes a file  (level 14)
                      which      #includes a file  (level 15)
                      which      #includes a file  (level 16)
                      which does not include a file.
    

    Some versions of the AD/Cycle(*) compiler have a NESTINC option that allows you change the maximum number of levels of nesting.

    Question- Templates

    Anybody know how to link code that contains templates? We have a program that uses IBMCLASS containers and are getting the .TEMPINC file generated by the compiler -- but can't find any documentation telling us how to get these generated template instantiations compiled & linked into our program.

    Answer

    There are two alternatives:

    1. Specify "NOTEMPINC" compiler option. This instantiates templates inline (does not put them in .TEMPINC PDS), so you do not have a .TEMPINC problem. The bad side of this approach is that your code gets bloated as you may have multiple template instantiations for the same class or function

    2. Do not specify "NOTEMPINC" compiler option. This instantiates templates in .TEMPINC PDS, so you need to compile them separately after all source has been compiled. You must use PDS compile option, as this is the only method that allows for recursive template instantiations. Once .TEMPINC has been compiled, you have to run C370LIB utility with "DIR" option against the object PDS generated during the compile of .TEMPINC. Concatenate this object PDS in the prelink step as part of PRELINK.SYSLIB DD, and linkedit the output of the prelink step.

    Question- Date difference checking

    If I have 2 sets of character variables containing data & time in the following format:

     Date - YYYYMMDD
     Time - HH:MM.:SS .
    are there C library functions that I can use to determine if one date is prior to the other ???

    Answer

    Here's a code snippet using LE/370 Services.

     #include <leawi.h>
     #include <stdio.h>
     #include <string.h>
    
     int main(void) {
    
     _VSTRING fmt;
     _VSTRING DateTim1, DateTim2;
     _FEEDBACK fc;
     _FLOAT8 sec1, sec2;
    
     strcpy(fmt.string,"YYYYMMDDHH:MI.:SS");
     fmt.length = strlen(fmt.string);
     strcpy(DateTim1.string,"1989031215.:23:14");
     DateTim1.length = strlen(DateTim1.string);
     strcpy(DateTim2.string,"1983021410:13:03");
     DateTim2.length = strlen(DateTim2.string);
    
      CEESECS(&DateTim1,&fmt,&sec1,&fc);
      CEESECS(&DateTim2,&fmt,&sec2,&fc);
    
      if (sec1 < sec2) printf("DateTim1 < DateTim2\n");
           else printf("DateTim1 >= DateTim2\n");
    
     }
    

    Question- Debugging trashed heap

    I'm having a big problem with one of my applications. The message is

      CEE0802C Heap storage control information was damaged.
    and the top part of the stack walkback is:
      CEEHDSP
      CEEHSGLT
      CEEV#FRS
      CEEVFRST
      free
      operator delete(void*)
      ostream::operator<<(double)
        operator<<(ostream&,const Money&)<--Money is 1 of my classes .

    I wrote the following function:

      void heapcheck( void )
      {
        int  * pint  = new int ;
        char * pc    = new char ;
        double * pd  = new double ;
    
        delete pint ;
        delete pc ;
        delete pd ;
      }
    

    and I called it at various places before the failing function, in hopes that I could detect the heap damage before my output function, but I couldn't get the error to move.

    I'm hoping that the heap damage can be detected somehow, because otherwise I don't know how I'm going to get to the erroneous line of code. Can anyone provide better heap checking code, or recommendations ?

    Answer

    This is a nasty one. What may work is the debuggable malloc that we provided in our June, 1994 newsletter.

    Here's a few other techniques that may help.

    Debuggable malloc puts eyecatchers before and after all storage allocated via malloc/calloc/realloc/free (and can be easily extended for C++ new/delete/new []/delete[] with operator overloading). When storage is freed, the eyecatcher is checked to see if it was overwritten - if it was, a diagnostic is issued.

    If you know what storage location is being tromped on and you have our debugger, you can use the debug command:

       AT CHANGE STORAGE(<address>, <bytes>)
    

    This command will tell the debugger to check this storage location. As soon as it changes (or soon after), the debugger will give you control.

    The STORAGE runtime option is really useful for catching wild pointers and such (which might be at the root of your problem). If you run your program with the runtime option STORAGE(FE,FE,FE) you might get closer to your problem and you will find that you get more reproducible abends (since far less storage has random values).

    Question- Inline Assembly Code

    We are currently a very heavy user of Assembly code. We have learned a considerable amount over the last month or two and now have a very nice mixed language functionality via the Persistent C Environment. However, I do have one question. Does C/370 support "Inline Assembly Code"? Because we are a high-volume processing shop, we sometimes need to tweak as much performance as possible out of an application. We don't want to incorporate much Assembler code, but there are times that we may need small, highly accessed sections of code in Assembler.

    Answer

    No support for inline assembly code, basically because of our optimization code. We can't really optimize if we have to preserve some assembler in the middle of the code. One of the many parts of optimization involves moving statements around; another involves removing dead code. In order to do either, we would have to interpret each assembler instruction to analyze the impact of the instruction being where it is, as well as the impact of moving other code around the assembler piece.

    For performance concerns, you may want to have a look at the HEAP and STACK allocations (see REPORT option if V2, otherwise see LE/370's RPTSTG option). Each GETMAIN we have to do will cost you time. You should also look into the INLINE option and associated pragma. It will cause functions to be included instream in your code, without the overhead of a function call. Cost - potential code bloat if function is called repeatedly from different points in program. Benefit - # executions of function times cost of STM/LM (plus other overhead) for each call to function; you determine which ones get inlined or let us default.

    Question- fwrite

    I am trying to get the following code to work.

     #define REP_LINE_LEN 133
     #define reportFile DD:REPORT
     char rep_buf[REP_LINE_LEN];
     FILE * file_ptr;
     /* open the report file */
     if ((file_ptr  = fopen(reportFile,
          "wb,type=record,recfm=fbsa,lrecl=133")) == NULL)
     {
         printf("Could not open the report file\n");
         exit(8);
     }
     memset(rep_buf, ' ',REP_LINE_LEN);
     sprintf(rep_buf,
     "Number of records processed.");
     fwrite(rep_buf,sizeof rep_buf,1,file_ptr); .

    When I check the file I am writing to the following line is printed:

     umber of records processed.
    

    I can't figure out why the first character of the sentence is missing.

    Answer

    The first character of an ASA file is supposed to be the ASA character. (' ', '+','-', '0' or '1') . FBSA is an ASA file. Since you have opened the file in binary (wb) the library allows you to insert your own ASA character.

    Question- VSE C/370 Compiler

    Does anyone know whether the VSE C/370 compiler can be used to generate an executable module to be run on an MVS system? Does the target system need the C run-time library and, if so, is it possible (and legal) to distribute the run-time library to the target system?

    Answer

    You cannot produce an executable C/370 module on VSE for execution on MVS. Although not officially supported on VSE, you may be able to produce an object module on VSE, ship it to MVS and relink there for execution. This is officially supported between VM & MVS.

    This would, of course, require the C run-time library. You would need to license the run time library to the target system. The C run-time is also packaged wit Language Environment for MVS&VM as well as MVS OpenEdition. If you have either of these, you may not have to worry about redistributing the C runtime library

    Question- Direct Access to BDAM Files From C/370

    I need to directly access very large BDAM files (up to 64 GB) in an MVS environment from a C/370 program. The fseek() function only allows me to seek to a byte address maximum of 2.147 GB at a time. Through repeated calls to the fseek() using SEEK_CUR I know can advance the file pointer beyond this 2.147 GB limit and read in the data at that position. My question is, is this the best way that this can be accomplished or is there some other alternatives?

    Answer

    I'm afraid there isn't any alternative. The TTRs we use for fgetpos()/fsetpos() are only supported for about 3 GBs of data at most. Larger files would make you resort to byte offsets (same overhead of using fseek()).

    Question- Include files search in MVS

    This question is related to the "INCLUDE" files search algorithm in the C/370 MVS compiler.

    The program has the following header files :

        #include "foo.h"
        #include "foo.hdr"
    

    The two header files "foo.h" and "foo.hdr" should be included during the compilation. To do this I have added the dataset to the "USRLIB" in the JCL as shown below :

         //USRLIB    DD  DSN=USERFIX.LEVEL1.H,DISP=SHR
                     DD  DSN=USERFIX.LEVEL1.HDR,DISP=SHR
    

    But during the compilation the include "foo.hdr" is taken from the dataset "USERFIX.LEVEL1.H" instead of the dataset "USERFIX.LEVEL1.HDR". I could not use the LSEARCH option, because of the parameter length limitation.

    Is there anyway to include the correct header files without changing the application code ? I couldn't effort to do the changes in the code because of large number source files.

    Answer

    In the C/C++ for MVS Version 3 Release 2, we did a lot of work to enhance LSEARCH. Now you can say LSEARCH('userfix.level1.+'). The '.+' instructs the compiler to use the extension in building the PDS name, so

      #include "foo.h"
    

    will look in USERFIX.LEVEL1.H(FOO) and

      #include "foo.hdr"
    
    will look in USERFIX.LEVEL1.HDR(FOO). Please refer to the new User's Guide for more information.

    In previous releases, the following will direct the compiler to look at DD 'other' for all ".hdr" include files, before looking in DD userlib.

      #LSEARCH((*.hdr)=(LIB(DD:OTHER))) .

    We also provided a new option, OPTFILE, in C/C++ for MVS/ESA product, which allows you to specify a dataset or ddname that contains additional compile parms. Additionally, we added support for the different suffixes (.h, .c, .whatever) through our SYSPATH and USERPATH parms, as well as '/' support for inclusion (eg. #include <types/sys.h>).

    Question- Using CBC* or EDC* Cataloged Procedures for C Programs

    Are there any performance or efficiency considerations we should know about when compiling C programs? Is it better to use the EDC* procs for C, or can we use the CBC* procs. What is recommended?

    Answer

    Use the C procs (EDC*) for C and the C++ procs (CBC*) for C++.

    Make sure your compiler module is blocked optimally for your DASD. I have seen a 50% compile time difference between U-6144 and U-32676. (32676 was faster).

    For the upcoming 3.2 compiler, VB datasets seem faster than FB datasets (try it with your own code before you decide). When testing for compile time advantages from system configuration using a test case larger than "hello world" is usually advantageous.

    And you should "protect" your own headerfiles against multiple inclusions by surrounding your header file with:

    #ifndef MYNAME
           < put filetag here if using one >
      #define MYNAME
    
      <normal header file contents >
    #endif
    
    If you are not in the traditional codepage (IBM-1047) you should use a trigraph for the '#'s.

    Question- Using DLLRNAME

    I'm trying to get a report of the DLLs imported by my application. I'm getting "EDC6200E An invalid argument list was specified." from both batch and from TSO when calling the DLLRNAME utility. Does anyone have an example of some working JCL?

    Answer

    All you have to do is supply load module member name on sysin input card. Steplib is the run time library in the example below.

    //STEP1  EXEC PGM=EDCDLLRN
    //STEPLIB DD DSN=CEE.V1R4M0.SCEERUN,DISP=SHR
    //SYSIN DD *
      MY.LOAD(APPL1)
      MY.LOAD(APPL1DLL)
     'USERID.TEMP.LOAD(MEMBER1)'
     'USERID.TEMP.LOAD(DLL1)'
    /*
    //SYSPRINT DD SYSOUT=*
    

    Question- Compile Listings

    On our compile listings (in the assembler listing section) we see a spill size and a dynamic memory allocated number. The dynamic memory number seems to be at least 128. What is this number? What kind of allocation is taking place?

    Answer

    The "dynamic storage" number is the amount of dynamic storage that you have in your program. Vary your program requirements, and that number will change too, often by an amount larger than the change in the storage, due to overhead of storage management.

    Question- Use of EXECOPTS

    Recently I was working on a mainframe C debug problem and the dump, showing a 4032 abend, wasn't real helpful. I wanted to have the program rerun generating an 0C# type abend (and the dump it produces) which I often do when trying to debug PL/1. It usually gives a clearer idea of the problem at the time it happens rather than letting C or PL/1 handle it first.

    With PL/1 I can (if not IMS(*) or CICS(*) simply add a runtime parm=N OSPIE, NOSTAE and it gives me a nice 0C# abend and dump. Unfortunately, C needs to have the pragma EXECOPTS statement in the source for runtime parms to be accepted. That is not something most programmers do. Many times the C program is a bought package and we don't even have the source.I know I can always make EXECOPTS the default, but I'm unsure if there is a negative side to doing so. What effect would it have, if any, on applications bought and only run here, not compiled?

    If there is no harm in making it the default I will, or at least make a recommendation to use the pragma statement to allow runtime options.

    Answer

    Like PL/I, we were based on the common library, so STAE/NOSTAE and SPIE/NOSPIE were valid for us too. TRAP(OFF) is the way to get it under LE.

    Our supplied default is EXECOPS - you may have changed it when you installed the compiler. If someone supplied you with object decks, you can't change the setting that they provided, unless you can get them to recompile their code with the right pragma.

    The impact of execops is basically one of control. If you do not want end users to get control of execution-related options, this is the one you want to specify. This is particularly useful when you are writing code to send to customers, and don't want them to be able to get a full dump (and walk your executable themselves).

    Question- ctrace cdump

    Can you point me to some info on how to use CTRACE/CEEDUMP in C/370? Are these modules included with the compiler? We keep getting link errors indicating that the functions/modules are not found.

    Answer

    ctrace() is a function that is supported both by the C/370 RunTime Library and also under LE. There are examples detailing the difference in usage with the two libraries in the AD/Cycle C/370 Migration Guide SC09-1359-03. Under LE the ctrace() function invokes the LE callable service CEE3DMP with options TRACEBACK, NOFILE, NOBLOCK, NOVARIABLE, NOSTORAGE, STACKFRAME(ALL), NOCOND, NOENTRY. Output would go to the CEEDUMP DDNAME. There is also information showing that a program using a ctrace() call needs to be relinked to run under LE, including @@CTRACE from SCEELKED.

    There is also good information about ctrace() in SC09-1761 AD/Cycle C/370 Library Reference.

    Question- incorrect code generated for incrementation

    I have this code:

        *STR++ = TOUPPER(*STR)
    

    The code generated increments the pointer before storing the UPPERed character. I am porting an application from AIX(*) where it works as expected. I found a workaround that is separating the line:

        *STR++ = TOUPPER(*STR);
    
    into two lines, first assignment, then incrementing pointer.

    Answer

    What you are trying to do is undefined by ANSI(**). Please see sec. 3.3 of the ANSI manual. (line 9-10)

    Question- Calling MVS WTO routines from C/370

    Is there a way to call a function that will perform a Write To Operator message from a C/370 program in an MVS/ESA operating system environment ?

    Answer

    Below is some code I've used in the past. You can tailor it as you like. C code must ILC to an assembler routine to do the WTO. However, what I did was remove the macro-ness from the WTO call, and changed it to dynamic (WTO is a static-string routine, so the complete string must be known at assembly time.)

     DYNWTO.ASSEMBLE:
    
    --------------------------------------------------------
    DYNWTO   CSECT
    DYNWTO   AMODE 31
    DYNWTO   RMODE ANY
             PRINT GEN
             EDCPRLG                   ALWAYS INCLUDE C PROLOG
             L     6,=A(ACTMSG)        SET SVC35.ACTMSG TO DYN MSG
             LA    7,76                LEN(WTO MESSAGE)-SET MAX 76
             L     5,0(,1)             PARM1 IS LENGTH OF DYN MSG
             L     5,0(,5)
             O     5,=X'40000000'      1ST BYTE - PAD CHAR (' ')
             L     4,4(,1)             PARM2 IS DYN MSG ADDR
             MVCL  6,4                 COPY DYNMSG TO SVC35 STRUCT
             CNOP  0,4
             BAL   1,BARNDMSG          BRANCH AROUND SVC35 STRUCT
             DC    AL2(80)             TEXT LENGTH (76+4)
             DC    B'1000000000000000' MCSFLAGS
    ACTMSG   DC    CL76' '             ARBITRARY SIZE OF 76
             DC    B'0000000000000000' DESCRIPTOR CODES
             DC    B'0100000000000000' ROUTING CODES
    BARNDMSG DS    0H
             SVC   35                  ISSUE SVC 35
             EDCEPIL
             END
    --------------------------------------------------------
    
    CWTO.C:
    
    --------------------------------------------------------
     #pragma linkage(dynwto,OS)
     void dynwto(int, char *);
     main()
     {
      dynwto(9,"something");
     }
     --------------------------------------------------------
    

    Question- EDCHOTC vs CEEPIPI

    I have a C/370 main() program which calls VTAM which causes an exit routine written in assembler to be dispatched in SRB mode. The assembler routine calls a C function() which includes these pragma statements

      #pragma  options (NOSTART)
      #pragma  environment (SENDEXIT, nolib)
    #if defined(ADCYCLE_COMPILER)
      #pragma  runopts (STACK(8K,4K,ANYWHERE,FREE))
    #endif
    

    Since I call the C routine in SRB mode, I link it with an EDCXGET/EDCXFREE of my own which uses no SVCs. The program includes <spc.h> and is linked with CEE.V1R3M0.SCEESPC. It calls EDCXREGS to locate its input parameter list which the assembler passes to it.

    This works with C/370 with C/370 run time and AD/CYCLE C/370 with LE/370. I want to make it more efficient by using EDCHOTC or CEEPIPI to avoid EDCXGET/EDCXFREE every time I call the C subroutine. What should I use?

    Answer

    You will want EDCHOTC rather than CEEPIPI. EDCXREGS is only officially supported in an SPC application. The key to whether EDCHOTC is adequate for you is whether you can initialize beforehand and store the handle somewhere your code can access it (e.g. TCB FSA). This will improve your performance and allow you to override the get/free storage mechanisms as before.

    Question- Long/short mismatch warning

    We are wondering if the C/370 compiler's handling of short and long integers and its default for int is justified. For example, if you define a short integer with an initial value you do not get a warning message. Then you add 5 to it and get a warning about long/short mismatch. This is strange, as we are not explicitly asking for a (long) 5, but just any 5. Only by typecasting twice can we avoid this warning. Is it some standard or is the C/370 compiler too strict in this?

     33       | short int Test = 0;
     34     1 | Test += 5;
    
    gives
    *=WARNING=========> a-EDC0817 Type conversion may result in
                                  lost precision.
    *=INFORMATIONAL===> a-EDC0140 Operand has type signed integer.
    *=INFORMATIONAL===> b-EDC0140 Operand has type signed short
                                  integer.
    

    Yet, the following compiles clean.

     35     2 | Test = (short)((long) Test + 5);
    

    Typecasting the 5 in line 34 still gives the error. We did not find a way to use the += operand with short integers without getting this warning. The C/Set2 compiler does not give these warnings.

    Answer

    It looks like you have the "CHECKOUT" option turned on. It's purpose is to warn you about such things as this. The compiler has no way of knowing (other than checking ALL of your code) what the value of "Test" is when it encounters the "+=5" - it may indeed lead to an overflow situation. This sort of things happens a lot to inexperienced programmers (ie. adding mixed types together without realizing they have done so), leading to incorrect output at execution time. The CHECKOUT option is intended as a "one-shot" to help you ensure that you don't have the "common pitfalls" in your code.


    A word from your editor

    If you did not get a copy of any 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).

    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 1 January/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 1 January/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 a trademark or service mark of others.