Skip to main content

     
  TPF : Library : TPF Newsletters
  Products   >   Software   >   Transaction Systems   >   TPF   >   Library   >   TPF System Newsletters   >  
 

Letters, We Get Letters 10

Bob Kopac, Sarat Vemuri, and P.J. Darcy, IBM TPF Development

This is the 10th article dealing with C and C++ issues. These customer queries usually do not result in PMRs or APARs. Instead, an explanation often clears up customer problems. Sometimes we receive the same question from different customers. Because some of these questions and answers may be of interest to many of you, we decided to write an article based on some of the questions and answers. You might even see one of your questions here! We also include TPF development C and C++ problems so that you can learn from our mistakes. Just don't tell our manager that we make mistakes!

Question: What is the correct way to code to compare a bit field? For example, I have the following definition:

struct 
   {
                             /* indicators field 1 */
       int isdd_ent_1eot :1; /* end of table indicator */
       int isdd_ent_selt :1; /* selective tracing active */
       int               :6; /* spares */
    }  isdd_ent_ind1;

I want to test one of the bits; for example:

if (sdd_ent_ptr->isdd_ent_ind1.isdd_ent_1eot == 1)

Is this correct?

Answer: No, it is incorrect. Remember that every bit counts. A 1-bit field needs to be defined as an unsigned int type. The minimum width of a signed int bit field is 2 bits. You should change your structure to use the unsigned int type instead of the int type.

 

Question: Why does my compiler listing show all C++ statements grouped together followed by all compiler-generated assembler instructions? Usually I see the C++ statements interleaved with the compiler-generated assembler instructions.

Answer: You used the NOTEST(NOHOOK) compiler option. This option groups the C++ statements together. Coding NOTEST(HOOK) makes it easier to match the C++ statements with the assembler code; for example:

                     00165 | *  sipcc_parms.sipcc_da2_len = 0;
 0002F6  5060  D0F0  00165 |        ST    r6,<a1:d240:l4>(,r13,240)
                     00166 | *  sipcc_parms.sipcc_priority = SIPCC_PRIORITY_YES;
 0002FA  50E0  D0F8  00166 |        ST    r14,<a1:d248:l4>(,r13,248)

Question: I have a question about double-byte character set (DBCS) support in TPF. Are wide character support and multibyte character support the same as DBCS?

Answer: Yes, they are. The TPF 4.1 Glossary has the following definitions:
 
DBCS Double-byte character set.
SBCS Single-byte character set.
multibyte characters A mixture of SBCS and DBCS characters.

TPF 4.1 APAR PJ21337 implemented the OS/390 DBCS double-byte wide character implementation and the OS/390 multibyte implementation. APAR PJ21337 does not support any other non-OS/390 wide character implementation. Note that there are other non-IBM wide character representations that may have different values for a character and may represent the character using more than 2 bytes.

For reading or writing multibyte characters, APAR PJ21337 updated the existing ISO-C printf() and scanf() functions to support new format characters for multibyte characters.

APAR PJ21337 added new functions on TPF to manipulate multibyte and wide character data; for example:

function Description
swprintf() Write wide characters to a wide-character array.
swscanf() Read a wide-character string.
wcstombs() Convert a wide-character string to a multibyte string.
mbstowcs() Convert a multibyte string to a wide-character string.

Appendix D ("C/C++ Functions Supported by the TPF 4.1 System but Not Documented") in the TPF C/C++ Language Support User's Guide lists these new functions, which are documented in the OS/390 C/C++ Run-Time Library Reference document. Following is a link:

http://www.ibm.com/software/awdtools/c390/cmvsdocs.htm

See "APAR PJ21337 — A Wide Character Is Not Godzilla" in the Second Quarter 2000 issue of the TPF Systems Technical Newsletter to help you gain a better understanding of the difference between wide characters (DBCS) and multibyte characters.

Note: You should use multibyte formatting, especially when writing data to DASD. You may use wide characters for processing strings in core.

Note: Do not write wide character data to DASD.

 

Question: Is it possible for C++ to "ENTRC" to an E-type TPF assembler program?

Answer: Yes, it is possible. You need to create a function prototype for the call to the assembler segment and surround the prototype with extern "C" { } . This makes the call interface a C function interface rather than a C++ function interface. The function name is then resolved with a DLM stub, which provides linkage to enter/back services.

Following is an example of a C++ program (qzz3) calling an assembler segment (QZZ1):

    #include <stdlib.h>
    #include <stdio.h>
    #include <tpfregs.h>

    extern "C" { void QZZ1 (struct TPF_regs *); } /* function prototype 
                                                  for assembler segment */
    void qzz3 (void)
    {
    struct TPF_regs * regs;
    QZZ1(regs);
    exit(0);  
    }

