TPF Systems Technical Newsletter, Vol. 4 No.4
by Bob Kopac, IBM TPF Development
APAR PJ25084 provides support for dynamic link libraries (DLLs)
and support for TPF application programs written in C++ programming
language.
QUESTION: I have been working on an assembler program for
the past 30 years and it almost assembles. I am ready to try some
thing new. What is C++?
ANSWER: C++ is a general-purpose, high-level programming
language designed to take advantage of object-oriented (OO) program
ming concepts. Except for minor details, C++ support is a superset
of C language. In addition to the facilities provided by C
language, C++ support provides flexible and effective facilities
for defining new types. You can partition an application into
manageable pieces by defining new types that closely match the
logical design of the application. When used well, these techniques
result in shorter, easier to understand, and easier to maintain
programs. A side benefit of an object-oriented programming language
is that you can say "OOPS!" when you make a programming error.
QUESTION: What is a DLL? Does it have anything to do with
pickles?
ANSWER: No, a DLL has nothing to do with dill pickles,
but a DLL may help you when you find yourself in a pickle. A
dynamic link library (DLL) is a collection of one or more functions
gathered in a load module and executable or accessible from a
separate application load module. The key concept in DLLs is that
functions or variables can be executed or referenced dynamically
while the application is running rather than statically when the
application is built. You can, therefore, call a function or use a
variable in a load module other than the one that contains the
definition. This allows you greater flexibility in accessing
library functions or variables when you find yourself in a pickle.
By the way, "find yourself in a pickle" is an American slang
expression that creates an interesting picture if you take the
saying literally!
QUESTION: How can a DLL execute or reference functions or
variables dynamically? Do not side-step this issue!
ANSWER: A DLL does this amazing feat, not by
side-stepping, but by using a definition side-deck. A definition
side-deck is a directive file that contains an IMPORT control
statement for each function and variable exported by the DLL.
When you build a DLL, a definition side-deck is automatically
created and written to the ACP.IMPORTS.RELvv SYSDEFSD definition
side-deck data set. You must include the ACP.IMPORTS.RELvv SYSDEFSD
definition side-deck data set when you prelink a DLL application
that imports any functions or variables from a DLL. The C load mod
ule build tool (CBLD) includes this data set automatically for you
when you code the DLL keyword in the load module build script.
When you link-edit a DLL or DLL application, in addition to
specifying the included object files, you must also specify the
definition side-deck inputs if the DLL or DLL application imports
from other DLLs. The linkage editor resolves external functions in
the following search order:
1. Those functions explicitly included in text decks
(this is from the original list of included object files in a load
modul e build script).
2. Those functions in included EXPORT data set
members.
3. Those functions for which static stubs exist in stub
data sets concatenated under the SYSLIB data set. These stubs are
cr eated with the library interface tool (LIBI) and the DLM
stub
Using sample load module FOOG, the following is an example of
code that exports functions and variables and its resulting
definition side-deck:
#pragma export(foo)
#pragma export(goo)
int foo()
{
...
}
int goo()
{
...
}
int keep_it_hidden()
{
...
}
...
#pragma export (hooVar)
#pragma export (gooVar)
int hooVar;
int gooVar;
int keep_it_hidden_variable;
Definition Side-Deck produced:
IMPORT CODE 'FOOG' foo
IMPORT CODE 'FOOG' goo
IMPORT DATA 'FOOG' hooVar
IMPORT DATA 'FOOG' gooVar
QUESTION: Can you be more explicit about DLLs?
ANSWER: You can use DLLs both implicitly and explicitly.
When an application calls an imported function or references an im
ported variable, the DLL is implicitly loaded. This is referred to
as load-on-call. For an explicit call, the application uses
explicit source-level calls to one or more run-time services to
connect the reference to the definition. These connections are made
at run time.
QUESTION: Do I have to go to a public library to use
DLLs?
ANSWER: The CISO run-time library has been updated with
several application programming interfaces (APIs) that allow a load
module to explicitly call the following run-time services:
- dllload, which loads the DLL and connects it to the
application
- dllqueryfn, which obtains a pointer to a DLL function
- dllqueryvar, which obtains a pointer to a DLL variable
- dllfree, which frees a DLL loaded with dllload.
See theTPF C/C++ Language Support User's Guide for more
information about these services.
QUESTION: What is the difference between a DLL and a
DLM?
ANSWER: The letter M.
QUESTION: There have got to be more differences than
that!
ANSWER: You are correct. The concept of dynamic load
module (DLM) was first introduced by APAR PJ17852 (ISO-C Support).
A DLM contains one or more functions and has a single entry point
that is called with TPF enter/back services. Functions or variables
in DLLs can be called or referenced only through DLL linkage. The
DLL cannot be called with TPF enter/back services. A function in a
DLL may import or export functions or variables.
QUESTION: So a DLM cannot import functions or
variables?
ANSWER: Well, yes. A DLM can import functions or
variables, but only if it has an import license. If the DLM is
written in C language, you must specify the DLL compiler option. If
the DLM is written in C++ language, the C++ compiler automatically
compiles the source code as a DLL. We call a DLM that can import
functions or variables a DLL application.
QUESTION: Can you give a summary of functions and
variables?
ANSWER: * Imported functions and variables are those that
are not defined in the load module where a reference to them is
made, but are defined in a referenced DLL.
* Nonimported functions are those that do not use DLL linkage.
Nonimported variables are those that are defined in the same load
mod ule where a reference to them is made.
* Exported functions and variables are defined in one load
module and can be referenced from another load module.
QUESTION: So I have to learn new terminology, such as DLL
application, DLL, definition side-deck, import, and export?
ANSWER: Yes, terminology helps programmers confuse
nonprogrammers. SeeTPF Application Programming for program
chara cteristics and attributes of DLMs, DLL applications, DLLs,
and more information about terminology.
QUESTION: The compiler I am using is 30 years old. How do
I know if my compiler supports DLLs?
ANSWER: To use DLLs, you must use one of the following
compilers:
* IBM C/C++ for MVS/ESA Version 3 Release 2 * IBM OS/390 C/C++
Version 1 Release 2 or later version.
Notes:
*There is no VM compiler that supports the DLL compiler
option.
*There is no RENT compiler option in the family of IBM C++
compilers on the System/390 platform. Source code written in C++
and compi led with a C++ compiler will automatically be compiled as
RENT. See theTPF Migration Guide for more information about
C/C++ compilers.
QUESTION: Can you give me a description of how a DLL
application imports functions or variables?
ANSWER: Let me give you a description of descriptors. A
DLL application imports using function descriptors and variable des
criptors.
A function descriptor is an internal control block that contains
the function address and its associated writable static area (WSA).
In the TPF system, a function descriptor can be thought of as a
dynamic linkage call stub in contrast to the static linkage call
(STUB) and the library interface tool (LIBI) before link-edit
time.
A variable descriptor is an internal control block that contains
the variable address. This control block is a dynamic linkage call
stub.
QUESTION: I crave more information. What else can you
tell me about function descriptors?
ANSWER: DLL applications written in C are compiled with
the DLL compiler option. For this case, function pointers are point
ers to corresponding function descriptors. Therefore, a function
pointer that is passed from code compiled without the DLL option to
a function in code compiled with the DLL option will not work
because the DLL code always expects a function descriptor pointer.
Code compiled with the DLL option can still pass a function pointer
to code compiled without the DLL option. The special code at the
beginning of the function descriptor handles these situations. See
the user's guide for the IBM C/C++ compiler on the System/390
platform used by your installation for more information about the
DLL compiler option and function descriptors.
Note: There is no DLL compiler option for source code written in
C++. It is automatically compiled as a DLL application.
QUESTION: Because of the balance of payments, I would
rather export than import. How do I export?
ANSWER: The following are ways to export functions and
variables:
* The EXPORTALL compiler option allows a DLL to export all
external functions and variables. This also means that no functions
or va riables can be hidden; everything in the DLL is accessible to
other DLLs and DLL applications. If you use EXPORTALL, you do not
need to include the #pragma export directive.
* The #pragma export directive permits you to control specific
functions that can be exported when you can code this directive in
your source file.
* Using the EXPORT C++ language extension keyword permits you to
declare that the function or variable is to be exported.
QUESTION: Okay, what's the catch?
ANSWER: Well, there is no catch support. This APAR does
not provide exception handling support for the following
keywords:< p>
* try
* catch
* throw.
Also, the TPF lab currently does not ship C++ class libraries.
You can, however, create and use your own class libraries.
QUESTION: Can C++ and C programs talk to each other?
ANSWER: Similar to people, C++ and C programs can talk to
each other only if they are speaking the same language. The param
eter list structure and linkage are different between C and C++.
Therefore, functions coded in C and compiled with a C compiler
cannot call functions written in C++ unless the C++ function is
declared to have C-type linkage. DLL applications are required to
have either main or an entry point with the same name as the load
module. If your DLL application does not have main, C++ mangles the
function name. You must code an extern "C" linkage specification to
produce an entry point with the same 4-character uppercase name as
the load module. The extern "C" linkage specification allows a C++
application entry point to be called through TPF enter/back
services. This linkage specification also forces the linkage to the
entry point of the load module to be C linkage instead of C++
linkage. Only the entry point function must have the extern "C"
linkage specification. Other functions in a C++ application do not
need this linkage specification. A function in the C++ applicat ion
that is called by another function in the same load module can have
C++ linkage. If you do not code the extern "C" linkage
specification, the offline loader (TPFLDR) will provide an error
message that the entry point is not found in the program.
In the following example, the extern "C" linkage specification
produces a entry point with the same name as the QZZ0 load module
name. The call to ReadIt in EmpClass does not require this linkage
specification.
class EmpClass
{
...
}
extern "C" void QZZ0 ();
{
double raise;
EmpClass *EmpPtr = new EmpClass<total_employees>;
...
raise = EmpPtr<i>.ReadIt(raise);
...
}
double & EmpClass::ReadIt (double & rate)
{
...
...
}
Also, see APAR PJ24541 for additional C header changes needed to
support C++.
QUESTION: Can you show me examples of build scripts?
ANSWER: If an @IMPORTDS statement is in the build script,
CBLD will include data sets in the output JCL file for definition
side-decks.
The following is an example of a build script for a DLL.
######################################################################
#
# SCRIPT NAME..... QZZ2BS
#
.
.
.
#
######################################################################
DLL QZZ2RX # Include startup code for DLL
@IMPORTDS CPP140 # Include a definition side-deck @IMPORTDS
DLL3RK # Include a definition side-deck
#Object File Function Source Language
#----------- -------- ---------------
QZZ2A41 # QZZ2A function C++
QZZ2B41 # QZZ2B function C
Note: With the DLL keyword, CSTDLL startup code is used. The
@IMPORTDS statements indicate the definition side-decks from which
this DLL imports. @IMPORTDS statements must follow the DLL
statement and precede the list of object files to be included.
The following is an example of a build script for a DLM that is
a DLL application.
######################################################################
#
# SCRIPT NAME..... QZZ1BS
#
.
.
.
#
######################################################################
DLM QZZ1RX # Include startup code for DLM (DLL application)
@IMPORTDS QCCC41 # Include a definition side-deck @IMPORTDS
QDDDRX # Include a definition side-deck
#Object File Function Source Language
#----------- -------- --------------
QZZ1A41 # QZZ1A function C
QZZ1B41 # QZZ1B function C++
Note: DLL applications use the DLM keyword in the build script.
CSTRTD startup code is used. The @IMPORTDS statements indicate the
definition side-decks this DLL application (DLM) imports from.
@IMPORTDS statements must follow the DLM statement and precede the
list of object files to be included.
Both the previous DLL and DLL application build script examples
show the use of the @IMPORTDS keyword. CBLD takes the load module
name that follows this keyword and adds a definition side-deck
INCLUDE statement to the list of INCLUDE statements in the JCL
output deck. For a DLL only, the JCL will also contain a definition
side-deck SYSDEFSD data set, ACP.IMPORTS.RELxx , for exported
functions and variables. The following examples show the different
JCL output decks generated by CBLD for a DLL and a DLL
application:
* For a DLL:
//$QZZ2BS0 JOB ...
//PRELINK EXEC EDCPL,COND.LKED=(0,NE),
// LPARM='AMODE=31,RMODE=ANY,LIST,XREF,MAP',
// PPARM='DLLNAME(QZZ2)'
//PLKED.SYSLIB DD DSN=ACP.CLIB.RELxx,DISP=OLD
// DD DSN=ACP.STUB.RELxx,DISP=OLD
//PLKED.OBJLIB DD DSN=ACP.OBJ.RELxx,DISP=SHR
//*
//PLKED.SYSDEFSD DD DISP=SHR,DSN=ACP.IMPORTS.RELxx(QZZ2RX)
//*
//PLKED.DSD DD DISP=SHR,DSN=ACP.IMPORTS.RELxx
//PLKED.SYSIN DD *
INCLUDE OBJLIB(CSTDLL40)
INCLUDE DSD(DLL240)
INCLUDE DSD(DLL3RK)
ESD @@LM0001
TXT QZZ2A 41
END 1569623400 010195215
INCLUDE OBJLIB(QZZ2A41)
ESD @@LM0002
TXT QZZ2B 41
END 1569623400 010195215
INCLUDE OBJLIB(QZZ2B41)
ESD @@LM0003
TXT END_OF_LAST_OBJ
END 1569623400 010195215
/*
//LKED.SYSLMOD DD DISP=OLD,DSN=ACP.LK.RELxx(QZZ2RX)
//
* For a DLL application:
//$QZZ1BS5 JOB ...
//PRELINK EXEC EDCPL,COND.LKED=(0,NE),
// LPARM='AMODE=31,RMODE=ANY,LIST,XREF'
//PLKED.SYSLIB DD DSN=ACP.CLIB.RELxx,DISP=OLD
// DD DSN=ACP.STUB.RELxx,DISP=OLD
//PLKED.OBJLIB DD DSN=ACP.OBJ.RELxx,DISP=SHR
//PLKED.DSD DD DISP=SHR,DSN=ACP.IMPORTS.RELxx
//PLKED.SYSIN DD *
INCLUDE OBJLIB(CSTRTD40)
INCLUDE DSD(QCCC41)
INCLUDE DSD(QDDDRX)
ESD @@LM0001
TXT QZZ1A 41
END 1569623400 010195215
INCLUDE OBJLIB(QZZ1A41)
ESD @@LM0002
TXT QZZ1B 41
END 1569623400 010195215
INCLUDE OBJLIB(QZZ1B41)
ESD @@LM0003
TXT END_OF_LAST_OBJ
END 1569623400 010195215
/*
//LKED.SYSLMOD DD DISP=OLD,DSN=ACP.LK.RELxx(QZZ1RX)
//
See the programming guide for the IBM C/C++ compiler used by
your installation for more information about DLLs and definition
side-decks. SeeTPF System Generation for more information
about the ACP.IMPORTS.RELvv definition side-deck data set.
QUESTION: In summary, what is the best benefit of
C++?
ANSWER: Being able to say "OOPS!" when you make a
programming mistake.
|