dos_compilers/Zortech C++ v30r1/README/README.ZTC
2024-07-02 08:01:21 -07:00

813 lines
30 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

ZORTECH C++ COMPILER
This is version 3.0.
Zortech has a policy of continuous improvement to our products. This
means that changes will have been made since completion of the documentation.
Therefore, please examine the readme files closely.
-----------------------------------
Version : V3.0r1 Release
Date : July 5, 1991
-----------------------------------
C AND C++ COMPILERS
The type 'long float' is no longer supported. This is
to be conformant with ANSI C.
The passes ZTC1.EXE and ZTC2.EXE have been dropped.
If you are running MSDOS with a 386 or 486, ZTC.COM will
automatically run the 'x' versions of the executable. Otherwise,
the 'b' versions will be run by default. You can force the use of
the 'b' versions on a 386 or 486 by using the -b flag explicitly.
When the -e switch is used, source file and line numbers now
are printed with error messages, rather than preprocessed
line numbers and the file "preprocessed".
_loadds and _export now work on member functions.
The way floats and doubles are returned from Pascal and C++
functions has been changed. Instead of the value being
returned in the CPU registers, it is returned on the stack
in the same way that structs are returned. This is compatible
with the way MSC returns floats and doubles for Pascal and
FORTRAN functions. The behavior of Pascal and FORTRAN
functions is identical in MSC and ZTC/C++.
The way floats and doubles are returned from C functions was
not modified because doing that would make it no longer
reentrant, a feature that many customers require.
10 byte long doubles are not supported in ZTC/C++. Long doubles
are 8 bytes. In the future we will probably make them 10
bytes.
With the -s flag to add stack overflow checking, the
function chkstk() is always called now instead of only
being called if there are local variables. This enables more
accurate checking and provides a hook for profilers who
need to know when functions are called.
Bit fields can now be any integral type, though they are always
treated as the unsigned versions of the integral types.
Conversions of ints directly to far pointers is a syntax error
instead of a warning. If you want to do this, cast the int to
a long before casting it to a pointer.
The following cast:
long l;
char near *p;
l = (long) p;
is handled like:
l = (long)(char far *)p;
instead of like:
l = (long)(unsigned)p; /* 0 fill high 16 bits */
The conversion of a far pointer to a long and vice versa
is done as a 'paint', i.e. no change in the bit pattern
occurs.
-----------------------------------
C++ COMPILER
If the -A switch is used, static class data members now must
have a definition elsewhere somewhere in the program. Without
-A, an initialization of 0 is provided if none is provided,
unless a constructor is required.
The #ident pragma is recognized and ignored. This is for
compatibility with some Unix software.
The predefined macros SPTR and LPTR have been removed. This is
because windows.h uses them, and because of their
Ansi incompatibility. If you need them, you can use the
following equivalent:
#if M_I86SM || M_I86MM
#define SPTR 1
#else
#define LPTR 1
#endif
The -P switch now causes global variables to have pascal
name mangling (converting name to upper case) as well as
function names. This is for improved compatibility with MSC.
However, be careful that if you are communicating with the
Zortech runtime library via global variables such as _8087,
_stack, and _okbigbuf, be sure to declare them as _cdecl,
as in:
int _cdecl _8087;
to ensure that they will link properly with the runtime library.
-----------------------------------
C COMPILER
// comments are now accepted for compatibility with much
existing Windows code.
-----------------------------------
SEGMENT CONTROL
There are now several ways to control the code segment name(s)
in your object files.
1. The old way, which is use the name generated by the compiler.
2. Use PATCHOBJ to modify the segment names in the .OBJ file.
3. Use the -NTcsname switch, which sets the code segment name
to csname. Note that there is no space between -NT and csname.
4. Use the -NS switch, which causes a new code segment to be
started each time a global far function is encountered.
(Be careful using near functions with this, the near functions
must immediately follow any global function which calls them,
and cannot be called by more than one global function (because
near functions cannot cross segment boundaries)).
The name of the segment is the name of the function with _TEXT
appended.
5. Use the #pragma:
#pragma ZTC cseg csname
which causes subsequent code to be placed in the segment
csname. When used with -NS, the -NS switch overrides the
pragma for global far functions.
The -NT, -NS and #pragma ZTC cseg can all be used independently
or in any combination.
Segment name control is an advanced technique usually used
when optimizing very large Windows and OS/2 protected mode
programs. The idea is to group functions which frequently
call each other into the same segment.
-----------------------------------
386ASM AND 386LINK
ZTC will now run .asm files through 386ASM instead of MASM
if -mp or -O is used, and assembling for the 386 or 486.
ZTC and MAKE can now handle arbitrarilly long command lines
for 386ASM and 386LINK.
-----------------------------------
OS/2 COMPILER
ZTC1.EXE and ZTC2.EXE have been dropped, ZTC1B.EXE and ZTC2B.EXE
are now always run, regardless of the -b switch setting on
ZTC.COM.
-----------------------------------
ZTC.COM
The -L switch has been extended. If there is a parameter following
the -L, it is assumed to be a switch or a path, which is added
to the linker command line. For instance,
ZTC test -L/packcode -L\test\prog\
will add the switch /packcode on the linker command, and will
add the library search path \test\prog\ to the command. Note that
MS-LINK requires the trailing \ on path commands.
.ASM extensions are now tried *after* .CPP and .C default extensions.
.CC and .C++ are now recognized as C++ source filename extensions.
.EXE and .COM files on the command line are now taken to be
the executable program file name. A .COM extension will cause
EXE2BIN to be run (same as -mt switch).
dos_abs_disk_read/write now work with DOS 4.
-----------------------------------
MAKE
Now accepts a sequence of rule flags, instead of only
allowing one.
SET commands can now be used to set environment variables for
executed programs.
-----------------------------------
C LIBRARY
Added the ANSI C functions:
mblen
mbtowc
wctomb
mbstowcs
wcstombs
tmpfile
The old way of doing wild card expansion on the command line
was to link in the file _main$(MODEL).obj. This is no longer
necessary, just add the line
WILDCARDS
at file scope level in one of your source files. WILDCARDS is
defined in DOS.H. The old method will be dropped probably in
the next release.
Sometimes it is convenient determine what type of executable
a program is at runtime rather than compile time. Especially
since Zortech supports so many different kinds of executables.
This feature can aid, for instance, in making fewer libraries
that still support all the exe types.
In dos.h, there is the global:
extern unsigned short _cdecl _exe_type;
which is set to one of the following:
EXE_DOS MSDOS
EXE_DOS16RM Rational 286 DOS Extender
EXE_ZPM ZPM 286 DOS Extender
EXE_PHAR386 Pharlap 386 DOS Extender
EXE_DOSX DOSX 386 DOS Extender
EXE_WINDOWS Windows 3
EXE_OS2 OS/2 1.x
EXE_SCOUNIX SCO Unix
alloca() has been implemented. alloca() cannot be used inside
functions that have the Windows prolog/epilog code in them.
-----------------------------------
C++ LIBRARY
This has now been combined with the C library. There no longer
are separate C++ libraries.
If you are linking with C++ .OBJ files generated by older
versions of ZTC++, the library PLx.LIB will not be found. The
solutions are:
o Recompile the code with ZTC++ 3.0.
o Link with the /NOD switch, and specify ZLx.LIB on
the command line to the linker.
o Use ZORLIB to create a PLx.LIB library with nothing
in it.
o Use PATCHOBJ to remove the default library names from
the .OBJ files.
-----------------------------------
ARRAYS LARGER THAN 64K
Frequently it is necessary to have arrays that are larger than 64K,
for instance:
char array[100000]; // 100,000 bytes
double values[10][1000]; // 10*1000*8 == 80,000 bytes
This cannot be directly done with 16 bit compilers. A portable
alternative is to construct an array of pointers to arrays, where
each unit is less than 64k in size. Our examples above become:
char *array[2];
#define array(i) (array[(i) & 1][(i) >> 1])
double *values[10];
This needs to be in one of the large data models, C, L, R or V.
These cannot be statically allocated, so we turn to calloc to initialize
them to all 0s:
main()
{
int i;
for (i = 0; i < 2; i++)
array[i] = (char *) calloc(100000/2,sizeof(char));
for (i = 0; i < sizeof(values)/sizeof(values[0]); i++)
values[i] = (double *) calloc(1000,sizeof(double));
...
}
To access an element of array[], instead of array[i], use the syntax:
long i;
array(i) = array(i + 10);
Note that the macro can be used both as an lvalue or an rvalue. Similarly,
for values:
int i,j;
values[i][j] = values[i][j] + 6.7;
It probably will not be necessary to deallocate the memory used for the
arrays, if they are used for the duration of the program. DOS will deallocate
them when the program terminates.
The methods used are not only portable ANSI C, they are also
frequently faster than using the so-called huge arithmetic. They are
directly portable to a 32 bit compiler.
-----------------------------------
HUGE POINTERS
Sometimes it becomes inevitable that one must deal with objects larger
than 64k, because they are returned by the operating system or some
third party library over which one has no control. Here are a set
of techniques for dealing with those rare cases in a portable manner.
Huge pointers are pointers that point into and can manipulate a
contiguous block of memory that is larger than 64k. Recall that
a far pointer on 80X86 CPUs consist of a 16 bit segment and a 16
bit offset. Thus, up to 64k can be addressed by adjusting the offset
alone. This is how far pointers work. To deal with blocks larger
than 64k, the segment must also be manipulated.
A moment's reflection will show that there are only three operations
that need to be done with a huge pointer. That is, adding a (possibly
negative) offset to it, comparing it with another huge pointer, and
computing the difference between it and another huge pointer. The
names of these functions are:
hugeptr_add
hugeptr_cmp
hugeptr_diff
These functions and macros are made available by #include'ing hugeptr.h.
If the compile is for 32 bit code, the functions become direct pointer
manipulation on 32 bit near pointers.
Example:
#include <hugeptr.h>
/* A version of strlen() that works with strings > 64k in length */
long hstrlen(const char _far *h)
{ const char _far *hend;
hend = *h;
while (*hend)
hend = (char _far *) hugeptr_add(hend,1);
return hugeptr_diff(hend,h);
}
-----------------------------------
RATIONAL SYSTEMS' 286 DOS EXTENDER
The following files are no longer distributed by Zortech:
DOS16.H
RUN.MAC
DOS16LIB.C
PML.ASM
ZRL.LIB
MAKEFILE.RAT
Instead, they or the equivalent will be supplied by Rational
when you purchase their extender.
There is no separate CR.OBJ needed anymore to link a Rational
executable, instead specify ZRL.LIB in the list of libraries
to the linker, and be sure to use the /NOE switch when using
Microsoft LINK.
Specific instructions on using Rational's extender with Zortech
C/C++ will be supplied by Rational.
-----------------------------------
OLD STREAMS
The old C++ 1.2 stream library, stream.hpp, has been moved out
of the main libraries and into separate libraries, as they are
incompatible with new streams. If you are using old streams, the
old stream library must be explicitly linked in, or specified on
the command line to ZTC.COM. The libraries are:
For MSDOS, OS/2 and Windows:
oldstrs.lib S model
oldstrm.lib M model
oldstrc.lib C model
oldstrl.lib L model
For MSDOS:
oldstrl.lib R,Z models
oldstrv.lib V model
For Pharlap and DOSX 386 extenders:
oldstrx.lib P,X models
Example:
ZTC test.cpp \zortech\lib\oldstrs.lib
We recommend that you convert to new streams, as the old streams
are obsolete and may get dropped in future versions of ZTC++.
-----------------------------------
DGROUP DATA SEGMENT OVERFLOWS
Many people with large programs run out of space in the
default data segment DGROUP. The solution is to move some
data out of DGROUP into other segments. Possible ways are:
1. Compile with the -R switch which causes switch statement
tables to be placed in the code segment rather than
DGROUP.
2. For C++ programs, use the -NV switch to place virtual
function pointer tables in far data segments.
3. Declare statically allocated data as far, i.e.:
int far array[100];
Data declared as far should be data that is both relatively
large and is accessed relatively infrequently, as the
segment register juggling required to access it is rather
expensive.
If another module should reference far static data, be
sure and declare it as far, as in:
extern int _far array[];
Otherwise, the compiler will assume that it is in the default
data segment DGROUP. Note that this is different from MSC6's
implementation, where extern data is not assumed to be in
DGROUP. Although our implementation involves more work on
the programmer's part (putting _far in the right places), we
feel that the fine control this offers makes it more than
worthwhile in high-performance applications. It's also true
that most applications need less than 64k of static data,
and so needn't pay any unnecessary penalty for assuming that
extern data is not accessible via DS.
-----------------------------------
WINDOWS SUPPORT
The -mw switch that specifies DS!=SS is only needed for the following types of code:
A) Windows Dynamic Link Libraries (DLLs).
B) OS/2 DLLs.
C) OS/2 threads.
Currently, class objects can only be created on the near heap for S and M
models, and only on the far heap for C and L models. Objects cannot be
created in __ss or __handle space.
If you are calling virtual member functions in Windows, and the
definition of the virtual member function is in your app, and the call
is from a DLL, the following problem occurs:
1. If _loadds (or -mu) is not used for the function, DS will probably not have
the right value in it when the function is called.
2. If _loadds is used (or -mu), DS will have the right value in it
*AS LONG AS THERE IS ONLY ONE INSTANCE OF THE APP RUNNING*. Second
instances will have the DS set to the value for the first instance.
There isn't any clean solution to this. The virtual function is what
Windows calls a 'callback function'. Callback functions cannot be called
directly from the app, they can only be called via a pointer returned
by MakeProcInstance(). They also must be compiled with the Windows
prolog/epilog code in (the -W switch). This is impractical for virtual
functions. Therefore, do not reference static or global data in
a virtual function meant to be called from a DLL, or use near pointers.
-----------------------------------
SYMBOLIC DEBUG INFO
Limitations and problems:
o In order to conserve the size of the symbol table,
typedefs and enums are not emitted. Only the minimal
type information for variables actually used is emitted.
o Because the optimizer moves variables and sections of code
around, there becomes only one scope per function. Thus,
avoid using the same variable names in nested scopes
if you want to use the debugger on them.
o Use the -S switch to cause the code generator to always
generate a stack frame for each function if you want
symbolic debugging. Most debuggers seem to expect this.
ZTC.COM will automatically invoke -S if the -g or -gs
switch was specified.
-----------------------------------
COMPILER VERSIONS
There are two ways to find the version of the compiler you are using:
1. Compile a file with -v.
2. Run ZTC1B, etc., with no arguments.
-----------------------------------
LIBRARY SOURCE
Added files to support ROM code development:
ROMASM BAT Replace ASM module in rom libs
ROMC BAT Replace C module in rom libs
CROM ASM Sample ROM startup code
The BUILDLIB.BAT file can be used to make all four rom libraries.
-----------------------------------
C PROGRAMMING TOOLS
Please look in the subdirectory SAMPLE for a description of these
useful utilities.
-----------------------------------
HEAP IS CORRUPTED
This error message appears sometimes when running programs
compiled with Zortech. This message is generated by the
library function free(), caused by:
1. Passing free() a pointer not generated by malloc, calloc or
realloc.
2. Free'ing the same pointer twice.
3. A wild pointer has trashed the heap's data structures.
If you are writing C code, the mem package is designed to
help track down such errors.
A common cause of this in C++ programs is when classes contain a
member that is a dynamically allocated pointer to something. If
there is no copy constructor, the compiler generates a memberwise
copy, resulting in two instances with the same pointer in them.
Destructing both results in case 2 above.
For example:
#include <stdlib.h>
#include <string.h>
struct X {
char *p;
X(char *s) { p = strdup(s); }
~X() { free(p); }
};
main()
{ X x("hello");
X y = x; // invoke copy constructor
x = y; // invoke X::operator=(X&)
}
This is guaranteed to generate the "Heap is corrupted" message.
To fix the code, add these member functions to X:
X(X& x) { p = strdup(x.p); }
X& operator =(X& x)
{ free(p);
p = strdup(x.p);
return *this;
}
-----------------------------------
VIRTUAL FUNCTION POINTER TABLES
A table of pointers to functions is created for each class that
has virtual functions or is derived from a class with virtual
functions. Because of separate compilation, sometimes redundant
copies of the tables are created, even leading to excessive
consumption of space in DGROUP. It turns out that the only time
the code needs a virtual function pointer table (vtbl) is within
the body of a constructor. So a vtbl for a class is only actually
generated when the body of a constructor that needs a vtbl is
instantiated. A maximum of one vtbl per class per object module
is generated.
This suggests a technique for minimizing vtbl generation:
1. Avoid inline constructors.
2. Group all the definitions for constructors for a class into
one source file.
This will create a maximum of one vtbl per class per program.
Other C++ implementations use command line switches to cause
vtbls in some modules to be global, in others external. This
can get rather complex, the above technique is simpler and more
reliable.
-----------------------------------
FLASH GRAPHICS
The FG support for the IBM 8514A is actually for the IBM 8514A API.
Thus, any display that conforms to the API definition will work with
FG. Another consequence of this is that the FG detect logic looks for
the API, *not* the 8514A itself. The API is actually a TSR supplied by
IBM with the 8514A, and must be run before the FG application is run.
===========================================================================
EXISTING PROBLEMS AND WORKAROUNDS
===========================================================================
************************ MAKE ****************
-----------------------------------
Should have a -k switch to cause make to continue after errors
occur attempting to make targets not depending on the one that failed.
-----------------------------------
Make cannot figure out more than one level of implicit rules.
I.e. this doesn't work:
.c.obj: ;ztc -c $<
.obj.exe: ;ztc $<
hello.exe: hello.c
****************** C COMPILER ****************
-----------------------------------
The macros:
#define STR(a) NXSTR(a)
#define NXSTR(a) #a
#define A 1
if (STR(A)[0] == '1')
result in:
if ("A"[0] == '1')
instead of:
if ("1"[0] == '1')
-----------------------------------
Conversions between doubles and unsigned longs are handled
as if they are conversions between doubles and signed longs.
-----------------------------------
Expecting void functions to return values gets ZTC2 bugs in the following
case:
{
extern void func();
i = h() ? g() : func();
}
-----------------------------------
The preprocessor line
#line 47 "FILE.C"
does not set the module name to file.c.
-----------------------------------
If there is an error writing the .OBJ file, the .OBJ file is not
deleted. If the error is because the disk is full, there remains a
partial corrupted .OBJ file.
-----------------------------------
The type 'long double' is not distinguished from type 'double'.
-----------------------------------
A near pointer is converted to a far pointer by putting DS, SS or CS into
the segment portion as appropriate. The bug is if the near pointer is NULL,
the resulting far pointer gets a non-zero segment value! When converting,
always testing for 0 before loading the segment would cause a lot of
inefficiencies. Note that MSC 5.1 has the same bug, I haven't checked
TC or BCC for it.
****************** C++ COMPILER ****************
-----------------------------------
All the current bugs in the C compiler (!).
-----------------------------------
When the compiler internally generates an X& X::operator=(X&), and
X has a virtual base class V that appears N times in the inheritance DAG,
the function V& V::operator=(V&) is called N times instead of once.
-----------------------------------
When the compiler internally generates an X& X::operator=(X&), and
X has a member that is an array of class Y, a bit copy is done of the
array instead of a loop calling Y& Y::operator=(Y&).
-----------------------------------
Inline functions that contain any whiles, dos,
fors or switches are not inlined. The code will still compile and
execute correctly, however. This is not a bug.
-----------------------------------
If a class name is hidden in a lower level scope, you still
can't access it even if you prefix it with 'struct' or 'class'.
-----------------------------------
To call a function via a pointer to member, you must use exactly
one set of parentheses:
a = (p->*mfp)(args...); // works
a = ((p->*mfp))(args...); // bug: gives syntax errors
b = p->*mfp; // what is this supposed to mean?
-----------------------------------
You cannot cast pointers to members to/from pointers to virtual base class
members. This produces a syntax error if tried.
-----------------------------------
Pointers to data members can be explicitly cast to ints. Since they are
implemented as ints, this seemed reasonable. It's not portable, but
is useful in special contexts (like debugging!).
Pointers to function members can be explicitly cast to regular pointers
to functions. This is also non-portable.
-----------------------------------
Pointers to function members, even when they should compare equal, may
not. This occurs when a thunk is generated to do things like adjust the
this pointer offset, or call a virtual member function. If the thunks
are generated in different files, the member pointers will compare
different (because just the addresses are compared).
-----------------------------------
The anachronistic member access syntax on the last page of the C++ book:
int cl.fct() { /* ... */ }
is not supported. Use instead:
int cl::fct() { /* ... */ }
-----------------------------------
Compiler should issue warning when a non-virtual method in a sub-class hides
a virtual method in the base class. (Tony Hagen, James Coggins,
Bjarne Stroustrup, et. al. suggest it.) The other way around, too.
-----------------------------------
Variadic cdecl functions that return structures return them in a static
temporary rather than on the stack, thus they are not reentrant and more
than one cannot be used in the same expression.
-----------------------------------
If a class or a struct has no tag name, the compiler will generate one of
the form "_C%d", where %d increments with each new name generated. This
is necessary for the name mangling to work. Unfortunately, if you have
structs like this defined in header files, and include the files in
different orders in different files, the names will come out different.
This can cause errors at link time due to the name mangling and typesafe
linkage not lining up. Because of this, the compiler generates a warning
if there is no tag name and the tag name is needed for name mangling
purposes.
-----------------------------------
struct s { f(); };
static s::f() {} // diagnosed as an error by cfront
ZTC++ allows member functions to be local to a file, i.e. static. Cfront
does not allow this.
-----------------------------------
Conditional expressions cannot be lvalues, as in:
(a ? b : c) = 7;
are not allowed.
****************** C LIBRARY ****************
-----------------------------------
The int package uses a local stack for the interrupt service routine (isr).
If there are overlapping interrupts to the same isr, the program will
crash because the stacks overlap!
If you use 0 for the stack size parameter to int_intercept(), no local
stack will be allocated for the isr. Thus, overlapping interrupts have
a chance of working (if the original stack is big enough).
-----------------------------------
The stat() function uses findfirst(). So if you are in the middle of a
findfirst-findnext sequence, you cannot use stat().
****************** BLINK ****************
-----------------------------------
If a .OBJ file is corrupted, BLINK may crash instead of issuing
an error message.
-----------------------------------
******************** BUG REPORTS **************************
Occasionally, you may run across a suspected problem with the compiler.
If you send in a bug report, please do the following:
1. Try to determine the smallest possible program that exhibits the problem.
Sending in a 10,000 line program with a message that "it doesn't work"
doesn't help much. Also, frequently the process of determining that
smallest program reveals the problem to be something other than a compiler
bug. At the least, an easy workaround to keep you going becomes obvious.
Compiling with the -v switch can help a lot in isolating the problem
down.
2. Include all the #include'd .h and .hpp files. Better yet, boil it all
down to 1 source file that demonstrates the problem without #include'ing
anything.
3. Include a READ.ME file describing the problem, and what command was
issued to the compiler to cause it.
4. Check to make sure it isn't one of the following problems. A large
number of the bug reports we get are really variations on these,
even experts frequently make these mistakes:
Common problem #1: Assigning beyond the end of alloc'd data.
p = malloc(4);
for (i = 0; i <= 4; i++)
p[i] = value;
The bug is that when i==4, assigning to p[4] will cause a
probable program crash.
Another common manifestation of this is:
p = malloc(strlen(s));
strcpy(p,s); /* didn't leave room for terminating 0! */
Common problem #2: Returning a reference to a variable that goes out of scope.
int &func()
{ int a;
...
return a;
}
Common problem #3: Expecting that \ line splicing does not occur in a // comment.
According to ANSI C para. 2.1.1.2, \ line splicing occurs in
translation phase 2, that is, *before* comment processing is done.
Therefore, // comments ending in a \ will splice lines, as in:
// This is a comment \
printf("This should never print.\n");
Since the ANSI C++ people say that they intend to 'drop in'
the ANSI C preprocessor, this is the correct way it should work.
Unfortunately, some C++ compilers do not do this correctly, and
the split seems to be about 50-50. Therefore, currently we
recommend that // comments should never end in a \.
We've also seen some code that used some nice-looking comments like:
//--------------------------\\
This is best avoided.
Common problem #4: Mixing old style definition with ANSI C prototype
The following is *NOT* a bug, it's ANSI C:
extern int func(char);
int func(c)
char c;
{
^
"test.c", line 5 Syntax error: type of 'c' does not match function prototype
Had: <int>
and: <char>
}
Old style parameter types undergo integral promotions before
comparing them against the prototype. The old style "char c;"
is interpreted as "c is passed as an int, but interpret it as
a char in func". If you want it to be a char, define func as:
int func(char c) { ... }
************************ ZORTECH ELECTRONIC SUPPORT *************************
Zortech operates a free bulletin board system for Zortech customers
and anyone else who is interested in C++. There are many files of interest to C++
programmers to download, and message areas where you can have conversations
with other C++ users. The phone numbers are:
(206) 822-6907 USA
081 855 3286 England
We also have a conference on BIX, to join type:
j zortech
In Europe, we also have a conference on CIX, again type
j zortech
For those with access to unix email, we have a Zortech mailing list. To
join in, send mail to:
ztc-list-request@zortech.com
Other unix email addresses are:
sales@zortech.com For pricing questions and orders
ztc-bugs@zortech.com For sending in bug reports
ztc-list@zortech.com For posting to the Zortech mailing
list
of old style function definition
parameter types.
-----------------------------------
EOF