Follow-Up Question: Could you explain further why I have to code extern "C" ?

Answer: The interface to the assembler program uses C linkage and not C++ linkage; for C linkage, register 1 points to a parameter list. The interface does not look like a C++ interface. That is why you have to wrap the prototype with the extern "C" wrapper. See "Grandson of Letters, We Get Letters" in the Second Quarter 2001 TPF Systems Technical Newsletter for a question and answer about extern "C". The article includes a paragraph that explains the difference between the C interface and the C++ interface.

Follow-Up Question: Can an assembler program call a DLL?

Answer: No.

Follow-Up Question: Can you CRETC to a DLL?

Answer: No.

Follow-Up Question: Is it possible to do a CROSC in assembler and pass parameters to a C DLM? If I set register 1 to point to my parameter list and then do a CROSC, will my parameters show up in the DLM arguments as they do on an ENTRC?

Answer: Yes, it can be done that way for a C program. It would not work for a C++ program unless the C++ prototype is wrapped in an extern "C" wrapper so that the parameters passed and received are in a C format, not a C++ format.

 

Question: Why am I receiving compiler error message CBC1055(S)? Following is an example of the error message.

    imqt_ent_struct* curr_mqt_ptr;   
    ...
    curr_mqt_ptr = malloc (MQIT_MAX_ENTRIES * IMQT_ENT_LEN);
    CBC1055(S) "void*" cannot be converted to "imqt_ent_struct*"

Answer: The stdlib.h header contains the following prototype for malloc:

    void * malloc (size_t);

That is, malloc() returns a pointer to void. The compiler expects the lvalue to be a pointer to void type. You coded curr_mqt_ptr to be a pointer to imqt_ent_struct and not as a pointer to void. The compiler detects that a pointer to void is not the same as a pointer to imqt_ent_struct and indicates this with the CBC1055(S) message. This error message may seem annoying, but remember that programmers sometimes erroneously code a pointer to the wrong structure, and this compiler error message finds those problems.

You should do one of the following.

  • Code the lvalue (the value to the left of the malloc() statement) to be a pointer to void.
  • Cast the return value of the function to be a pointer as follows:
    curr_mqt_ptr =
          (imqt_ent_struct*) malloc (MQIT_MAX_ENTRIES * IMQT_ENT_LEN);

Optionally, compiling with the NOANSIALIAS compiler option may get rid of the problem.

Note: You should always compile TPF-provided code using the NOANSIALIAS compiler option. The ANSIALIAS/NOANSIALIAS compiler option default is ANSIALIAS. This default assumes that pointers access objects of the same type; that is, the pointers are ANSI compliant. Casting a pointer to point to a different object is a common C practice, but it is not ANSI compliant. If your code casts a pointer to point to a different object, you must then compile your code using the NOANSIALIAS compiler option. See "Letters, We Get Letters . . . Usually C and C++" in the Fourth Quarter 2000 TPF Systems Technical Newsletter for additional questions and answers about NOANSIALIAS.

 

Question: Why are QCON references relocated to the static block for each ECB? Function calls within the same .cpp file are resolved with an ADCON. Function calls across .cpp files in the same DLM or DLL (not calls to exported functions) are resolved with a QCON instead of a VCON. Merging two .cpp files into one .cpp file eliminates this overhead. I wonder why these are not resolved with a VCON?

Answer: While compiling a .cpp source unit, the compiler does not know the difference between calls to functions in a different source unit versus functions in a different module. For C++ or DLL applications, calls to any external function are via DLL linkage, which uses the descriptor that resides in static storage. Thus, for external function calls (external to the source unit being compiled), the compiler generates a QCON reference.

 

Question: If I have a DLM that does not have a main() function but has a 4-character TPF entry point function, and the function uses static, will its static block be preserved between subsequent calls to that DLM, or will its static block be reallocated and reinitialized on every call to that DLM?

Answer: The static block is reused for the DLM for the life of the ECB. Each ECB gets its own static block for that DLM. Thus, static blocks exist for the life of the ECB and exist across multiple invocations.

The static frame is used for each C DLM that uses reentrant static storage. A Hash table is used to locate the static frame associated with each C program. Static storage frames are allocated and initialized using descriptors produced by the compiler. Extern variables are the same as static variables with the exception that extern variables can be accessed by all functions within a DLM.

The rules for function, file, and module scope are as follows: Every definition that has a static qualifier becomes file scope. If that definition (with a static qualifier) is within a function, it becomes function scope. Any definition that is outside of a function without the static qualifier becomes externalized to module scope.

 

Question: Are those all the questions you received from customers and TPF developers?

Answer: No, there have been many questions. We have enough material for several newsletter articles. We hope that is a good thing!