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!
|