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

November 1996 Volume 4 Number 4


Table of Contents

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

  • In This Issue
  • A new C Compiler for VSE/ESA
    What's New in OS/390 C/C++ Release 2.
    Date and Time Classes
    You ask, we answer
    A word from the editor
    Coming Soon
  • Reader's Comment Form

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

    In This Issue

    ( )

    A new C Compiler for VSE/ESA

    IBM C for VSE/ESA Version 1 (C for VSE) is the newest addition to the C/390 family of IBM compilers that now support the VSE/ESA, MVS/ESA(*), VM/ESA(*) and OS/390 mainframe platforms.

    C for VSE is an implementation of IBM AD/Cycle(*) C/370 for MVS and VM. C for VSE works in conjunction with Language Environment(*) for VSE/ESA Version 1 Release 4 (LE for VSE) or with the the new C Language Run-Time Support of VSE/ESA Version 2 Release 2. C for VSE supports the 31-bit virtual addressing of VSE/ESA and adds a number of new features and functions to those that exist in it's predecessor product, IBM C/370 V2R1:

    Support of Language Environment for VSE/ESA: Support of Language Environment for VSE/ESA allows for a common language environment that supports both compilation and execution of applications with run-time libraries serving multiple programming languages. Language Environment for VSE/ESA provides a common execution environment and library of run-time services that include callable services, common exception handling, library of intrinsic math routines, and storage management. As an option, C users may use the C Language Run-Time Support contained in VSE/ESA Version 2 Release 2.

    Interlanguage Communication: C for VSE, in conjunction with Language Environment for VSE/ESA, provides Interlanguage Communication (ILC) capability. Applications written in C for VSE can call or be called by other LE supported languages such as COBOL for VSE, PLI for VSE and also Assembler. This means that an application originally written in one language can be extended or enhanced in another. For example, C for VSE code can be used where you are most familiar with the language or where the unique features of the C language are required.

    Source Level Debug Tool for C for VSE/ESA: C for VSE/ESA offers the same powerful Source Level Debugger that is also offered with the COBOL for VSE and PLI for VSE compilers. The debugger is combined with the C for VSE compiler in a full function package. An optional alternate function package consists of the compiler without the debugger. For COBOL for VSE, PLI for VSE or C for VSE, only one debugger is required as the common debug facilities can be used by all three programming languages.

    The interactive source level debugger featured in C for VSE, allows you to debug your applications as they are executing in their native host environments, such as CICS(*). The debugger contains functions such as:

    An entire debug session may also be recorded in a log file. Using this log file, the complete history of the debug session can be subsequently reviewed and further analyzed. You can also replay the entire debug session from a script file. Used in this way, the debugger becomes a valuable regression testing tool when making future changes to the applications. You may find the log/replay function useful for regression testing of the application when making changes that may be necessary to support Year 2000.

    Year 2000 Ready The C for VSE compiler is Year 2000 ready. It can handle dates and date fields for the Year 2000 and beyond. In addition date fields in listings and reports are all Year 2000 conformant. Using C for VSE with Language Environment for VSE/ESA 1.4, you can write applications for the Year 2000 and beyond.

    Packed Decimal Data Type-Access to Enterprise Data: Packed decimal data is widely used in business applications and is supported in the COBOL, PL/I, and Assembler languages. C for VSE introduces the support of packed decimal (fixed-point decimal) data as a native C data type. This simplifies the manipulation of this data type for business-oriented applications, and the passing of this data type between applications written in C for VSE and other programming languages. Fixed-point decimal arithmetic operations can now be performed without translation of data into standard C data types. This data type allows for expressions with up to 31 digits of precision enabling full exploitation of the System/370 and System/390 native hardware support for decimal data calculations.

    Coded Character Set: With C for VSE, international coded character set (codepage) support is now included. The compiler can process C syntax characters, such as square brackets, in source files without the use of trigraphs or alternative characters.

    Softcopy Examples: Shipping of samples and examples from books in the product library is a new feature of C for VSE. You can now easily incorporate these samples in your programs.

    C Language Standard: C for VSE conforms to the ANSI/ISO 9899:1990 (1992) C language standard. Conformance to the ANSI/ISO standards gives the source code written for the C for VSE/ESA compiler the advantage of a high level of portability to other IBM C compilers.

    Popular Features and Functions: C for VSE is built on the strengths of the C/370 family of products and offers many features and functions that are in common with its predecessor, IBM C/370 Version 2 Release 1:

    Language Environment for VSE/ESA includes the DSECT utility which provides additional help to programmers when creating mixed C and Assembler programs. The DSECT utility converts descriptive data produced by IBM High Level Assembler, into C for VSE data structure declarations for the sharing of data for C for VSE programs that interface with assembler programs.

    IBM C for VSE/ESA V1 is a state-of-the-art language-centered environment that application developers can use to create, modify, test, and debug mission critical applications targeted to execute on the VSE/ESA platform. Source code written in C/370 Version 2 (VSE feature) is fully compatible with C for VSE/ESA Version 1. C for VSE/ESA Version 1 is planned to be available on December 13, 1996.


    What's New in OS/390 C/C++ Release 2.

    OS/390 C/C++ Release 2 enhances compile-time and execution-time performance of the compiler and usability relative to OS/390 C/C++ Release 1, through the following:

    For more information on the OS/390 C/C++ Release 2 you can go to http://www.software.ibm.com/ad/c370/. For a whole new section on debugging C/C++ on MVS you can go to http://www.software.ibm.com/ad/c370/debugger/.


    Date and Time Classes

    The IDate and ITime classes provide you with data types to store and manipulate date and time information. With these classes, you can create date and time objects, and use member functions to do the following:

    Note: The IDate and ITime classes are independent. When an ITime object's time passes 23:59:59 (24-hour format) or 11:59:59 p.m. (12-hour format), it has no effect on the value of any IDate object. If you want to have interdependent date and time objects (where wrapping past midnight on the time object causes the date object to be incremented), you must create your own class, containing IDate and ITime data members, and define constructors, operators, and member functions that take into account the dependency of the IDate and ITime data members. See "Simple Combined Date and Time Example" later for an example of how to do this.

    IDate Class

    The IDate class uses Gregorian calendar dates. The Gregorian calendar consists of the 12 months, January to December.

    IDate also supports the Julian date format, which contains the year in positions 1 and 2, and the day of the year in positions 3 through 5. If the day of the year is less than three digits, zeros are added on the left to increase the size to three digits. For example, February 14, 1965 is 65045 as a Julian date as it is the 45th day of the year.

    The IDate class returns the names of the days and months in the language defined by the current locale. For information on defining the locale, see the standard C library function setlocale().

    Creating an IDate Object

    You can create an IDate object using different IDate constructors. For example:

      IDate OneDay(IDate::June,30,1994);      // Month, day, year
      IDate AnotherDay(23,IDate.::April,1961);// Day, month, year
      IDate SomeDay(940616);                  // Julian date format
      Date Yesterday(1994,177);               // Year, day of year .
    

    The constructors accepting a month use the IDate enumeration Month, whose members are named January through December (the months of the year in English).

    Changing an IDate Object

    You can add days to, or subtract days from, an IDate object. You can also subtract one date from another, in which case the result is the number of days between the two dates. For example:

      IDate Day1, Day2;
      int NumDays;
      Day1=IDate:today();
      Day2=Day1+1;         // Day2 is one day after Day1
      Day2+=2;             //  Day2 is now three days after Day1
      NumDays=Day2-Day1;   // NumDays=3
    

    Note that you cannot add two IDate objects together, because such an addition does not make sense. However, you can add two ITime objects together.

    Information Functions for IDate Objects

    The IDate class defines information functions that you can use to obtain specifics about an IDate object. For example, you can find out what day of the week, month, or year an IDate object's date falls on, or what the name of the day or month is for the current locale. You can also find out what today's date is. The following example shows some of the IDate information functions:

      //  Information functions for IDate class
    
      #include <iostream.h>
      #include <istring.hpp>
      #include <idate.hpp>
    
      void main () {
       IDate Day1(27,IDate::May,1964);
         cout << Day1.dayName() << " "
            << Day1.monthName() << " "
            << Day1.dayOfMonth() << " out of "
            << IDate:daysInMonth(Day1.monthOfYear(),Day1.year()) << "days in month"
            << IDate:daysInYear(Day1.year()) << " days in year "
            << Day1.year() <<'.' << endl;
             }
    

    This program produces the following output:

      Wednesday May 27 out of 31 days in month, 366 days in year 1964.
    

    Testing and Comparing IDate Objects

    You can compare two IDate objects to determine whether they are equal, or whether one is later than the other. The following operators are defined: ==, !=, <, <=, >, >=. For example, the expression if ((Day1>Day2) && (Day1!=Day3) evaluates to true if Day1 is January 1 1994, Day2 is June 3 1968, and Day3 is July 12 1941.

    You can also check whether a particular year is a leap year, or whether a particular combination of day, month, and year is valid. The isLeapYear() function returns true if its integer argument is a leap year. The isValid() function accepts combinations of day, month, and year (or day of year and year), and returns true if the provided date is valid. For example, it returns true for the first date below, and false for the second date:

      if (IDate:isValid(IDate.::June, 30, 1990)) // ...
      if (IDate:isValid(1965,366)               // ... False (No day number 366 in 1
    

    ITime Class

    The ITime class refers to time in the 24-hour format by specifying time units (hours, minutes, seconds) past midnight. If you want to display ITime objects in the 12-hour format, you must convert them to IStrings using the asString function with a char* argument of "%r". (This argument is a format string. All format specifiers of the strftime() function of the standard C library are supported by the IString conversion function).

    Note: Objects of the ITime class are precise only up to the nearest second, and cannot be used for more precise timings.

    Creating an ITime Object

    You can create an ITime object and initialize it to a number of seconds past or before midnight, or to a number of hours, minutes, and optionally seconds past midnight:

      ITime Time1(33556),     // 09:19:16
            // 33556 = 9 hours   (32400 seconds), 19 minutes (1140 seconds),
            // 16 seconds (adds up to 33556)
            Time2(-33556),    // 14:40:44
            // (9 hours, 19 minutes and 16 seconds BEFORE midnight)
            Time3(12,00),     // 12:00:00 (noon)
            Time4(3,3,3);     // 03:03:03
    

    The constructors translate incorrect times into valid ITime objects using modulo arithmetic. For the seconds past midnight format, any number whose absolute value is greater than or equal to 86400 is divided by 86400, and the remainder is used to calculate the time. For the hours, minutes, and optional seconds format, excess minutes and seconds are added to the hours and minutes values, respectively, and if the hour exceeds 23 it is divided by 24 and the remainder is taken. For example:

      ITime Time1(133556),    // 13:05:56(13356-86400=47156 seconds after midnight)
            Time2(-133556),   // 10:54:04(13356-86400=47156 seconds before midnight)
            Time3(10,119,60), // 12:00:00(noon) (10 hours plus 119 minutes plus 60)
            Time4(33,33);     // 09:33:00 (33 hours - 24 hours = 9 hours)
    

    Changing an ITime Object

    You can add or subtract two times. Four operators are provided: +, + =, -, and -=. The following example shows the use of these operators:

      ITime Start(12:00), Duration(2:00),
            Done=Start+Duration;  // Done=14:00
      Start=Done-Duration;        // Start=12:00 still
      Start+=Duration;            // Start=14:00
      Start-=Duration;            // Start=12:00 again
    

    Information Functions for ITime Objects

    Three of the information functions return an ITime's hour, minute, or second settings; the other information function returns the current time as determined by the system clock. For example:

      ITime Time1(ITime:now());
        cout << Time1.hours() << " o'clock occurred "
           << Time1.minutes() << " minutes and "
           << Time1.seconds() << " seconds ago." << endl;
    

    This displays a result such as the following:

             12 o'clock occurred 16 minutes and 23 seconds ago.
    

    Comparing ITime Objects

    Functions are defined to let you compare ITime objects for equality, inequality, or relative position in time. The following operators are defined: ==, !=, <, <=, >, >=. In the following example, a message is displayed if enough time elapses between the first and second calls to the now() member function:

      #include <itime.hpp>
      #include <iostream.h>
      ITime First(ITime:now());
       void main() {
            ITime Second=ITime.:now();
            if (First<Second) // Some time has passed
                cout << "You must be debugging me!" << endl;
            }
    

    This message usually does not print when the program is run outside of a debugging session. However, if you debug the program and step through each line slowly, the message may be displayed, because the first ITime object is initialized during program initialization (before main is called) while the second ITime object is initialized within main.

    Writing an ITime Object to an Output Stream

    ITime defines an output operator that writes an ITime object to an output stream in the format hh:mm.:ss. If you want to write the object out in a different format, you should convert the object to an IString using the asString member function. This member function accepts a char* argument containing a format specifier. The format specifier is the same one as used by the C library function strftime. The following program displays some valid specifiers and the output they produce:

      // Examples of ITime output
    
      #include <istring.hpp>
      #include <itime.hpp>
      #include <iostream.h>
      #include <iomanip.h>  // needed for setw(), to set output stream width
    
      void main() {
         char* FormatStrings         ={
            "%H : %M and %S seconds", // %H, %M, %S - 2 digits for hrs/mins/secs
            "%r",                     // %r - standard 12-hour clock with am/pm
            "%T",                     // %T - standard 24 hour clock
            "%T %Z",                  // %Z - local time zone code
            "%1M past %1I %p"         // %1... - One digit for hour/minute
            };                        // %p - am/pm
    
         cout.setf(ios:left,ios.:adjustfield);    // Left-justify output
    
         cout << setw(30) << "Format String"  // Title text
              << setw(40) << "Formatted ITime object" <  ;< endl;
                for (int i=0;i<5;i++) {       // Show each time
                    IString Formatted=ITime.:now().asString(FormatStrings[i]);
                    cout << setw(30) << FormatStrings[i]
                         << setw(40) << Formatted << endl;
                    }
                 }
    

    Note: The format specifier %n, where n is an integer, is not supported by strftime on MVS. As a result, if you use a format specification string containing %n in ITime output, the format specification string may appear in place of the desired output.

    The program produces output that looks like the following:

          Format String                 Formatted ITime object
          %H : %M and %S seconds        16 : 13 and 04 seconds
          %r                            04:13:04 PM
          %T                            16:13:04
          %T %Z                         16:13:04 EST
          %1M past %1I %p               13 past 4 PM
    

    Simple Combined Date and Time Example

    The following example shows a class MyDateTime that links its date and time data members together within its addition operator definition. The class has three data members, one from each of IDate and ITime, and one for the number of days to be added when the addition operator is used. The class defines two addition operators: one that accepts another MyDateTime object, and uses the number of days and the time as the basis for the addition; and one that accepts only an ITime object, and adds that to the MyDateTime object's time. Both addition operators check for wraparound in the ITime member, and increment the IDate member if wraparound has occurred.

        CLB3ADTI
    
      // Simple combined date-time class
      #include <idate.hpp>
      #include <itime.hpp>
      #include <iostream.h>
    
      class MyDateTime {
        public:
         IDate date;       // date subobject
         ITime time;       // time subobject
         int addDays;      // number of days to add for addition operator
    
         // Copy constructor
         MyDateTime(IDate adate, ITime atime, int add = 0):
            addDays(add), date(adate), time(atime) {}
    
         // Default constructor
         MyDateTime() {}
    
         // Addition operator for other MyDateTime objects
         MyDateTime operator + ( const MyDateTime &aDateTime) const {
            MyDateTime temp;
    
            // Add any addDays value to date if necessary
            temp.date = this->date + aDateTime.addDays;
    
            // Add times together
            temp.time = this->time + aDateTime.time;
    
            // If resulting time is greater than original time,
            // clock wrapped around, so increment date
            if (temp.time < this->time) temp.date += 1;
    
            return temp;
            }
    
         // Addition operator for ITime objects
         MyDateTime operator + ( const ITime 11:21:12) const {
            MyDateTime temp;
            temp.date = this->date;
    
           // Add time to time member of MyDateTime temporary
            temp.time = this->time + time;
    
            // If resulting time is greater than original time,
            // clock wrapped around, so increment date
            if (temp.time < this->time) temp.date += 1;
    
            return temp;
            }
         };
    
      ostream& operator << (ostream& os, MyDateTime& dt) {
         cout << dt.date << " at " << dt.time;
         return os;
         }
    
      void main() {
         MyDateTime TodayNow(IDate:today(), ITime.:now()),
                    Add, Temp;
         Add.addDays = 17;
         Add.time = ITime(12,24);
         Temp = TodayNow + Add;
         cout << "Right now it is " << TodayNow << ".\n"
              << "In 17 days, 12 hours, and 24 minutes, it will be "
              << Temp << "." << endl;
    
         Temp = TodayNow + ITime(21,16);
         cout << "In 21 hours and 16 minutes, it will be " << Temp <<  "." <<
         }
    

    This program produces output that resembles the following:

          Right now it is 06/22/94 at 13:25:31.
          In 17 days, 12 hours, and 24 minutes, it will be 07/10/94
          at 1:49:31.
          In 21 hours and 16 minutes, it will be 06/23/94 at 10:41:31.
    

    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 our answer in the next newsletter.

    Question - Opening a file

    I can't figure this out. I'm trying to open a file for binary output. I try this:

    #define OPEN_FLAGS ios::out | ios.::binary
    #define OPEN_PROT   filebuf.::openprot
    ofstream os("dd:trpfile","wb,lrecl=8205, recfm=vbm,blksize=8209",
                 OPEN_FLAGS,OPEN_PROT);
    
    
    : : but os is null when I try to write to it. Then I try this:
    :
    FILE *fh;
    if (!os)
    {
    cout << "Output stream in invalid state." << endl;
    fh=fopen("dd:trpfile","wb,lrecl=8205,recfm=vbm,blksize=8209");
    if (!os) perror ("error opening file");
    }
    
    : and get the following message from perror:
    :
      error opening file: EDC5057I The open mode string was invalid.
    

    Any ideas? I get the same error if I specify the file name instead of using a DD statement. The file exists and is 8205/8209/vbm.

    Answer

    ofstream os("dd:trpfile","wb,lrecl=8205,recfm=vbm,blksize=8209",
                 OPEN_FLAGS,OPEN_PROT); 

    Given the above call to the ctor, iostreams maps the OPEN_FLAGS value of ios::out | ios.::binary to "wb, " and then appends the second argument passed to the ctor. So, by the time it gets to fopen(), it looks like:

    fopen("dd:trpfile","wb,wb,lrecl=8205,recfm=vbm,blksize=8209"); 

    The call to the ctor should be:

    ofstream os("dd:trpfile","lrecl=8205,recfm=vbm,blksize=8209",
      OPEN_FLAGS,OPEN_PROT); 

    Question - C global variable and CMS multitasking threads

    Are C global variables shared across threads or does each thread get its own independent copy. When a program I was writing bombed a trace showed very different addresses for the same global variable; I tentatively assumed that gv's "instance" upon thread creation, but when I wrote a q&d to try to demo this it failed - a gv changed on thread 1 then printed on thread 2 showed the changed value. Anyone have any ideas on this?

    Answer

    There should be only one copy of a global variable across threads. I am surprised that you have two different addresses for the same global variable from different threads. Could one of them be overwritten somehow? The only case when they have 2 distinct addresses is when in a thread a fetch and a fetch execution to another program were carried out. However, the global variable in a fetched program is distinct from the global variables of the root program. Also, ensure you define #pragma variable(xxxx, NORENT) otherwise you will get the symptoms you describe.

    Question - C Compiler That Supports Packed Decimal

    We currently have C/370 v2.1. We need to access DB2 tables that have columns defined as DECIMAL. Using a data type that was close (such as double) caused DB2 to ignore the indexes. We have gotten around this with some #ifdef's that make DB2 think the data type is a decimal while C treats it as a char. Is there a C compiler (perhaps in LE/370 or even C++) that supports packed decimal data types? The DB2 v4.1 manual hints at the existence of such a thing without referencing any vendors.

    Answer

    Congratulations! You've just described AD/Cycle C/370 V1R2. It can run with either the V2R2 runtime or with LE V1R3 (or higher), depending on how you choose to install it. Regardless of how it is installed, it can generate object code in either style: V2R2 or LE style, through the TARGET option. And the _Decimal datatype is supported by both the compiler and runtime as a native data type.

    Here's a example program that you may want to make use of in your application.

     #include <decimal.h>
     main()
     {
     _Decimal(15,3) abc,def;
     abc = 10.5d;
     def = 3.3d + abc;
     printf("%D(15,3)\n",def);
     }
    

    Question - __STDC__

    What exactly does __STDC__ mean? I have an application which tests '__STDC__' to see whether it should use the 'a ## b' convention for token catenation in macros. (Otherwise it will try to use the 'a/**/b' convention).

    With 'c89 -W0,langlvl=extended' under MVS/OE, it finds __STDC__ undefined and assumes (incorrectly ...) that it should use 'a/**/b'. (This with the 'new' C/C++ compiler)

    I thought '__STDC__' meant 'This compiler will compile programs written in ANSI C, and possibly a few more besides', and therefore it ought to be set when langlvl=extended. Should it ?

    Answer

    __STDC__ says that the compiler is a conforming implementation (ANSI sec 3.8.8) which means that it will accept any strictly conforming program (ANSI sec 1.7).

    A conforming program is one which uses only the features of the language and the library which are described in the standard.

    The absence of a definition for __STDC__ does not in any way imply the implementation of any particular obsolete feature (such as concatenation using comments). It just means that some strictly conforming programs won't compile.

    The reason it is not defined for langlvl=extended is because langlvl=extended exposes a bunch of extra functions in the library headers. This means that some strictly conforming programs which use these same names won't compile.

    Question - C/370 and C 3.1

    We are programming using C 2.1. Is it possible to execute our code using the C 3.1 (LE) runtime libraries?

    Answer

    "Most well-behaved" programs compiled and linked under V2 will continue to run under LE. The "well-behaved" definition means that no undocumented interfaces are written to. "Most" means non-ILC, non-SPC, non-cdump/csnap/ctrace. Any V2 module can be executed under LE, if linked with LE.

    Question - Compiler - WARNING EDC0848

    I have been getting the following warnings when compiling C code:

    WARNING EDC0848 CARDBLD.CCEDTPKG.C:158   Some program text not
    scanned due to MARGINS option
    
    It is complaining about text in column 73-80. I thought this was controlled by the Sequence option(which in this case defaults to 73-80) and should not be a problem. I can get rid of the warning by removing all text in columns 73-80 but shouldn't have to if Sequence was specified. Any idea what is going on? Here's my compiler output.
    5688187 V2 R1 M00 IBM C/370
    CARDBLD.CCEDTPKG.C
                                          * * * * *   P R O L O G   * * * *
     Command options:
    
        Program name. . . . . . . . . : CARDBLD.CCEDTPKG.C
        Compiler options. . . . . . . : *NOGONUMBER *NOALIAS    *NODECK
    *NORENT  *TERMINAL   *NOUPCONV   *SOURCE  *NOLIST
                                      : *NOXREF     *AGGR       *NOPPONLY
    *NOEXPMA  *SHOWINC    *NOOFFSET   *NOMEMORY   *NOSSCOMM
                                      : *NOCSECT    *NOLONGNAME *START
                                      : *TARGET()   *FLAG(I)
    *NOTEST(SYM,BLOCK,LINE) *OPTIMIZE(1)*SPILL(128)
                                      : *NOINLINE(AUTO,NOREPORT,250,1000)
    *NESTINC(16)
    *CHECKOUT(NOPPTRACE,PPCHECK,GOTO,ACCURACY,PARM,NOENUM
    NOEXTERNAL,TRUNC,INIT,NOPORT,GENERAL)
                                      : *NOSEARCH
                                      : *NOLSEARCH
                                      : *OBJECT
        Language level. . . . . . . . : *EXTENDED
        Source margins. . . . . . . . :
          Varying length. . . . . . . : 1 - 32767
          Fixed length  . . . . . . . : 1 - 72
        Sequence columns. . . . . . . :
          Varying length. . . . . . . : none
          Fixed length. . . . . . . . : 73 - 80
    

    Answer

    This warning is telling you that the compiler did in fact see some text in the columns excluded by the MARGINS/SEQUENCE combination and that this text was ignored. This warning is on because you have CHECKOUT(PPCHECK) turned on.

    Question - C370LIB directory and multiple object libraries

    If I have a setup to combine object modules from two or more object libraries, by allocating them to SYSLIB when I call the prelinker, what happens with the C370LIB directory (@@DC370$)? Does the prelinker see only the directory in the first object library in the SYSLIB concatenation?

    I would like applications to use a common object library, and ideally I would like the members of the common library to use long names that reference one another. So I have a common library with its own C370LIB directory and my application library with its own C370LIB directory.

    Putting the common library second in the SYSLIB concatenation, will the prelinker be able to resolve the long names in both libraries? And if not, is it possible to call the C370LIB utility to create a @@DC370$ member that uses long names from members of both object libraries?

    Answer

    Yes, the prelinker can correctly resolve a concatenation that contains multiple libraries built using C370LIB. It actually looks at each dataset in the concatenation and reads the @@DC370$ member associated with it (if there is one) and builds it's own set of 'longname' aliases (in essence) for each member of each PDS. For example, if you had:

    USER.OBJECT1(mem1) <---longnames of 'joe' and 'fred' in here
    USER.OBJECT2(mem2) <---longnames of 'steve' and 'bill' in here
    
    and these datasets are on a SYSLIB concatenation, then if your code has references to 'joe' and 'steve' it will know to pull in both mem1 (to resolve 'joe') and mem2 (to resolve 'steve').

    Question - How do I disable full word alignment of int & long?

    I am writing a C370 program to process SMF data from NetView's NLDM component. Here is part of the record structure.

      struct   s_nldm_smf {       /* nldm smf record structure */
        char   s_nldm_smf_sysi;
        char   s_nldm_smf_type;
        short  s_nldm_smf_time;
        short  s_nldm_smf_date;
        char   s_nldm_smf_smfid ??(4??);
        char   s_nldm_smf_name ??(4??);
        int    s_nldm_smf_subt;
    

    The first two fields, both CHAR, map fine. The next field, type INT, skips ahead to a full word boundary. It's like something in C370 forces short, int and long to full word boundaries. If I define the field as CHAR, it maps out correctly. It there something in our C370 install or a compile or run-time parm to disable this full word alignment 'feature' ?

    Answer

    C/370 aligns fields on their "natural" boundaries. However, you can modify that behavior by using the _Packed attribute. In the structure definition you can say "_Packed struct { ..." -- and then C/370 will ignore the natural boundaries and align each field on the next available byte boundary.

    Question - JCL Setup

    I'm trying to execute a C/370 V2.1 batch program under MVS/ESA 4.3. I can't determine how to set up the JCL with an input and output file. As you can see below the input file is input2 and the output file is output2.

      FILE *in;
      FILE *out;
      in=fopen("input2","r");
      out=fopen("output2","w");
        while ((ch = getc(in))  != EOF)
        "
     putc(r3"r2"r1"ch+ofs1&MASK"+ofs2&MASK"+ofs3&MASK",out);
    
    The JCL that doesn't work is
     //INPUT2 DD DSN=ANYNAME,DISP=SHR
     //OUTPUT2 DD  DSN=ANYNAME,DISP=SHR
    
    This is a simple question but this is my first program and I can't find an example. The error message I received in executing the getc instruction was:
     IBM534I  'ONCODE'=8094  PROTECTION EXCEPTION
        AT OFFSET +0000A2 IN PROCEDURE WITH ENTRY encode
     'FINISH' CONDITION RAISED
        AT OFFSET +0000A2 IN PROCEDURE WITH ENTRY encode
     FROM OFFSET +0000D4 IN PROCEDURE WITH ENTRY main
    

    Answer

    Your current sample C/370 program FOPEN statement is referencing a specific dataset name ie INPUT2. If you wish to reference a ddname with MVS JCL then a sample FOPEN statement would be:

         in=fopen("DD:CFILE","r"); 
    :
         //CFILE  DD DSN=datasetname,DISP=SHR
    
    NOTE: to open an MVS file for write access you need DISP=OLD For further explanation and examples, please refer to IBM C/370 Programming Guide- Chapter 29 - Opening Files under MVS.

    Question - 'Space' parameter seems to default to 1

    I try to allocate a file with the 'SPACE' parameter. An example follows:

     #include <stdlib.h>
     #include <stdio.h>
     int main()
     {
     FILE *fd;
      fd = fopen("'sss09.jqtest'","wb,space=(cyl,(10,,10))");
       perror("Error");
     return(0);
     }
    

    The file gets allocated, but the PRIMARY seems to DEFAULT to '1' If I change units to 'cyl' 1 cyl is allocated, 'trk' 1 trk is allocated, 'blksize' 1 trk is allocated.

    Answer

    The results you are seeing is not because of the defaulting by the C/370 library. The current design releases the unused space as if RLSE is coded for the SPACE parameter.

    Question - Spanning Record

    I have a dataset with the following description : RECFM = VBS , RECL = 4004 , BLKSZE = 4000. I try to read this dataset using C370 program. If the data in the record within the maximum size of the record, the record is read without any problems.

    The problem appears if the record is spanned, the C/370 program will read only the first part of the record (3996), and it skips the spanned portion, and then it reads the second record and so on. The result is that the data read is not what it should be . I have another PL/I program that is able to read the records completely, even if it is spanned.

    Answer

    The design of the C/370 2.1 Library supports VBS file records up to the LRECL, including the ones greater than the BLKSIZE and spanned. Record sizes greater than LRECL are not supported. This support has been added to C/370 2.2 Library and LE 1.3 and higher, and ONLY if LRECL=X is used.

    Question - Class lib compile

    To compile a C++ program using I0String and IQueue, what data sets should be used for SYSLIB in a 3.2 compile? I find headers for I0String and IQueue in:

    SCLD3H.H
    SCLD3H.HPP
    SCLB3H.H
    SCLB3H.HPP
    
    I also concat these to SYSLIB:
    CEE.V1R5M0.SCEEH.SYS.H
    CEE.V1R5M0.SCEEH.SYS.H
    

    What should be used and is the order important?

    Answer

    You should not use the class library header files in SYSLIB. Use the SEARCH option instead. (SYSPATH could also be used.)

    Datasets that begin with SCLD belong to the source feature, and should *not* be used unless you have built your own copy of the libraries from the source. In general, use the SCLB datasets only. I would recommend a SEARCH option like:

       SEARCH('CBC.SCLB3H.+','CEE.V1R5M0.SCEEH.+')
    
    The compiler will then search all datasets that begin with these names for the include files.

    Question - C Compiler generated code performance

    I moved a C program from my SPARCstation(**) 5 to MVS recently and I was disappointed to find that it ran in 5.2 seconds CPU on the Sparc but 20.5 seconds CPU on the 9021-832.

    Upon investigation I found the MVS C compiler is generating about 6 times as many instructions as the SPARC compiler, consistently across varied functions.

    The program has minimal string processing. Nor is it a case of LE/370 runtime overheads. According to STROBE(**), the time is going in the generated code. I tried optimize and this reduced the time to 15 seconds cpu. However a more recent run on a Pentium(**) plus with GCC optimize ran in 1.3 seconds. The algorithm is an implementation of DES.

    Do you have any suggestions as to what I can do to improve the performance.

    Answer

    Some options that can slow you down are DLL and RENT (though you probably don't want to remove RENT).

    One big difference between PC's and mainframes is the cost of a function call. So using the inliner (to remove function calls) along with the optimizer will generally help. Here are some basic instructions for tuning with the inliner.

    1. Use #pragma noinline(funcname) for low use functions, ie. debug or error handling functions.
    2. Compile with the OPTIMIZE option and ask for a report from the inliner. For C, compile with "INLINE(,REPORT,,)".
    3. Look at the report to see what was inlined that should not have been(see 1 above).
    4. For frequently used routines, add the "inline" keyword (C++) or the "#pragma inline" directive (C) to inline.
    5. Recompile with OPTIMIZE, regenerate the inline report, and check for functions that should and should not be inlined.
    6. For C vary the limit and threshold values.
      • The inline report shows the abstract code units (ACUs) for each function. With these you can determine an appropriate starting threshold. In general the initial threshold should be small and the initial limit should be 1000-2000.
      • Increase the threshold slightly to catch a few more functions.
      • Because performance will improve as a function of both the limit and the threshold values, don't change both of them at the same time. I usually use 50, 1000 as a baseline and alternately increment the threshold by 50 and the limit by 250. When you hit a degradation determine why a new function got inlined to cause it by slowly degrading the limit. #pragma noinline the offending function and continue to use auto tuning to see further inlining is required.
    7. Repeat the process until you have the best performance.
    If this program is multi-file (and no static name collisions exist between source files) then you may get some gains by #including all of the source into 1 file and then running the inliner on that. There is additional information in the Optimization Chapter of programming guide.

    Question - Which modules should I put in LPA?

    I need a recommendation of which module for IBM C/C++ V3.1 I should place in LPA. I have already placed SCEERUN for the C-RTL in LPA.

    Answer

    If you are doing C/C++ compiles very often, I would recommend you put the SCBC3CMP compiler dataset in the ELPA. The compiler will run faster if it is in the ELPA since it has to load itself.

    If your C++ code uses the class libraries, I would recommend you put the SCLB3DLL class library DLLs in the ELPA. This is less important than the first recommendation.

    Question - Linking C/370 with Sockets and APPC code libs

    The following is the JCL that I am using to link three C objects with the LIB of APPC (SYS1.CSSLIB).

    //LINK    EXEC EDCPL,PPARM='OMVS,NONCAL'
    //PLKED.SYSINP   DD DSN=DASER.IOAATM.TEXT(IOANMAIN),DISP=SHR
    //   DD DSN=DASER.IOAATM.TEXT(IOANOTCP),DISP=SHR
    //   DD DSN=DASER.IOAATM.TEXT(IOANAPPC),DISP=SHR
    //PLKED.SYSLIB   DD DSN=&LIBPRFX..SCEEOBJ,DISP=SHR
    //   DD DSN=&LIBPRFX..SCEERUN,DISP=SHR
    //PLKED.SYSIN    DD *
                    INCLUDE SYSLIB(EDCOEXT0)
                    INCLUDE SYSINP
    //LKED.SYSLIB   DD DSN=SYS1.CSSLIB,DISP=SHR
    //LKED.SYSPRINT DD DSN=DASER.IOAATM.OUTLIST(IOANMAIN),DISP=OLD
    //LKED.SYSLIN   DD DSN=*.PLKED.SYSMOD,DISP=(SHR,DELETE)
    //LKED.SYSLMOD  DD DSN=DASER.IOAATM.LOAD(IOANMAIN),DISP=SHR
    

    When I run this JCL it I get unresolved reference for standard C functions like printf and malloc. If I move the SYS1.CSSLIB after the PLKED.SYSLIB statement without the LKED.SYSLIB I get the error that it can't open syslib.

    Answer

    You need to add the CEE.V1R5M0.SCEELKED dataset to your link-edit step (note that the name on your machine may be different, but it probably ends in SCEELKED).

    You will still get a warning from the prelink step, but the resultant link-edit will be clean. So, it should be:

    //LKED.SYSLIB   DD DSN=SYS1.CSSLIB,DISP=SHR
    //              DD DSN=&LIBPRFX..SCEELKED,DISP=SHR
    

    Make sure that the block size on SYS1.CSSLIB is at least as big as the SCEELKED dataset.

    Also, your prelink SYSLIB should be:

    //PLKED.SYSLIB   DD DSN=&LIBPRFX..SCEEOBJ,DISP=SHR
    

    Question - "fetch" function conflict

    We are migrating a considerable amount of UNIX(**) code to OpenEdition(*). I have a problem with a naming conflict involving the "fetch" function.

    UNIX systems often run a database manager known as "DBM". DBM has several APIs, one of which is called "fetch". This conflicts with the "fetch" that in IBM C/C++. The IBM "fetch" is in stdlib, along with many other functions that DBM uses. So, to migrate DBM, stdlib must be included, which obviously conflicts between the IBM "fetch" and the DBM "fetch".

    I could change the DBM "fetch" to "dfetch", but then I would have to change every program that uses the DBM "fetch" to "dfetch". How do I resolve this problem?

    Answer

    Compiling with LANGLVL(ANSI) instead of the default LANGLVL(EXTENDED) should fix this problem so that the user gets his fetch, not MVS's.

    Question - function like macro

    My question concerns a "function like macro" where I am having to use the "\" to continue the macro. When I look at the expansion in the listing (using the EXPMAC option), the entire macro is on 1 line. This makes it hard to debug the thing. Any way to make the macro expand to multiple lines?

    Answer

    There is no PP (preprocessor) option or service to allow for this. In fact, the macro expansion logic involves several layers of buffering; the continuation characters would get discarded as soon as they're scanned. Also, please note that the PP will skip all extra blanks in a function like macro; any attempt to make the replacement code wider than the 132 column limit of the listing will not cause the line to be expanded and displayed differently. i.e.

     #define FUNC(x) { if ((x))                                              { \
                         printf(..); }
                     }
    
    will still be expanded to a single line.

    Question - DLL's

    Is there any disadvantage (in code size or within-the-DLL linkage) in specifying EXPORTALL ?

    I'm porting some code from AIX. I have a list of symbols which need exporting from a DLL (which I can use to post-process the '.x' file); but the individual compilation units are not tagged up with the export information.

    Answer

    No, EXPORTALL will instruct the prelinker to generate a lot more symbols in the definition side deck. The key disadvantage to EXPORTALL vs. #pragma export is that you may pollute your user's namespace with a bunch of irrelevant functions you are exporting that he doesn't need to know about. With #pragma export, you can precisely control what stuff can be referenced by the end user. From a different perspective, it also prevents users from coding to 'undocumented' interfaces that you have exported to them. You may get performance gains when prelinking if you use #pragma export instead of EXPORTALL, since they will be prelinking a much smaller definition side deck (some definition side decks can be thousands of records long when EXPORTALL is used).

    However, it's easier to port a program to MVS by just compiling with EXPORTALL than changing your source to add #pragma export. If you have stable code, you can manually prune the definition side deck to keep only the functions/variables you want to export. A little warning - don't _add_ new export entries, only _delete_ them from a definition side deck.

    Question - Updating tape files

    My program opens a file with "ab+". This file is defined in the JCL DD Statement with LRECL,DSORG,RECFM,SPACE etc.. Last month there was a shortage on DASD and the O/S changed the UNIT field to TAPE. As described in the programming guide it is impossible to open a file on tape with the append-option. Why is it not allowed to open a file on tape with "ab+" and how can I verify that the fopen failed because the UNIT is a tape?

    Answer

    Tape drives don't support updating. So you should be able to open in rb, wb or ab mode, but not rb+, wb+ or ab+. Appending is OK. Try using perror() which should issue a message like:

    EDC5088I An invalid open mode was specified for the
    the current device.
    

    Question - Calling C++ from COBOL

    I've successfully built a DLL and can call it from C and C++ applications. Now I need to try to call it from COBOL. I see in the C++/MVS Programming Guide (ch 20, p224) under DLL Restrictions it says "DLL facilities are not available to application programs written in COBOL and that dynamically call to C/MVS functions".

    Can someone tell me more about what this means? Can I not call C++ functions (that have extern C) from my COBOL app or just not as a DLL? and if not as a DLL? How would I call them, other than linking in all my text decks with the user's application (which they've told me is not acceptable).

    Answer

    If you want to have COBOL call C++ code that is in a DLL, I would recommend you provide a thin stub library that you link in with your COBOL code. The stub library will be of the form:

        extern "C" int ENTRY1(int p1, int p2) {
          return(DLL_ENTRY1(p1, p2));
        }
    

    This will enable COBOL to call the C++ DLL routines - COBOL can't call them directly because it doesn't generate the special code that will dynamically load the DLL at run-time. By binding this stub into your code, you can use your DLLs without having to statically bind very much stuff into your COBOL app.

    Question - Porting to MVS from VisualAge(*) for OS2 using Istring

    I'm trying to port a small application from VisualAge C++ for OS2 to MVS C++ using open class libraries. One of the most common problems I'm running into is mixing IStrings with chars. For example:

    func(IString& inString)
    {
        if (inString[5] == 'a')
           ...
    
    In VisualAge the conversion from an IString to a char or char* is pretty versatile, but not in MVS C++. The documentation says the above operand || is suppose to treat the results as either a const char or a char, but I get the following message from the compiler:
    line 12.36: CBC1231(S) Call to "operator  " matches some
    functions best in some arguments, but no function is a best
    match for all arguments.
    
    Is this working as designed?

    Answer

    This is due to the compiler performing arithmetic widening for the '=' operator. The 'A' is widened to an integer as a standard promotion, then it is truncated to a character again.

    It is debatable that the compiler could be 'smarter' and see this immediate widen/truncate feature and suppress the informational.

    I would recommend you write:

        int x;
        x = 'A';
      or:
        char x;
        x = (char) 'A';
    

    Either of those should suppress the message.

    Question - LE C 370 & APPC

    I've been digging for some information on how to invoke APPC services via LE C 370. Further if what the requirements are to latch into APPC MVS or VTAM services. I've been able to find some high level information in Book Manager(*) but haven't come across any "here's what you need" (requirements inventory list) and "here's the transition" from your C program to the APPC service. Can you point me in the right direction?

    Answer

    Perhaps the book you want is:

       MVS/ESA
    
       Application Development:
       Writing Transaction Programs for APPC/MVS
    
       MVS/ESA System Product:
       JES2 Version 4
       JES3 Version 4
    
       Document Number GC28-1121-04
    

    The examples are PL/I-oriented, but C is in there too.

    Question - VL-style parms passed to main

    We sell an assembler-language file transfer utility that I'm currently rewriting in AD/CYCLE C370 release 2 for MVS. The present version of our product supports being called from an application program as well as from an EXEC statement. As do many IBM utilities - IDCAMS, IEBGENER, etc. - we allow these callers to supply optional parms beyond the 1st, JCL-style one, by using the standard VL-list format (the last parm address in the list is flagged with the high-order bit on). I'm currently looking for a way to accomplish this in C370. Would you have any suggestions as to the best way to accomplish this?

    I've found the NOARGPARSE pragma that coerces the entire parm string into argv[1], but it doesn't seem to go the next logical step, which would be to put any other argument addresses into argv[2-n] and bump up argc accordingly.

    Answer

    Here's how you can get OS style linkage with C without system programmer C. Use:

     #pragma runopts(PLIST(OS),NOEXECOPS,NOARGPARSE,NOREDIR)
    

    Then, in your program, to get the address of R1, use __R1. For documentation on __R1 and the pragma's used, see the LE Programming Guide section "C and C++ Parameter Passing Considerations".

    Question - Entry Points

    I am responsible for moving new parts of the application from the test environment into production. I use ENDEVOR(**) to manage our software libraries. I wrote a series of C/370 programs some of which interact with IMS. I am a former UNIX programmer and have always defaulted on specifying an ENTRY point. I put this program suite in ENDEVOR and now I get "bad entry points."

    Our question for you is: How do we identify the entry points?

    Answer

    Most vanilla C/C++ load modules have an entry point of CEESTART.

    If you use the LIST LOAD function of AMBLIST, it will tell you the entry point for a load module. This is the only way to be sure, since load modules that are fetched may not have an entry point of CEESTART .

    In addition, load modules that contain other languages (notably COBOL) can have an entry point other than CEESTART.

    Question - A CVB type of function

    I need a piece of code to convert a packed decimal number to a binary number. In assembler this is rather simple to do using the CVB instruction. CVB is the S/390 convert to binary instruction. How do I do it in C/370?

    For example, I have an unsigned long int A and A=x'0004567f', and A is a packed decimal number. I need to convert this binary value to the decimal equivalent of d'4567' (ie. four thousand, five hundred and sixty seven (base 10)).

    Answer

    The AD/Cycle C/370 and following compilers have built in packed decimal support that will do this for you; for example:

        #include <stdio.h>
        #include <decimal.h>
    
        int main(void)
        {
            decimal(8,0) ad80 = 4567d;
            unsigned long aul = ad80;
    
            printf("ad80 = %D(8,0); aul = %lu.\n", ad80, aul);
            return 0;
        }
    
    The listing shows that the compiler generates the CVB instruction inline for you:
                        00007 |    * unsigned long aul = ad80;
    000052  D204  D0BB  D090    30      MVC   187(5,r13),144(r13)
    000058  D702  D0B8  D0B8    31      XC    184(3,r13),184(r13)
    00005E  4F10  D0B8          32      CVB   r1,184(,r13)
    


    A word from the 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).

    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 4 November/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-2893. Please mark your fax for the attention of Gord Sinclair. Thanks.

    C/370 Compiler News Volume 4 Number 4 November/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
                          91/604/895/TOR
                          895 Don Mills Road
                          North York
                          Ontario, Canada
                          M3C 1W3
    


    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.