Welcome to the July, 1995 Issue of the C/370 Newsletter

IBM Corp 1995

                  370 370 370 370 370 370 370 370 370 370
              370 370 370 370 370 370 370 370 370 370 370 370
              370 370 370 370 370 370 370 370 370 370 370 370 370
         370 370 370 370 370 370 370 370 370 370 370 370 370 370
         370 370 370 370 370 370 370 370 370 370 370 370 370 370
         370 370 370 370 370 370 370 370 370 370 370 370 370 370
         370 370 370
         370 370 370
         370 370 370
         370 370 370
         370 370 370 370 370 370 370 370 370 370 370 370 370 370
         370 370 370 370 370 370 370 370 370 370 370 370 370 370
         370 370 370 370 370 370 370 370 370 370 370 370 370 370
             370 370 370 370 370 370 370 370 370 370 370 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(TM) Compiler News

July 1995 Volume 3 Number 3


Table of Contents

Welcome to the July, 1995 Issue of the C/370 Newsletter

  • In This Issue
  • Introduction to Reentrancy in C and C++
  • Preparing a Reentrant Program
  • Controlling External Static Variables
  • Controlling Where String Literals Exist
  • Using the I/O Streams Class Library in C++
  • Advantages of Using the C++ I/O Stream Class Library
  • Predefined Streams for C++
  • How C++ Streams Relate to C Streams
  • Specifying File Attributes
  • VisualAge C++ for OS/2, V3.0
  • Construction from Parts
  • Object-Oriented Access to Relational Data
  • IBM Open Class
  • Powerful C++ Tools
  • Join The VisualAge
  • SHARE and GUIDE Sessions
  • C and C++
  • Mainframe C and C++ For Non-Mainframe Programmers
  • C/C++
  • You Ask, We Answer
    Missed our Previous Editions?
    A word from your editor
    Coming Soon
  • Reader's Comment Form

  • Welcome to the July, 1995 Issue of the C/370 Newsletter

    In This Issue

    ( )

    Introduction to Reentrancy in C and C++

    Reentrancy allows one or more users to share a single copy of a load module or to repeatedly use a load module without reloading it. Reentrancy has the following advantages:

    A Reentrant program is structured so that it can be run by many users at the same time. The program is split into two parts:

    Reentrant programs can be categorized by their Reentrancy type as follows:

    Natural Reentrancy
    These programs contain no writable static and do not require additional processing to make them Reentrant. If your programs contain no writable strings and none of your static or external variables are updated in your application (that is, they are read-only), your program is naturally Reentrant.
    Constructed Reentrancy
    These programs contain writeable static and require additional processing to make them Reentrant. Because all C++ programs are categorized as constructed reentrant, the C++ prelink and link step automatically performs a call to the IBM Language Environment® to process your compiled C++ Code. Since OpenEdition(TM) MVS(TM) features require that all OpenEdition application programs be reentrant, c89 also specifies the compiler RENT option and always invokes the prelinker during its link edit process.

    Reentrancy is an advantage if there will be concurrent users of a program. These advantages become more apparent when the program is large. Even when a program is large and will have more than user at the same time, there are also these limitations to consider.

    Preparing a Reentrant Program

    This section applies only to C/370 programs NOT running under MVS OpenEdition features since C++ and C/370 programs running under MVS OpenEdition are automatically prelinked.

    If your program contains writable static, use the Language Environment prelinker to make your program Reentrant. This utility concatenates compile-time initialization information (for writable static) from one or more object modules into a single initialization unit. In the process, the writable static part is mapped.

    To generate a reentrant load module, you follow these steps.

    1. If your program contains no writable static, compile your program as you would normally (that is , no special compiler options are required), and then go to step 4.

      Note: If you are unsure about whether your program contains writable static, compile with the RENT option. Invoking the Language Environment prelinker with the MAP option and the object module as input, produces a prelinker map. Any writable static data in the object module appears in the writable static section of the map.

    2. If your program contains writable static, you compile your C/370 source files using the RENT compiler option.

    3. Use the Language Environment prelinker to combine all input object modules into a single-output object module.

    4. Optionally, do ONE of the following

    Controlling External Static Variables

    Certain program variables with the extern storage class may be constant and never written to. If this is the case, every user does not need to have a separate copy of these variables. In addition, there may be a need to share constant program variables between C/370 and another language.

    As a programmer, you have some control over where objects with global names and string literals exist. You can use the #pragma variable(varname, NORENT) directive to specify that the memory for an object with a global name is to be in the code area rather than the writable static area.


        /***********************************************************/
        /*             RATES is constant and in code area          */
        /***********************************************************/
        #progma variable(RATES, NORENT)
        const float RATES[5] = { 1.0, 1.5, 2.25, 3.375, 5.0625}
        float totals[5];
    

    In this example, the variable RATES exists in the executable code area because #pragma variable(RATES, NORENT) has been specified. The variable TOTALS exists in the writable static area. All users have their own copies of the array TOTALS, but the array RATES is shared among all users of the program.

    When you specify #pragma variable(objname, NORENT) for a variable, and the program is to be Reentrant, you must ensure that this variable is never modified. Also you must include #pragma variable(objname, NORENT) in every source file where the object is referenced or defined; otherwise, the compiler will generate inconsistent addressing for the object, sometimes in the code area and sometimes in the writable static area.

    Controlling Where String Literals Exist

    Whether all string literals are distinct (non-overlapping) or modifiable is not defined by the ANSI C++ draft.

    In C++/MVS(TM), the string literals exist in the code part by default, and are not modifiable if the code is Reentrant. In a large number of programs, string literals may be constant. In this case, every user does not need a separate copy of these strings.

    By using the #pragma strings(writable) directive, you can ensure that the string literals for that compilation unit will exist in the writable static area and be modifiable. The following example illustrates how to make the string literals modifiable:


        /************************************************************/
        /* this example demonstrates how to make literals modifiable*/
        #pragma strings(writable)
        #include <iostream.h>
        int main(void)
        {
          char * s
          s = 'wall\n";   // point to string literal
          *(S+3) = 'k';   // modify the string literal
          cout << s;      // output "walk\n"
    

    In this example program, the string "wall\n" will exist in the writable static area because #pragma strings(writable) is specified. The fourth character is modified.

    For additional information on Reentrant code, see the Language Environment Programming Guide.


    Using the I/O Streams Class Library in C++

    The object-oriented model for Input/Output consists of a set of C++ classes that make up the I/O Stream Class Library. This set of classes implements and manages stream buffers for input and output. Stream buffers are arrays of bytes where data is stored between the program and ultimate consumer (for output), or between the ultimate producer and the program (for input). Formatting of data is also done using stream buffers and manipulators.

    There are two base classes, ios and streambuf, for which all other classes in the I/O Stream library are derived. The ios class and the classes derived from it are used to implement formatting of I/O and maintain error state information of stream buffers - implemented with the streambuf class.

    To use the I/O Stream Library, include iostream.h header file in your program.

    Advantages of Using the C++ I/O Stream Class Library

    Although input and output are implemented with streams for both C and C++, the C++ I/O Stream Class Library provides the same facilities for input and output as C stdio.h. The I/O Stream Class library has the following advantages:

    Predefined Streams for C++

    C++/MVS provides the following predefined streams:

    cin
    The standard input stream
    cout
    The standard output stream
    cerr
    The standard error stream, unit buffered such that characters sent to this stream are flushed on each output operation
    clog
    The buffered error stream.

    All of the predefined streams are tied to cout. When you use cin, cerr, or clog, cout gets flushed so that the contents of cout are sent to the ultimate consumer.

    All I/O to I/O Streams standard streams is accomplished with C/MVS(TM) standard streams:

    When you redirect or intercept a C standard stream, the corresponding C++ I/O Stream standard stream is redirected along with it. This applies unless you redirect an I/O Stream standard stream.

    How C++ Streams Relate to C Streams

    C++/MVS I/O Stream Class Library file I/O is implemented in terms of C/MVS file I/O, and is, by default, buffered from it, with the exception of cerr, which is unit buffered (iso::unitbuf).. A filebuf object is associated with each ifstream, ofstream and fstream object. When filebuf is flushed, its contents are written to the underlying C stream, which has its own buffer.

    Specifying File Attributes

    The fstream, ifstream and ofstream class are used to specialize stream input and output for files.

    For C++/MVS, overloaded fstream, ifstream and ofstream constructors, and open member functions, with an additional parameter, are provided so you can specify C/MVS fopen() You can use this additional parameter to specify any C/MVS fopen() mode value except type=record. If you choose to use a constructor without this additional parameter, you will get the default C/MVS fopen file characteristics.

    Because I/O Streams I/O is based on C/MVS I/O, output to cout or clog may be interleaved with output to stdout or stderr, by explicitly flushing cout or clog before calling the C/MVS output function. Results of attempting to interleave output to cout or clog without explicitly flushing are undefined. Input to cin may be interleaved with input to stdin, on a line-by-line basis. Results of attempting to interleave on a per-character basis are undefined.

    The following sample code illustrates use of C++/MVS predefined streams and interleaving of C/C++ Input and Output.

    Program

    // Example of interleaving I/O
    
    #include <stdio.h>
    #include <fstream.h>
    
    
    int main() {
       cout << "object: to illustrate interleaving I.O\n"
            << endl;
       printf( "interleaving output");
       cout << "works with an (endl of line 1) \n" << flush;
       cout << "explicit flush of cout           " << flush;
       printf( "(end of line 2) \n\n");
    
       char string1 ??(80??) = "";
       char string2 ??(80??) = "";
       char string3 ??(80??) = "";
       char* rc = NULL;
    
       cout << "type the following 3 lines:\n"
                       "interleaving input\n"
                       "on a per-line basis\n"
                       "is supported\n" << endl;
       cin.getline(string1,80);
       rc = gets(string2);
       cin.getline(string3,80);
       cout << "\nstring1 is " << string1 << "\n"
            <<   "string2 is " << string2 << "\n"
            <<   "string3 is " << string3 << "\n" << endl;
    
               //The endl manipulator inserts a newline
               //character and calls flush().
    
       char char1 = '\0';
       char char2 = '\0';
       char char3 = '\0';
    
        cout << "type the following 2 lines.:\n"
             << "results of interleaving input on a per-\n"
             << "character basis are not defined\n" << endl;
    
        cin >> char1;
        char2 = (char) getchar();
        cin >> char3;
        cout << "\nchar1 is " << char1 << "\n"
             <<   "char2 is " << char2 << "\n"
             <<   "char3 is " << char3 << "\n"  << endl;
    
     }
     

    Input/Output

     // sample output (with user input shown with **)
     //                and program output with //)
    
    
     // object: to illustrate interleaving I/O
    
     // interleaving outputworks with an (end of line 1)
    
     // explicit flush of cout           (end of line 2)
    
    
    
    
    
    
    
     // type the following 3 lines:
     // interleaving input
     // on a per-line basis
     // is supported
    
     ** interleaving input
     ** on a per-line basis
     ** is supported
    
     // string1 is interleaving input
     // string2 is on a per-line basis
     // string3 is is supported
    
    
    
    
    
    
    
    
     // type the following 2 lines:
     // results of interleaving input on a per-
     // character basis are not defined
    
     ** results of interleaving input on a per-
     ** character basis are not defined
    
    
     // char1 is r
     // char2 is c
     // char3 is e
    

    For more information on the classes available with the I/O Stream Class Library and how to use them, see the C++/MVS Class Library Reference and the C++/MVS Class Library User's Guide.


    VisualAge C++ for OS/2, V3.0

    The new generation of C++ has arrived. IBM VisualAge C++ for OS/2, V3.0 takes C++ application development to new levels of productivity. Now, mission-critical , object-oriented applications are within easy reach with Visual Age C++'s powerful application "construction from parts" paradigm. VisualAge C++ delivers:

    Construction from Parts

    With VisualAge C++, application construction has never been easier. Even the most complex applications can be constructed from the large set of predefined parts from IBM Open Class. You can also create your own parts and import them easily to the visual builder. These parts can be assembled quickly and easily with the visual builder, and your application can be generated with the click of a button. Reuse is now real world!

    The visual builder generates ANSI-compliant C++ source code that is compiled into a highly-optimized application. No performance compromise and a royalty-free runtime environment.

    Object-Oriented Access to Relational Data

    Now, make quick work of bringing existing relational data into the object world. The Data Access Class Builder visually maps DB2(TM) for OS/2 relational database tables into objects with a single click. All of the C++ and SQL code generation is done for you. Simply import these "data objects" into the visual builder and construct your application. An underlying set of class libraries handles the complexities of data access and CORBA-compliant persistent object storage.

    IBM Open Class

    IBM Open Class is a set of C++ class libraries that gives you an extensive choice of building blocks for creating your applications. While Open Class handles the complexity of low-level APIs, you can focus on your application. Because Open Class is the foundation for the visual builder, you become productive immediately by using real objects directly from the builder. You spend more time building applications, not learning the complexities of object-oriented designs and class libraries.

    You can also create your own custom objects and extend Open Class by using the extensive set of C++ programming tools integrated in VisualAge C++. With IBM Open Class, you have the flexibility of a consistent programming interface across a wide range of platforms including: OS/2, Warp, AIX®, and Sun Solaris(**). In the future, it is our intent to make VisualAge C++ available on MVS, OS/400®, Windows NT(**), Windows 95(**) and OS/2 for the PowerPC(TM). With IBM's C++ environments on these platforms, you can code your application once and deploy it anywhere in your enterprise.

    Powerful C++ Tools

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

    Get Started Fast With WorkFrame and Project Smarts

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

    The New Standard in C++ Browsers

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

    From Bug-Free to Blazingly Fast

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

    Easy Editing

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

    Superior C++ Code

    The 32-bit C/C++compiler delivers rock-solid code ready to meet the demands of your business.

    With precompiled header files, you can expect fast compiles. A new 32-bit linker, built for C++, is up to three times faster than the previous version. C++ generates applications that are highly optimized for OS/2. In addition, code can be optimized for any Intel(**) processor from the i386 to the Pentium(**).

    Create SOM Objects Easily

    Now, you can generate SOM objects directly from familiar C++ syntax simply by turning on a compiler option. The compiler will also generate the corresponding Interface Definition Language(IDL) for interlanguage or Distributed SOM applications. In addition, you can browse and debug SOM objects with the VisualAge C++ tools. SOM objects can be imported to the visual builder so distributed object applications can be assembled quickly.

    Join The VisualAge

    VisualAge C++ is part of the IBM family of C++ products, which provides a consistent set of tools and class libraries across many platforms including OS/2, Warp, AIX, Sun Solaris**, MVS and, in the future, OS/400, Windows NT, Windows 95 and OS/2 for the PowerPC.

    SHARE and GUIDE Sessions

    Come and talk to the developers of mainframe C/C++ at GUIDE in Boston, July 16-21 and at SHARE in Orlando, August 13-18. Check the appropriate calendar for session numbers. The sessions currently planned are:

    C and C++

    : What's New and What's Coming?

    The information processing business has never been more complicated. We're trying to deliver products and related tools to help you simplify it. Come and hear what changes we've made, and are making, with C and C++; learn how C/C++ for MVS/ESA works with other products; and, learn how you can leverage these powerful languages to satisfy your business needs across a variety of platforms.

    Mainframe C and C++ For Non-Mainframe Programmers

    C and C++ are known for their portability; however, the underlying operating systems are different and there may be changes to make to your applications to allow them to run on the mainframe. There are also a number of extensions to ANSI standards that could save you time and effort. If you're doing application porting to the mainframe, and want some coaching, please come and see us.

    C/C++

    : How Do I ... ?

    C and C++ have a number of capabilities that you can't always pick up on, except by using them. Since we use these languages and tools every day ourselves, we can show you real code to do what you need to do. There a number of 'tricks' to make your code run faster, as well as minimize maintenance costs.


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

    Question- Demangling

    I would like to know how the mangling/demangling works under the covers. Is there a piece of the compile or the linkage editor which looks and knows from a lookup table or other method which is the next number? Or is it simply a counter held in some place? How does it keep up with the original long name corresponding to the created short one -- is it using a table look-up, dictionary look-up or some other method?

    Answer

    The term 'demangling' refers to two different things.. mapping long names to short ones, and handling multiple instances of a function name, each of which accepts different parameter types.

    Both the compiler and prelinker do the work. The compiler allows a long name to be put into the text deck, and the prelinker does the resolution from long names to short ones. It does this while exposed to all C objects at the same time (eg. if two module components are to be tied together, they must be prelinked together, regardless of the compilation setup or if a long variable name used in two different modules, can only be resolved correctly when those two modules are prelinked together.).

    Under C++, the name demangler (CXXFILT) is available to 'demangle' a 'mangled' name. For more info, see the C++/MVS User's Guide.

    The prelinker numbers the instances of long names and creates a name from that numbering, mapping the long name to the short one. A list of these mappings can be generated with prelinker option 'MAP'.

    Question- Longname mapping

    I want to be able to use a long-name naming convention to map to shorter named C external entry names. Example: External entry name GETDATE -- and I want to use a longer name ie....ACCTINGGETDATERTN to map to or convert to the shorter real entry point name. I thought a new function in C/370 called map pragma's might help them with this.....

    Answer

    When processing longnames, the prelinker will do a highly unsophisticated mapping. It starts with @ST00001, as the first mapping, then adds 1 to the counter for each unique name. It is faster to go sequentially than it is to hash and maintain a collision chain.

    The prelinker has access to all of the information in all text decks that are to be linked together, and can thus make use of this technique. If it didn't have all the information, then hashing would be required.

    Question- Activating Debugging Tool Interface

    How do I activate the debugging tool interface? I included the following lines in my c++ program, run the clist below, but no debugging window appeared. What did I do wrong?

      #pragma options(test)
      #pragma runopts (execops)
    
      CONTROL LIST CONLIST SYMLIST MSG
       ALLOC FI(INSPSAFE) DA(INSPECT.SAVE) REUSE SHR
       ALLOC FI(INSPLOG) DA(INSPECT.LOG) REUSE SHR
       ALLOC FI(INSPPREF) DA(INSPECT.PREFEREN) REUSE SHR
       CALL 'my.load.lib(BIO)' 'TRAP(ON) TEST(ALL)'
       FREE FI(INSPSAFE)
       FREE FI(INSPLOG)
       FREE FI(INSPPREF)
    

    Answer

    #pragma options and #pragma runopts aren't supported under C++.

    Compile with the TEST option and run your program with the TEST option (in your example, you were already running with the TEST option). The #pragma runopts(execops) is not necessary, since EXECOPS is the default. If you want to control execops, use the compile-time option EXECOPS|NOEXECOPS.

    Question- Normal Batch C program in CICS(TM)

    How easy (or is it possible) to take a normal C program (no terminal I/O) and to compile it to CICS using C/370.

    Will it work ?

    Answer

    Basic migration for non-CICS to CICS:

    If your application fits within these boundaries, then you should be able to migrate.

    Question- make and ar utilities

    We were looking into using the 'make' and 'ar' utilities, but the documentation implies that they are part of Open Edition MVS (C++ User's Guide p. 178).

    Is this true? Does this mean we have a compiler without a 'make' utility?

    Answer

    Correct. 'make' and 'ar' are part of OpenEdition, not C/370. No, we do not have any makefile function as part of our current products.

    Question- Portable Class Lib for C++

    I need a collection class library that is available for both MVS and HP-UX. One opinion out of IBM was that the Taligent library from HP would match the collection classes from IBM. An HP/Taligent engineer and I compared class and method semantics briefly and concluded the IBM and Taligent collection classes aren't compatible. Did we miss something? When will the Taligent libraries find their way into MVS? Has anyone attempted to port Rogue Wave's Tools.h++ to MVS?

    Answer

    Taligent libraries are being rolled into IBM Class over time. The rollout is over the course of a couple of years, as IBM Class expands. I have heard from people who have ported all but the template code for tools.h++ already onto C++/MVS. We have been in contact with the folks at Rogue Wave and are working with them in an attempt to label it as 'C++/MVS compatible' (or equivalent).

    Question- Length of data in Storage

    I have to write an application designed to run under several platforms (OS/2, MVS, AIX® and others). Application must provide access to files system (sequential, indexed and relative ) under these platforms, so I wrote a library which handles generic file access, and provides developers some Open functions, like SOPEN to open a sequential, Iopen to open an indexed data set, etc ...

    Ideally to make a write to a file, developers code OWRITE(File descriptor,Record), and do not care about the length of the record. In my libraries, I don't know at run time the length of data in record, record is only a pointer to void. Do you know a function which return the length of data in storage at runtime. The sizeof function of C is only useful on compile time!

    Answer

    I think you're going to have to impose a limitation on your user: either they tell you the size, or put a delimiter on the buffer so that you can pick it off for yourself.

    Question- Performance Tuning

    We are in the process of performance tuning some of our applications which are written in AD/Cycle C/370. We have both batch jobs as well as CICS transactions.

    We have a couple of questions regarding our tuning efforts.

    1. Can you provide any very general "rule of thumb" performance improvements to be gained by using optimize(1) and/or optimize(2) instead of optimize(0). I know that this depends on the application, etc., etc., but what kind of numbers have you or your customers typically seen?

    2. We would also like to inline some of our functions. The trick is trying to determine which ones (and what size to use in the inline() option). Do you have any suggestions? We'd like to see application execution profiles at the function level (number of times called, average/cumulative time in function, etc.).

    3. Finally, many compilers offer control over the various types of optimization that can be performed. It appears that we just have optimize(1) and (2). Is this correct? Are all of the optimizations "safe"? i.e., is there any reason we can't use optimize(2) for everything? (I know that some optimizing compilers perform "unsafe" optimizations. Does yours?)

    Answer

    1. No real 'rule of thumb' here. The optimizer will give the best results if you're a novice coder, setting variables that you don't use, doing a lot of redundant calculations, etc. It gives less improvement if you code based on reuse of previous calculations. Before using the optimizer, you may want to consider the trade-offs in terms of cost of doing business. Don't use OPT(2) until you're finished debugging your logic ... far too expensive. Also, weigh the cost of the OPT(2) compile vs. the execution profile of your application. eg. If you run the program twice a year, and OPT(2) cuts your execution time by 25 percent, but drives compile time up by 300 percent, it may not be worth it to you.
    2. If you have functions that are typically not called, I suggest you DO NOT inline them. The corresponding code growth is not worth it. If you have a function call within a loop, it is certainly a very good candidate for inlining. Basic rule: realize that the inlining of functions will cause the code to be inserted instream, before optimizations are performed. Call overhead is reduced, optimizer can see further, but actual code instructions will typically increase. If you have two calls to the same function within a loop, consider trying to fold them into one call, then inline it. The compiler option INLINE(,REPORT,,) may help you. You may also want to look into using CODE/370 or the debug facility available with C/C++ for MVS/ESA, which both provide frequency analysis.
    3. We do not currently offer control over which optimizations will be performed. The optimization intertwining is so extensive, to allow you to undo some of them would cause our currently SAFE optimizers to become unsafe. Should you ever find a optimization-related bug, you should report it immediately and get a fix.

    Question- fetchable

    Can load modules containing c++ code be fetchable?

    Answer

    Modules with C++ code can't be fetched. Use dllload() followed by dllqryfn()/dllqryvar() instead.

    Question- Code Coverage Tool

    I'd like to know if there is any tool available which allows to measure code coverage for C++ on MVS. Any hints are appreciated.

    Answer

    The debugger shipped with C/C++ for MVS/ESA has commands to get frequency analysis at the function or statement level.

    Question- Batch Debugger

    I need to execute a lot of testcases without user invention. This seems not to be possible if I use a debugger which must be invoked interactively. May I use the debugger in batch jobs?

    Answer

    Yes, the debugger shipped with C/C++ for MVS/ESA supports having a script file being read under MVS Batch, and using that instead of working interactively. You can use the script file under other environments as well. The TEST runtime option lets you specify the file you can read from. For example, the runtime option TEST(ALL,INSPIN,NOPROMPT,INSPPREF) would instruct the debugger to use the DDName INSPIN to get debug commands from, read preferences (like a profile, or config file) from the DDName INSPPREF, and not to prompt you. Also, you would want to allocate the DDName INSPLOG to point to a LRECL 72 RECFM F file - this is where the output of the Debug Tool commands will be written.

    Question- C/370 Environments

    What is the difference between a C/370 Preinitialization Environment and a Persistent C Environment? Which one performs better when a C function is called from an assembler language application?

    Answer

    'Persistent' allows assembler to call C routines, with or without the Library loaded (HOTL or HOTC). These routines must be non-main.

    'Preinit' allows assembler to call C main() functions only, and supports the use of 'RENT' - 'persistent' does not.

    Both interfaces allow you to repeatedly call C code while only setting up and taking down the environment once.

    Under LE/370, 'Preinit' is extended to allow the execution of both main() and non-main() code, plus other features.

    Question- Return statements

    I generally like to have all C/370 modules compiled with the checkout option at some point near the end of the development cycle just to see what's going on. Additionally, some of our "C" code is developed on AIX and its an opportunity to take different look at the code.

    My question is that based on the way the code was written the warning message EDC0833 Implicit return statement encountered is constantly generated. Is there a technical reason, performance or otherwise, why an explicit vs. implicit "return" makes a difference.

    Answer

    'implicit return' happens when we run out of code to execute and fall onto the final right brace of the routine. The situation is flagged because you may or may not have intended to take this sort of exit. "Proper" programming says that you should use exit() or return statement, and not allow your program to run off.

    Question- Dilemma with certain Hex characters

    Was wondering if anyone has run into this and figured out a way around it. I am using C on MVS and am fairly new to C but familiar with MVS.

    Simplified, I am trying to read a file, count the records, and write out what I read. Both the file I am reading and the file I am writing has variable length records (no specific format nor record length). I do know the maximum possible record length. I don't care about the actual values of the data I read. The input can have valid hex data as follows: x'C1C200153CC1C2'

    My problem is figuring out a way to read/write the x'15' (newline character - and probably other control values). I realize I probably can't use text I/O. The part I'm missing is how the MVS file system communicates to C as to what is a "record" in the MVS file (ie the length of that particular record). How many characters are in a single MVS file record.

    Someone I spoke with suggested using the OPEN and READ primitives. The READ might return the number of bytes. However, I don't know what system libraries these might be in and therefore was unsuccessful in linking using these calls.

    The output if not coming out looking like the input.... if I only had a way to tell how many "real" data bytes are in each record....

    I currently have the following code:

      #include <stdio.h>
      #include <stdlib.h>
      #define MAXLEN 100
      FILE *infile;
      FILE *outfile;
      char *filename;
      fldata_t *info;
      unsigned char line[MAXLEN];
      int count;
    
      main()
      {
       if ((infile=fopen("DD:INFILE", "rb")) == NULL)
           printf("Could not open data file-1\n");
       else {
             outfile=fopen("DD.:OUTFILE", "ab,recfm=vb");
             for (i=0; i < 5; i++)
             {
              fgets(line,MAXLEN,infile);
              for (count=0; count < MAXLEN; count++)
              {
               fputc(line[count],outfile);                                                                              }
              }
              fputc('/n',outfile);
             }
            }
          }
    

    Answer

    If I understand you correctly what you want to do is copy the contents of one V(B) file to another VB file in such a way that the content (blanks, control chars etc) is not disrupted at all.

    What you want to do is open the file "type=record". This is a binary mode - so control characters are ignored - with the special feature that it will only read in a maximum of 1 record on a read. type=record requires that you use fread/fwrite to read and write from the file.

    If you are using the Language Environment 1.3 or 1.4 library or the C/370 2.2 library and you know you are not going to be repositioning (ftell, fseek, fgetpos, fsetpos, rewind in a file opened for something other than read ) you should also put "noseek" in second parameter of your fopen call. Noseek may give you a nice little performance boost. Noseek works - with less of an performance impact - on the other currently supported C libraries.

    To change your code

    #include <stdio.h>
    #include <stdlib.h>
    #define MAXLEN 100
    FILE *infile;
    FILE *outfile;
    char *filename;
    fldata_t *info;
    unsigned char line[MAXLEN];
    int count,reclen;
    
    main()
    {
    if ((infile=fopen("DD:INFILE","rb,type=record,noseek"))==NULL)
     printf("Could not open data file-1\n");
     else {
       outfile=fopen("DD.:OUTFILE","ab,recfm=vb,type=record,noseek");
       for (i=0; i < 5; i++)
        {
         reclen=fread(line,sizeof(char),MAXLEN,infile);
         fwrite(line,sizeof(char),MAXLEN,outfile);
         }
       }
    
     

    And, if you wanted to get fancier, you could use fldata() on the file you open to check all the info about the file, such as record length, record format, etc.. That way, you can dynamically create a buffer to hold a record and (probably more importantly) you can check to make sure the DDname that was provided as input is what you expected (e.g. VB).

    Question- Specifying high level qualifier in batch

    I have a portable C program that runs on DOS, VM, and MVS. The program opens several files. An example of code that opens a file is fopen("router.ini", "r"). On DOS, the program will open the file ROUTER.INI. On VM, the program will open the ROUTER INI *. In TSO, the program opens the file "HLQ.ROUTER.INI" where HLQ is the high level qualifier. When I run the program on MVS in batch, my program doesn't successfully open the file. Apparently, the high level qualifier used is not the same as the file. I was wondering if there was a way to specify the high level qualifier to use for all files that are reference by my C program in batch. Thanks!

    Answer

    First, there are two ways to set up your header files in a reasonably portable manner:

    You may also want to put single quotes around the name of the file you want to fopen(), eg. fopen("'TS36133.ROUTER.INI'", "r") or use the TSO command PROFILE PREFIX(xxx) where xxx is the new high level qualifier.

    Question- ASCII to EDBCDIC and Viceversa

    What are the standard functions used for this purpose? I'm doing some hand translations to do it but I'd rather use the service, and it's not clear to me how to find it from the C370 Programming Guide. Thanks for any help!!!

    Answer

    Use the standard iconv() function to convert from one codepage to another.

    Question- Migrating from C/370 2.1 to Language Environment/370

    I've got a C/370 program that calls a couple of PL/1 routines. It worked fine under C/370 2.1. Now I'm trying to see if it will work with LE/370 C, so I recompiled and relinked it. I needed to specify the PL/1 linktime libraries because of the included PL/1 code, even though there's no support for a common C and PL/1 environment - but I figured that it should work anyway.

    Anyhow, any attempt to run the program fails because it can't find module CEEEV010. I use the LE/370 C runtime library when I run it.

    What do I need to do?

    Answer

    The entire application needs to be linked with LE, and nothing else. eg. DO NOT include the old PL/I stubs, or the common library stubs, just use SCEELKED (from LE) and you should be okay.

    CEEEV010 is part of LE/370 - one of the PL/I parts.

    Question- Using TCP/IP #includes

    I have a C program that has an #include for a member from the TCP/IP PDS (we have TCP/IP 2.2.1). These #include members have numbers in columns 73-80 which isn't very nice. All our other #include libraries are VB files that could have data well beyond column 72. The end result is that the compiler does not like the numbers in those columns.

    Short of editing the TCP/IP and removing data from all those columns , do I have any options?

    Answer

    Any include file (be it source or header) should have a '#pragma sequence(m,n)' if it has sequence numbers, or '#pragma nosequence' if it doesn't have them.

    The pragma is only valid for the current file (base compile or include file) and does not propagate to other files. The compile option applies to all files, unless specifically overridden in a specific file.

    If your own code had said '#pragma nosequence' in all files, and you compiled with SEQ(73,80), then it would work, but that is changing everything backwards. All you can do is edit the TCP headers. Best bet is to insert the '#pragma sequence(73,80)' at the top and not touch the data lines below.

    Question- C/370 CICS and HEAP Storage

    I am about to put a C/370 CICS application in production, however I have discovered a major performance problem. The application does an enormous number of CICS Getmain/Freemains for HEAP/Stack storage, and apparently the HEAP parameter is ignored in a CICS environment. Do you know if the HEAP parameter is supported if I move to LE/370 Runtime?

    Answer

    HEAP is honoured under LE/370, but not under the non-LE base (C/370 V2).

    HEAP is used for static storage + malloc/calloc/realloc. STACK is used for everything else (including automatic storage). As we issue some *alloc routines from within the library, some of these calls are beyond programmer control. If the source of the problem is truly the HEAP allocations, (see REPORT option), the programmer can investigate their own use of the *alloc routines and attempt to combine storage requests (eg. do some of the storage management themselves). If the source of the problem is STACK, some adjustment of the STACK option may be required. Additionally, the EDSA parm at CICS startup may need to be adjusted.

    Question- C and LE Support for String Instructions

    As you know, some 390 mainframes have support for the string assembler instructions (move string, search string) and others don't. Do the C and LE runtime libraries "know" about these instructions and use them if they are present? If not, is there some way to implement them (without us writing our own functions)? We could really use any performance boost they may offer.

    We are using AD/Cycle C/370 V1R2 and LE/370 V1R3. MVS/ESA is 4.3.

    Answer

    Logical String Assist (LSA) is available via the compiler options:

       HWOPTS(STRING) to enable the lsa opcodes
       HWOPTS(NOSTRING) to disable
    

    Default is NOHWOPTS. STRING is a suboption in case other hardware options come up in the future.

    Question- Replicating Initializers

    I want to declare an array of structures with initial values, and I was wondering whether there is any way of indicating a "repetition factor", to avoid repeated coding of the initial values. Other languages have this, but I could not find any mention of it in the C manual. This is a simplified example of what I would like to do:

     typedef struct {
       long  NumEntries;
       long  NumDone;
      } HEADER;
     #define HEADER_DEFAULT 0,0
    
     typedef struct {
       char  Name[8];
       long  Index;
      } ENTRY;
     #define ENTRY_DEFAULT {""},0
    
     /* static structure with 100 entries */
     #define MAX_SIZE 100
     struct {
       HEADER Header;
       ENTRY  Entries MAX_SIZE ;
     } Table={ {HEADER_DEFAULT}, { (MAX_SIZE) {ENTRY_DEFAULT}} };
    

    Here I have used MAX_SIZE as a repetition factor (based on what is valid in other languages), but that is not valid C. Is there a way to do what I want?

    Answer

    There is no way to indicate a repetition under C unless you want to initialize to 0. If you had a global array:

       int x[10000];
    
    that you wanted to initialize to 0's, then you could write:
        int x[10000] = {0};
    
    the compiler will generate code to ensure that the last 9999 full words are initialized to 0. If you wanted to initialize the array to anything else, you're stuck. What is typically done in C is that you would initialize the variable early in your main() program using the memset() function, e.g.
      main() {
        memset(x, 98, sizeof(x));
        .
        .
        .
        return(0);
      }
    

    There is another approach - I don't recommend it though because it might have other affects, but you could use the LE/370 STORAGE runtime option, which will initialize all your storage to a particular value when it is allocated (you can choose different values for HEAP and STACK storage). This will cause your code to crawl and is really intended for debugging purposes.

    Question- @@DC370$

    What is the @@DC370$ member used for? Does the Prelinker or the Linker use it?

    Answer

    The @@DC370$ member is created and maintained by the C370LIB utility. The information in this member is used by the prelinker to do AUTOCALL with this library - it contains information about where external variables reside in an object library. If you don't use the PDS for autocall, you don't need this member. To update the member, use the C370LIB utility with the DIR option - to see what's in your library, use the MAP option.

    The linker has no understanding of the @@DC370$ member - AUTOCALL libraries that are used for prelinking cannot be used by the linker, since the linker doesn't understand the internal format of the object library.

    Question- OS Linkage and variable number of arguments

    We have an existing program which accepts variable length parameter lists using "standard" assembler conventions. We are writing C code to invoke that program. The C/370 User's Guide and the C/370 Programming Guide are not clear in their description of "#pragma linkage(routine,OS)".

    How do we specify a variable length parameter string in the prototype? Do we say "int routine {...};" ? Does linkage OS set the high order bit to '1'b in the last argument? Do we have to use the va_ functions in stdarg.h?

    We can't change the existing program to use EDCPRLG and EDCEPIL. Does linkage OS use "standard" assembler program save area linkage?

    My existing program returns values to locations specified as parameters. Do we pass the addresses, or does linkage OS build this for us? In other words, do we say "routine(&arg1,&arg2,&arg3,&rc,&reas);" or do we say "routine(arg1,arg2,arg3,rc,reas);"?

    Answer

    If you want to call a function with OS linkage, you can code the following:

        int FN(int x, int y, int z);
        #pragma linkage(FN, OS)
    

    When you call the function, the last parameter will have the high order bit set on to indicate it is the last parameter. We use standard save area linkage, I believe, so you should be ok.

    You can actually pass variables by reference or by value for OS linkage - they both amount to the same thing. We recommend that you pass variables by address; we find it makes things simpler. If you pass variables by value, the compiler will create a temporary variable to copy the variable into, then take the address of that, so you'll be getting worse code if you pass variables by address. For example, if you have an OS linkage function that will be getting an integer by reference, you could code either:

        int FN(int x);
        #pragma linkage(FN, OS)
           .
           .
           .
        int x = 7;
        y = FN(x);
    
    or you could code:
        int FN(int* x);
        #pragma linkage(FN, OS)
           .
           .
           .
        int x = 7;
        y = FN(&x);
    
    both would work, but the first style would cause the compiler to (in essence) generate:
        int x = 7;
        int* temp = &x;
        y = FN(temp);
    
    to maintain call-by-value semantics.

    Question- Problems using fldata

    I'm having a problem using fldata. I execute the following program, passing the argument 'SYS1.CLIST'.

     #include <stdio.h>
     fldata_t filedata;
     FILE *infile;
     int i;
     main(int argc, char *arg&lrbk.])
     {
     for (i=2; i<argc; i++) *(arg[i]-1) = ' ';
     infile = fopen(arg[i],"r");
     if (!infile) exit(1);
     fldata(infile, NULL, &filedata);
     printf("FILEDATA.__DSNAME    = <%s>\n",filedata.__dsname);
     printf("FILEDATA.__RECFMF    = <%i>\n",filedata.__recfmF);
     printf("FILEDATA.__RECFMV    = <%i>\n",filedata.__recfmV);
     printf("FILEDATA.__RECFMBlk  = <%i>\n",filedata.__recfmBlk);
     printf("FILEDATA.__DSORGPO   = <%i>\n",filedata.__dsorgPO);
     printf("FILEDATA.__DSORGPS   = <%i>\n",filedata.__dsorgPS);
     printf("FILEDATA.__MAXRECLEN = <%i>\n",filedata.__maxreclen);
     printf("FILEDATA.__BLKSIZE   = <%i>\n",filedata.__blksize);
     fclose(finfile);
     }
    

    And I get the following results

      FILEDATA.__DSNAME    = <SYS1.CLIST>
      FILEDATA.__RECFMF    = <0>
      FILEDATA.__RECFMV    = <0>   Should be 1 (Variable Records)
      FILEDATA.__RECFMBlk  = <0>   Should be 1 (Blocked)
      FILEDATA.__DSORGPO   = <1>
      FILEDATA.__DSORGPS   = <0>
      FILEDATA.__MAXRECLEN = <256> Should be 255
      FILEDATA.__BLKSIZE   = <256> Should be 3665
    

    Answer

    These results are correct. When using fldata with a Partitioned Data Set name with no member specified, fldata returns the values specified for the directory of the PDS which does not have variable blocked records, but has a record length of 256 and a blocksize of 256.

    Question- Compilation Problem

    I'm having a problem using stat.h on CMS. When I compile the following program I get the message that "The operation cannot be performed on an incomplete struct or union". It works with other other C Compilers. Why doesn't this program compile under CMS?

      #include <stdio.h>
      #include <stat.h>
      main(int argc, char *arg&lrbk.])
      {
      printf("SIZE OF STRUCT is%i>\n",sizeof(struct stat));
      }
    

    Answer

    The stat.h is not part of the ANSI standard. The compiler gives you the message because there is a stat.h file with the compiler for use by MVS OpenEdition applications. However, the preprocessor on VM makes the struct stat incomplete, hence no operation can be preformed.


    Missed our Previous Editions?

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


    A word from your editor

    If you haven't already sent for your free subscription to this newsletter, now is the time to mail it in. So far, we have almost been able to keep to our plan of four issues a year. If you prefer, mail or fax your business card to the address/phone number on the Reader's Comment Form. You can also subscribe to this newsletter via INTERNET. Just 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).

    For the many of you sending in the reply forms with your comments, please include your phone number as we may need to call you to discuss your comments further. Please keep your cards, letters, faxes and messages coming so we can continue to provide this service and improve the newsletter. We really thank the many of you who have already sent your comments and subscriptions in.


    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 1995. In Canada - © Copyright IBM Canada Ltd. 1995. +---------------------------------------------------------------------+


    C/370 Compiler News Volume 3 Number 3 July/95 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 3 Number 3 July/95 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 (TM) or ® are trademarks or registered trademarks of the International Business Machines Corporation, unless otherwise indicated. Company, product or service marked ** may be a trademark or service mark of others.