C/C++ Compilers

This chapter covers the following topics:

C/C++ command-line format

The formal Watcom C/C++ compiler command-line syntax is shown below:

The square brackets [ ] denote items that are optional.

wcc
is the 16-bit Watcom C compiler.
wpp
is the 16-bit Watcom C++ compiler.
wcc386
is the 32-bit Watcom C compiler.
wpp386
is the 32-bit Watcom C++ compiler.
file_spec
is the QNX name specification of the file to be compiled. The Watcom C and C++ compilers assume default file extensions when no extension is specified:
  • Watcom C assumes an extension of .c
  • Watcom C++ checks for a file with one of the following extensions, in the order listed:
    • .C
    • .cpp
    • .cc
    • .c

If a path isn't specified, the current working directory is assumed. If the file isn't in the current directory, an adjacent C directory (that is,../c) is searched.

options
is a list of valid compiler options, each preceded by a dash (-). Options may be specified in any order. See the C/C++ Compiler Options chapter for more information.
extra_opts
is the name of an environment variable or file that contains additional command-line options to be processed. If the specified environment variable doesn't exist, a search is made for a file with the specified name.

If no file extension is included in the specified name, the default file extension is .occ. A search of the current directory is made. If not successful, an adjacent OCC directory (that is, ../occ) is searched, if it exists.

WCC, WPP, WCC386 and WPP386 environment variables

You can use an environment variable to specify commonly used compiler options, as follows:

CommandEnvironment Variable
wccWCC
wppWPP
wcc386WCC386
wpp386WPP386

These options are processed before options specified on the command line.

For example:

export "WCC=-d1 -ot"
export "WPP=-d1 -ot"
export "WCC386=-d1 -ot"
export "WPP386=-d1 -ot"

The above examples define the default options to be d1 (include line number debugging information in the object file), and ot (favor time optimizations over size optimizations).

Once a particular environment variable has been defined, those options listed become the default each time the associated compiler is used. The compiler command line can be used to override any options specified in the environment string.

If you use the same compiler options all the time, you might find it handy to define the environment variable in your user initialization file.

C/C++ command-line examples

This section gives some examples of using Watcom C/C++ to compile C/C++ source programs.

  1. Use the 16-bit compiler to process report.c or report.cpp, producing an object file that contains source line number information. Omit stack overflow checking from the object code.
        wcc report -d1 -s
        wpp report -d1 -s
        
    
  2. Use the 16-bit compiler to compile calc.c or calc.cpp for the medium memory model, and generate calls to floating-point library emulation routines for all floating-point operations. Memory models are described in the chapter 16-bit Memory Models, in the 16-bit Topics supplement to this manual.
        wcc -mm -fpc calc
        wpp -mm -fpc calc
        
    
  3. Use the 16-bit compiler to process kwikdraw.c or kwikdraw.cpp, producing object code for an Intel 286 system equipped with an Intel 287 numeric data processor (or any upward-compatible 386/387, 486DX, or Pentium system). While the choice of these options narrows the number of microcomputer systems where this code will execute, the resulting code is highly optimized for this type of system.
        wcc kwikdraw -2 -fpi87 -oaxt
        wpp kwikdraw -2 -fpi87 -oaxt
        
    
  4. Use the 32-bit compiler to compile calc.c or calc.cpp for the flat memory model, using the stack-based argument-passing convention. Memory models are described in the chapter 32-bit Memory Models. Argument-passing conventions are described in the chapter 32-bit Assembly Language Considerations.
        wcc386 -mf -3s calc
        wpp386 -mf -3s calc
        
    
  5. Use the 32-bit compiler to process kwikdraw.c or kwikdraw.cpp, producing object code for a 386-compatible system equipped with a 387 numeric data processor. The register-based argument-passing convention is used. The resulting code is highly optimized for 386 systems.
        wcc386 kwikdraw -3r -fpi87 -oaimxt
        wpp386 kwikdraw -3r -fpi87 -oaimxt
        
    
  6. Process ../.source/.modabs.c or ../.source/.modabs.cpp (a file in a directory that's adjacent to the current one). The object file is placed in the current directory. Information on local symbols and data types is included with the object code and data. The code generated is straight-forward, unoptimized code that can be readily debugged with the Watcom Debugger.
        wcc ../source/modabs -d2
        wpp ../source/modabs -d2
        wcc386 ../source/modabs -d2
        wpp386 ../source/modabs -d2
        
    
  7. Process the program contained in the file /.cprogs/.grep.tst. The file iomods.c is included as if it formed part of the source input stream. The include search path and memory model options are defaults each time the compiler is invoked. The memory model option could be overridden on the command line. After looking for an include file in the current directory, the compiler searches each directory listed in the i path. See ``#include file processing'' for more information.
        
        export "WCC=-i=/includes -mc"
        export "WPP=-i=/includes -mc"
        export "WCC386=-i=/includes -mf"
        export "WPP386=-i=/includes -mf"
        
    
        wcc /cprogs/grep.tst -fi=iomods.c
        wpp /cprogs/grep.tst -fi=iomods.c
        wcc386 /cprogs/grep.tst -fi=iomods.c
        wpp386 /cprogs/grep.tst -fi=iomods.c
        
    
  8. Process the program contained in the file grep.c or grep.cpp, which is located in the current directory. The object file is placed in the directory ../obj, under the name grep.o.
        wcc grep -fo=../obj/ -mm
        wpp grep -fo=../obj/ -mm
        wcc386 grep -fo=../obj/ -mf
        wpp386 grep -fo=../obj/ -mf
        
    
  9. Process the program contained in the file grep.c or grep.cpp that's located in the current directory. The macro DBG is defined so that conditional debugging statements that have been placed in the source are compiled. The object file is placed in the directory ../obj, and its filename extension is .dbo (instead of .o). Selection of a different filename extension permits easy identification of object files that have been compiled with debugging statements.
        wcc -dDBG=1 grep -fo=../obj/.dbo -mc
        wpp -dDBG=1 grep -fo=../obj/.dbo -mc
        wcc386 -dDBG=1 grep -fo=../obj/.dbo -mf
        wpp386 -dDBG=1 grep -fo=../obj/.dbo -mf
        
    
  10. Generate code for gopks.c or gopks.cpp, and place it in the GKS group. If the g option isn't specified, the code isn't placed in any group.
        wcc -g=GKS -s /gks/gopks
        wpp -g=GKS -s /gks/gopks
        
    

    This example assumes that this file contains the definition of the routine gopengks(), as follows:

        void far gopengks( int workstation, long int h )
        {
            .
            .
            .
        }
        
    

    For a small code model, the routine gopengks() must be defined in this file as far, since it's placed in another group. The s option is also specified to prevent a runtime call to the stack overflow check routine that's placed in a different code segment at link time. Since the gopengks() routine appears in a different code segment, it must be prototyped by C routines in other groups as:

        void far gopengks( int workstation, long int h );
        
    

Benchmarking hints

The Watcom C/C++ compiler contains many options for controlling the code to be produced. It's impossible to have a certain set of compiler options that produce the absolute fastest execution times for all possible applications. With that said, we'll list the compiler options that we think give the best execution times for most applications. You might have to experiment with different options to see which combination of options generates the fastest code for your particular application.

The recommended options for generating the fastest 16-bit code are:

Pentium
-oneatx -zp4 -5 -fpi87 -fp5
486
-oneatx -zp4 -4 -fpi87 -fp3
386
-oneatx -zp4 -3 -fpi87 -fp3
286
-oneatx -zp4 -2 -fpi87 -fp2
186
-oneatx -zp4 -1 -fpi87
8086
-oneatx -zp4 -0 -fpi87

The recommended options for generating the fastest 32-bit code are:

Pentium
-oneatx -zp4 -5 -fp5
486
-oneatx -zp4 -4 -fp3
386
-oneatx -zp4 -3 -fp3

Option on causes the compiler to replace floating-point divisions with multiplications by the reciprocal. This generates faster code (multiplication is faster than division), but the result might not be the same because the reciprocal might not be exactly representable.

Option oe causes small user-written functions to be expanded in-line rather than generating a call to the function. Expanding functions in-line can further expose other optimizations that couldn't otherwise be detected if a call were generated to the function.

Option oa causes the compiler to relax alias checking.

Option ot must be specified to cause the code generator to select code sequences that are faster without any regard to the size of the code. The default is to select code sequences that strike a balance between size and speed.

Option ox is equivalent to oilmr and s, which cause the compiler to expand intrinsic functions in-line (oi), perform loop optimizations (ol), generate 387 instructions in-line for math functions such as sin(), cos() and sqrt() (om), reorder instructions to avoid pipeline stalls (or), and not to generate any stack overflow checking (s). Option or is very important for generating fast code for the Pentium processor.

Option zp4 causes all data to be aligned on 4-byte boundaries. The default is zp1, which packs all data. This reduces the amount of data memory required but requires extra clock cycles to access data that isn't on an appropriate boundary.

Options 0, 1, 2, 3, 4 and 5 emit code sequences optimized for processor-specific instruction set features and timings. For 16-bit applications, the use of these options might limit the range of systems on which the application will run, but there are improvements in execution performance.

Options fp2, fp3, and fp5 emit floating-point operations targetted at specific features of the math coprocessor in the Intel series. For 16-bit applications, the use of these options might limit the range of systems on which the application will run but, there are improvements in execution performance.

Option fpi87 causes in-line 80x87 numeric data processor instructions to be generated into the object code for floating-point operations. Floating-point instruction emulation isn't included, so as to obtain the best floating-point performance in 16-bit applications.

For 32-bit applications, the use of the fp5 option gives good performance on the Pentium but less than optimal performance on the 386 and 486. The use of the 5 option gives good performance on the Pentium and minimal, if any, impact on the 386 and 486. Thus, the options -oneatx -zp4 -5 -fp3 give good overall performance for the 386, 486 and Pentium processors.

Compiler diagnostics

The Watcom C/C++ compiler issues two types of diagnostic messages: warnings and errors. A warning message doesn't prevent the production of an object file. However, error messages indicate that a problem is severe enough that it must be corrected before the compiler will produce an object file.

If the compiler prints diagnostic messages on the screen, it also places a copy of these messages in a file in your current directory. The file has the same file name as the source file, and an extension of .err. This error file is a handy reference when you wish to correct the errors in the source file.

To illustrate the diagnostic features of Watcom C/C++, we'll modify the hello program in such a way as to introduce some errors:

#include <stdio.h>

int main()
  {
     int x;
     printf( "Hello world\n" );
     return( y );
  }

The equivalent C++ program follows:

#include <iostream.h>
#include <iomanip.h>

int main()
{
    int x;
    cout << "Hello world" << endl;
    return( y );
}

In this example, we have added the lines:

    int x;

and

    return( y );

and changed the keyword void to int.

We compile the program with the warning option.

wcc hello -w3
wpp hello -w3
wcc386 hello -w3
wpp386 hello -w3

For the C program, the following output appears on the screen:

hello.c(7): Error! E1011: Symbol 'y' has not 
            been declared
hello.c(5): Warning! W202: Symbol 'x' has been 
            defined, but not referenced
hello.c: 8 lines, included 174, 1 warnings, 
         1 errors

For the C++ program, the following output appears on the screen:

File: hello.cpp
(8,13): Error! E029: symbol 'y' has not been 
        declared
(9,1): Warning! W014: no reference to symbol 'x'
       'x' declared at: (6,9)
hello.cpp: 9 lines, included 1267, 1 warning, 
           1 error

Here we see an example of both types of messages; an error and a warning message have been issued. As indicated by the error message, we require a declarative statement for the identifier y. The warning message indicates that, while it isn't a violation of the rules of C/C++ to define a variable without ever using it, we probably didn't intend to do so. On examining the program, we find that:

  1. the variable x should have been assigned a value, and
  2. the variable y has probably been incorrectly typed, and should have been entered as x.

The complete list of Watcom C/C++ diagnostic messages is presented in an appendix of this guide:

#include file processing

When using the #include preprocessor directive, a header is identified by a sequence of characters placed between the ``<'' and ``>'' delimiters (for example, <file>), and a source file is identified by a sequence of characters enclosed by quotation marks (for example, "file"). Watcom C/C++ makes a distinction between the use of ``<>'' or quotation marks to surround the name of the file to be included. The search techniques for header files and source files are slightly different. Consider the following example:

    #include <stdio.h>  /* a system header file */
    #include "stdio.h"  /* your own header or 
                           source file */

You should use ``<'' and ``>'' when referring to standard or system header files, and quotation marks when referring to your own header and source files.

The character sequence placed between the delimiters in an #include directive represents the name of the file to be included. The file name may include node, path, and extension.

It isn't necessary to include the node and path specifiers in the file specification when the file resides on a different node or in a different directory. Watcom C/C++ provides a mechanism for looking up include files that might be located in various directories and disks of the computer system. Watcom C/C++ searches directories for header and source files in the following order (the search stops once the file has been located):

  1. If the file specification enclosed in quotation marks (for example, "file-spec") or angle brackets (for example, <file-spec>) contains the complete node and path specification, that file is included (provided it exists). No other searching is performed. The node needn't be specified, in which case the current node is assumed.
  2. If the file specification is enclosed in quotation marks, the current directory is searched.
  3. Next, if the file specification is enclosed in quotation marks, the directory of the file containing the #include directive is searched. If the current file is also an #include file, the directory of the parent file is searched next. This search continues recursively through all the nested #include files until the original source file's directory is searched.
  4. Next, if the file specification is enclosed in quotation marks (for example, "file-spec") or in angle brackets (for example, <file-spec>), each directory listed in the path given in the i option is searched (in the order that they were specified).
  5. Next, each directory listed in the os_INCLUDE environment variable is searched (in the order that they were specified). The environment variable name is constructed from the current build-target name.

    The default build targets are:

    DOS
    when the host operating system is DOS
    OS2
    when the host operating system is OS/2,
    NT
    when the host operating system is Windows NT, or
    QNX
    when the host operating system is QNX.

    For example, the environment variable OS2_INCLUDE is searched if the build target is ``OS2''. The build target would be OS/2 if:

    • the host operating system is OS/2 and the bt option isn't specified, or
    • the bt=OS2 option is explicitly specified.
  6. Next, each directory listed in the INCLUDE environment variable is searched (in the order that they were specified).
  7. Finally, if the file specification is enclosed in quotation marks, an adjacent H directory (that is, ../h) is searched, if it exists.

In the above example, <stdio.h> and "stdio.h" could refer to two different files if:

  • there's a stdio.h file in the current directory,
  • there's one in the Watcom C/C++ include file directory (/usr/include), and the current directory isn't listed in the path given with the i option or in the INCLUDE environment variable.

The compiler searches the directories listed in i paths (see the description of the compiler i option) and the INCLUDE environment variable in a manner analogous to that which the operating system shell uses when searching for programs by using the PATH environment variable.

The export command can be used to define an INCLUDE environment variable that contains a list of directories. Issue a command of the form

export INCLUDE=path:path...

before running the Watcom C/C++ compiler for the first time.

The following example illustrates the use of the #include directive:

#include <stdio.h>
#include <time.h>
#include <dos.h>

#include "common.c"

int main()
  {
    initialize();
    update_files();
    create_report();
    finalize();
  }

#include "part1.c"
#include "part2.c"

If the above text is stored in the source file report.c in the current directory, then we might issue the following commands to compile the application.

export INCLUDE=/usr/include://1/headers
wcc report -fo=../obj/-i=../source
wpp report -fo=../obj/-i=../source
wcc386 report -fo=../obj/-i=../source
wpp386 report -fo=../obj/-i=../source

In the above example, the export command is used to define the INCLUDE environment variable. It specifies that the /usr/include directory (of the current node) and the /headers directory (a directory on node 1) are to be searched.

The i option for the Watcom C/C++ compiler defines a third place to search for include files. The advantage of the INCLUDE environment variable is that it doesn't need to be specified each time you use the compiler.

C/C++ preprocessor

The Watcom C/C++ preprocessor forms an integral part of Watcom C/C++. When any form of the p option is specified, only the preprocessor is invoked. No code is generated, and no object file is produced. The output of the preprocessor is written to the standard output file, although it can also be redirected to a file using the fo option.

Suppose the following C/C++ program is contained in the file msgid.c:

#define _IBMPC 0
#define _IBMPS2 1

#if _TARGET == _IBMPS2
char *SysId = { "IBM PS/2" };
#else
char *SysId = { "IBM PC" };
#endif

/* Return pointer to System Identification */

char *GetSysId()
  {
    return( SysId );
  }

We can use the Watcom C/C++ preprocessor to generate the C/C++ code that would actually be compiled by the Watcom C/C++ compiler by issuing the following command:

wcc msgid -plc -fo -d_TARGET=_IBMPS2
wpp msgid -plc -fo -d_TARGET=_IBMPS2
wcc386 msgid -plc -fo -d_TARGET=_IBMPS2
wpp386 msgid -plc -fo -d_TARGET=_IBMPS2

The file msgid.i is created, and contains the following C/C++ code:

#line 1 "msgid.c"



char *SysId = { "IBM PS/2" };
#line 9 "msgid.c"

/* Return pointer to System Identification */

char *GetSysId()
  {
    return( SysId );
  }

Note that the file msgid.i can be used as input to the Watcom C/C++ compiler, as follows:

wcc msgid.i
wpp msgid.i
wcc386 msgid.i
wpp386 msgid.i

Since #line directives are present in the file, the Watcom C/C++ compiler can issue error messages in terms of the original source file line numbers.

C/C++ predefined macros

In addition to the standard ANSI-defined macros supported by the Watcom C/C++ compilers, several additional system-dependent macros are also defined. These are described in this section. See the WATCOM C Language Reference manual for a description of the standard macros.

The Watcom C/C++ compilers run on various host operating systems including DOS, OS/2, Windows NT, and QNX. Any of the supported host operating systems can be used to develop applications for a number of target systems. By default, the target operating system for the application is the same as the host operating system unless some option or combination of options is specified. For example, DOS applications are built on DOS by default, OS/2 applications are built on OS/2 by default, and so on. But the flexibility is there to build applications for other operating systems/environments.

The macros described below can be used to identify the target system for which the application is being compiled.

In several places in the following text, a pair of underscore characters appears as __, which resembles a single, elongated underscore.

The Watcom C/C++ compilers support both 16-bit and 32-bit application development. The following macros are defined for 16-bit and 32-bit target systems:

M_I86
Watcom C/C++ predefines the macro M_I86 to identify the target as a 16-bit Intel-80x86-compatible environment (defined by the Watcom C/C++16 compilers).
M_I386
Watcom C/C++ predefines the macro M_I386 to identify the target as a 32-bit Intel-386-compatible environment (defined by the Watcom C/C++32 compilers).
__386__
Watcom C/C++ predefines the macro __386__ to identify the target as a 32-bit Intel-386-compatible environment (defined by the Watcom C/C++32 compilers).
_M_IX86
Watcom C/C++ predefines the macro _M_IX86 to identify the target architecture. The macro is identically equal to 100 times the architecture compiler option value (-0, -1, -2, -3, -4, -5, etc.). If -5 (Pentium instruction timings) were specified as a compiler option, then the value of _M_IX86 would be 500.

The Watcom C/C++ compilers support application development for a variety of operating systems. The following macros are defined for particular target operating systems.

__DOS__
Watcom C/C++ predefines the macro __DOS__ to identify the target operating system as DOS (including DOS Extenders).
MSDOS
Watcom C/C++ predefines the macro MSDOS to identify the target operating system as DOS (including DOS Extenders).
__OS2__
Watcom C/C++ predefines the macro __OS2__ to identify the target operating system as OS/2 (16-bit or 32-bit).
__QNX__
Watcom C/C++ predefines the macro __QNX__ to identify the target operating system as QNX.
__NETWARE_386__
Watcom C/C++ predefines the macro __NETWARE_386__ to identify the target operating system as Novell NetWare 386.
__NT__
Watcom C/C++ predefines the macro __NT__ to identify the target operating system as NT.
__WINDOWS__
Watcom C/C++ predefines the macro __WINDOWS__ to identify the target operating system as Microsoft Windows.

(16-bit only) This macro is defined when the zw, zW, zWs, or bt=windows option is specified.

(32-bit only) This macro is defined when the zw or bt=windows option is specified.

__WINDOWS_386__
Watcom C/C++ predefines the macro __WINDOWS_386__ to identify the target operating system as 32-bit Microsoft Windows. This macro is defined when the zw or bt=windows option is specified and you are using a 32-bit compiler.

The following macros indicate which compiler is compiling the C/C++ source code:

__cplusplus
Watcom C++ predefines the macro __cplusplus to identify the compiler as a C++ compiler.
__WATCOMC__
Watcom C/C++ predefines the macro __WATCOMC__ to identify the compiler as one of the Watcom C/C++ compilers.

The value of the macro depends on the version number of the compiler. The value is 100 times the version number (version 8.5 yields 850, version 9.0 yields 900, etc.).

The following macros are defined under the conditions described:

__FPI__
Watcom C/C++ predefines the macro __FPI__ when the fpi or fpi87 option is used.
__CHAR_SIGNED__
Watcom C/C++ predefines the macro __CHAR_SIGNED__ when the j option is used.
__CHEAP_WINDOWS__
(16-bit only) Watcom C/C++ predefines the macro __CHEAP_WINDOWS__ when the zW option is used.
__INLINE_FUNCTIONS__
Watcom C/C++ predefines the macro __INLINE_FUNCTIONS__ when the oi option is used.
NO_EXT_KEYS
Watcom C/C++ predefines the macro NO_EXT_KEYS when the za option is used.
__FLAT__
(32-bit only) Watcom C/C++ predefines the macro __FLAT__ when the mf option is used.
M_I86SM, __SMALL__
Watcom C/C++ predefines the macros M_I86SM and __SMALL__ when the ms option is used.
M_I86MM, __MEDIUM__
Watcom C/C++ predefines the macros M_I86MM and __MEDIUM__ when the mm option is used.
M_I86CM, __COMPACT__
Watcom C/C++predefines the macros M_I86CM and __COMPACT__ when the mc option is used.
M_I86LM, __LARGE__
Watcom C/C++ predefines the macros M_I86LM and __LARGE__ when the ml option is used.
M_I86HM, __HUGE__
(16-bit only) Watcom C/C++ predefines the macros M_I86HM and __HUGE__ when the mh option is used.

C/C++ extended keywords

Watcom C/C++ supports the use of some special keywords to describe system-dependent attributes of functions and other object names. These attributes are inspired by the Intel processor architecture and the plethora of function-calling conventions in use by compilers for this architecture. In keeping with the ANSI C and C++ language standards, Watcom C/C++ uses a double underscore (__), or a single underscore followed by an uppercase letter (for example, _S) as a prefix for these keywords. To support compatibility with other C/C++ compilers, alternate forms of these keywords are also supported through predefined macros.

__near
Watcom C/C++ supports the __near keyword to describe functions and other object names that are in near memory, and pointers to near objects.

Watcom C/C++ predefines the macros near and _near to be equivalent to the __near keyword.

__far
Watcom C/C++ supports the __far keyword to describe functions and other object names that are in far memory, and pointers to far objects.

Watcom C/C++ predefines the macros far, _far and SOMDLINK (16-bit only) to be equivalent to the __far keyword.

__huge
Watcom C/C++ supports the __huge keyword to describe functions and other object names that are in huge memory, and pointers to huge objects. The 32-bit compilers treat these as equivalent to far objects.

Watcom C/C++ predefines the macros huge and _huge to be equivalent to the __huge keyword.

__based
Watcom C/C++ supports the __based keyword to describe pointers to objects that appear in other segments, or the objects themselves. See ``Based Pointers'' for an explanation of the __based keyword.

Watcom C/C++ predefines the macro _based to be equivalent to the __based keyword.

__segment
Watcom C/C++ supports the __segment keyword, which is used when describing objects of type segment. See ``Based Pointers'' for an explanation of the __segment keyword.

Watcom C/C++ predefines the macro _segment to be equivalent to the __segment keyword.

__segname
Watcom C/C++ supports the __segname keyword, which is used when describing segname constant based pointers or objects. See ``Based Pointers'' for an explanation of the __segname keyword.

Watcom C/C++ predefines the macro _segname to be equivalent to the __segname keyword.

__self
Watcom C/C++ supports the __self keyword, which is used when describing self-based pointers. See ``Based Pointers'' for an explanation of the __self keyword.

Watcom C/C++ predefines the macro _self to be equivalent to the __self keyword.

_Packed
Watcom C/C++ supports the _Packed keyword, which is used when describing a structure. If specified before the struct keyword, the compiler forces the structure to be packed (no alignment, no gaps), regardless of the setting of the command-line option or the #pragma controlling the alignment of members.
__cdecl
Watcom C/C++ supports the __cdecl keyword to describe C functions that are called using a special convention. This keyword is described in the chapters

Watcom C/C++ predefines the macros cdecl, _cdecl, _Cdecl and SOMLINK (16-bit only) to be equivalent to the __cdecl keyword.

__pascal
Watcom C/C++ supports the __pascal keyword to describe Pascal functions that are called using a special convention described by a pragma in the stddef.h header file. For more information on this pragma, see the chapters:

Watcom C/C++ predefines the macros pascal, _pascal and _Pascal to be equivalent to the __pascal keyword.

__fortran
Watcom C/C++ supports the __fortran keyword to describe functions that are called from FORTRAN. It converts the name to uppercase letters and suppresses the underscore (_) that's appended to the function name for certain calling conventions.

Watcom C/C++ predefines the macros fortran and _fortran to be equivalent to the __fortran keyword.

__interrupt
Watcom C/C++ supports the __interrupt keyword to describe a function that's an interrupt handler.

For example:

#include <i86.h>

void __interrupt int10( union INTPACK r )
{
    .
    .
    .
}
       

The code generator emits instructions to save all registers. The registers are saved on the stack in a specific order so that they may be referenced using the INTPACK union as shown in the DOS example above.

The code generator emits instructions to establish addressability to the program's data segment since the DS segment register contents are unpredictable. The function returns using an IRET (16-bit) or IRETD (32-bit) (interrupt return) instruction.

Watcom C/C++ predefines the macros interrupt and _interrupt to be equivalent to the __interrupt keyword.

Don't use the __interrupt keyword for QNX interrupt handlers.
__export
Watcom C/C++ supports the __export keyword to describe functions and other object names that are to be exported from a Microsoft Windows or OS/2 Dynamic Link Library (DLL). See also the description of the zu option.

For example:

void __export _Setcolor( int color )
{
    .
    .
    .
}
      

Watcom C/C++ predefines the macro _export to be equivalent to the __export keyword.

__loadds
Watcom C/C++ supports the __loadds keyword to describe functions that require specific loading of the DS register to establish addressability to the function's data segment. This keyword is useful in describing a function that is placed in a Microsoft Windows or OS/2 1.x Dynamic Link Library (DLL). See also the description of the nd and zu options.

For example:

void __export __loadds _Setcolor( int color )
{
    .
    .
    .
}
      

If the function in an OS/2 1.x Dynamic Link Library requires access to private data, the data segment register must be loaded with an appropriate value since it will contain the DS value of the calling application on entry to the function.

Watcom C/C++ predefines the macro _loadds to be equivalent to the __loadds keyword.

__saveregs
Watcom C/C++ recognizes the __saveregs keyword, which is an attribute used by C/C++ compilers to describe a function that must save and restore all registers.

Watcom C/C++ predefines the macro _saveregs to be equivalent to the __saveregs keyword.

__stdcall
(32-bit only) The __stdcall keyword may be used with function definitions, and indicates that the 32-bit Win32 calling convention is to be used. Note the following: For more information, see ``Predefined __stdcall alias'' in the 32-bit Pragmas chapter.
__syscall
(32-bit only) The __syscall keyword may be used with function definitions, and indicates that the calling convention used is compatible with functions provided by 32-bit OS/2. For more information, see ``Predefined __syscall alias'' in the 32-bit Pragmas chapter.

Watcom C/C++ predefines the macros _syscall, _System and SOMLINK (32-bit only) to be equivalent to the __syscall keyword.

__far16
(32-bit only) Watcom C/C++ recognizes the __far16 keyword, which can be used to define far 16-bit (far16) pointers (16-bit selector with 16-bit offset) or far 16-bit function prototypes.

This keyword can be used under 32-bit OS/2 to call 16-bit functions from your 32-bit flat model program. Integer arguments are automatically converted to 16-bit integers, and 32-bit pointers are converted to far16 pointers before calling a special thunking layer to transfer control to the 16-bit function.

Watcom C/C++ predefines the macros _far16 and _Far16 to be equivalent to the __far16 keyword. This keyword is compatible with Microsoft C.

In the OS/2 operating system (version 2.0 or higher), the first 512 megabytes of the 4 gigabyte segment referenced by the DS register is divided into 8192 areas of 64K bytes each. A far16 pointer consists of a 16-bit selector referring to one of the 64K byte areas, and a 16-bit offset into that area.

A pointer declared as:

[type] __far16 *name;

defines an object that is a far16 pointer. If such a pointer is accessed in the 32-bit environment, the compiler generates the necessary code to convert between the far16 pointer and a flat 32-bit pointer.

For example, the declaration:

    char __far16 *bufptr;
      

declares the object bufptr to be a far16 pointer to char.

A function declared as:

    [type] __far16 func( [arg_list] );
      

declares a 16-bit function. Any calls to such a function from the 32-bit environment causes the compiler to convert any 32-bit pointer arguments to far16 pointers, and any int arguments from 32 bits to 16 bits. (In the 16-bit environment, an object of type int is only 16 bits.) Any value returned from the function is converted in an appropriate manner.

For example, the declaration

    char * __far16 Scan( char *buffer, int len,
                         short err );
      

declares the 16-bit function Scan(). When this function is called from the 32-bit environment, the buffer argument is converted from a flat 32-bit pointer to a far16 pointer (which, in the 16-bit environment, would be declared as char __far *. The len argument is converted from a 32-bit integer to a 16-bit integer. The err argument is passed unchanged. On returning, the far16 pointer (far pointer in the 16-bit environment) is converted to a 32-bit pointer that describes the equivalent location in the 32-bit address space.

_Seg16
(32-bit only) Watcom C/C++ recognizes the _Seg16 keyword, which has a similar, but not identical, function to the __far16 keyword described above. This keyword is compatible with IBM C Set/2.

In the OS/2 operating system (version 2.0 or higher), the first 512 megabytes of the 4 gigabyte segment referenced by the DS register is divided into 8192 areas of 64K bytes each. A far16 pointer consists of a 16-bit selector referring to one of the 64K byte areas, and a 16-bit offset into that area.

_Seg16 isn't interchangeable with __far16.

A pointer declared as:

[type] * _Seg16 name;

defines an object that's a far16 pointer. Note that the _Seg16 appears on the right side of the *, which is opposite to the __far16 keyword described above.

For example,

    char * _Seg16 bufptr;
      

declares the object bufptr to be a far16 pointer to char (the same as above).

The _Seg16 keyword may not be used to describe a 16-bit function. A #pragma directive must be used instead. A function declared as:

    [type] * _Seg16 func( [parm_list] );
      

declares a 32-bit function that returns a far16 pointer.

For example, the declaration:

    char * _Seg16 Scan( char * buffer, int len, 
                        short err );
      

declares the 32-bit function Scan(). No conversion of the argument list takes place. The return value is a far16 pointer.

__pragma
Watcom C++ supports the __pragma keyword to support in-lining of member functions. The __pragma keyword must be followed by parentheses containing a string that names an auxiliary pragma. Here's a simplified example showing usage and syntax:

#pragma aux fast_mul = \
    "imul eax,edx" \
    parm caller [eax] [edx] \
    value struct;

struct fixed {
    unsigned v;
};

fixed __pragma( "fast_mul") 
    operator *( fixed, fixed );

fixed two = { 2 };
fixed three = { 3 };

fixed foo()
{
    return two * three;
}
      

See the following chapters for more information on pragmas:

Based pointers

Near pointers are generally the most efficient type of pointer because they are small, and the compiler can assume knowledge about what segment of the computer's memory the pointer (offset) refers to. Far pointers are the most flexible because they allow the programmer to access any part of the computer's memory, without limitation to a particular segment. However, far pointers are bigger and slower because of the additional flexibility.

Based pointers are a compromise between the efficiency of near pointers and the flexibility of far pointers. With based pointers, the programmer takes responsibility to tell the compiler which segment a near pointer (offset) belongs to, but may still access segments of the computer's memory outside of the normal data segment (DGROUP). The result is a pointer type that's as small as, and almost as efficient, as a near pointer, but with most of the flexibility of a far pointer.

An object declared as a based pointer falls into one of the following categories:

  • the based pointer is in the same segment as another named object
  • the based pointer is in the segment described by another object
  • the based pointer, used as a pointer to another object of the same type (as in a linked list), refers to the same segment
  • the based pointer is an offset to no particular segment, and must be combined explicitly with a segment value to produce a valid pointer.

To support based pointers, the following keywords are provided:

  • __based
  • __segment
  • __segname
  • __self

as well as the :> operator. These keywords and this operator are described in the following sections:

Two macros, defined in malloc.h, are also provided:

  • _NULLSEG
  • _NULLOFF

They are used in a manner similar to NULL, but are used with objects declared as __segment and __based, respectively.

Segment constant based pointers and objects

A segment constant based pointer or object has its segment value based on a specific, named segment. A segment constant based object is specified as:

    [type] __based( __segname( "segment" ) ) object_name;

and a segment constant based pointer is specified as:

    [type] __based( __segname( "segment" ) ) *object-name;

where segment is the name of the segment in which the pointer or object is based. As shown above, the segment name is always specified as a string. There are three special segment names recognized by the compiler:

  • _CODE
  • _CONST
  • _DATA

The _CODE segment is the default code segment. The _CONST segment is the segment containing constant values. The _DATA segment is the default data segment. If the segment name isn't one of the three recognized names, then a segment is created with that name. If a segment constant based object is being defined, then it is placed in the named segment. If a segment constant based pointer is being defined, then it can point at objects in the named segment.

The following examples illustrate segment constant based pointers and objects.

In the first example,

    int __based( __segname( "_CODE" ) )  ival = 3;
    int __based( __segname( "_CODE" ) ) *iptr;

ival is an object that resides in the default code segment. iptr is an object that resides in the data segment (the usual place for data objects), but points at an integer that resides in the default code segment. iptr is suitable for pointing at ival.

In the second example,

    char __based( __segname( "GOODTHINGS" ) ) thing;

thing is an object that resides in the segment GOODTHINGS, which is created if it doesn't already exist. (The creation of segments is done by the linker, and is a method of grouping objects and functions. Nothing is implicitly created during the execution of the program.)

Segment object based pointers

A segment object based pointer derives its segment value from another named object. A segment object based pointer is specified as follows:

    [type] __based( segment ) *name;

where segment is an object defined as type __segment.

An object of type __segment may contain a segment value. Such an object is particularly designed for use with segment object based pointers.

The following example illustrates a segment object based pointer:

    __segment           seg;
    char __based( seg ) *cptr;

The object seg contains only a segment value. Whenever the object cptr is used to point to a character, the actual pointer value is made up of the segment value found in seg and the offset value found in cptr. The object seg might be assigned values such as the following:

  • a constant value (for example, the segment containing screen memory),
  • the result of the library function _bheapseg(),
  • the segment portion of another pointer value, by casting it to the type __segment.

Void based pointers

A void based pointer must be explicitly combined with a segment value to produce a reference to a memory location. A void based pointer doesn't infer its segment value from another object. The :> (base) operator is used to combine a segment value and a void based pointer.

For example, on a personal computer running DOS with a color monitor, the screen memory begins at segment 0xB800, offset 0. In a video text mode, to examine the first character currently displayed on the screen, the following code could be used:

extern void main()
{
    __segment          screen;
    char __based( void ) *scrptr;

    screen = 0xB800;
    scrptr = 0;
    printf( "Top left character is '%c'.\n",
            *(screen:>scrptr) );
}

The general form of the :> operator is:

    segment :> offset

where segment is an expression of type __segment, and offset is an expression of type __based( void ) *.

Self-based pointers

A self-based pointer infers its segment value from itself. It is particularly useful for structures such as linked lists, where all of the list elements are in the same segment. A self-based pointer pointing to one element may be used to access the next element, and the compiler uses the same segment as the original pointer.

The following example illustrates a function that prints the values stored in the last two members of a linked list:

struct a {
    struct a __based( __self ) *next;
    int             number;
};


extern void PrintLastTwo( struct a far *list )
{
  __segment           seg;
  struct a __based( seg ) *aptr;

  seg  = FP_SEG( list );
  aptr = FP_OFF( list );
  for( ; aptr != _NULLOFF; aptr = aptr->next ) {
    if( aptr->next == _NULLOFF ) {
      printf( "Last item is %d\n",
          aptr->number );
    } else if( aptr->next->next == _NULLOFF ) {
      printf( "Second last item is %d\n",
          aptr->number );
    }
  }
}

The argument to the function PrintLastTwo is a far pointer, pointing to a linked list structure anywhere in memory. It is assumed that all members of a particular linked list of this type reside in the same segment of the computer's memory. (Another instance of the linked list might reside entirely in a different segment.) The object seg is given the segment portion of the far pointer. The object aptr is given the offset portion, and is described as being based in the segment stored in seg.

The expression aptr->next refers to the next member of the structure stored in memory at the offset stored in aptr and the segment implied by aptr, which is the value stored in seg. So far, the behavior is no different than if next had been declared as:

    struct a *next;

The expression aptr->next->next illustrates the difference of using a self-based pointer. The first part of the expression (aptr->next) is as described above. However, using the result to point to the next member is done by using the offset value found in the next member, and combining it with the segment value of the pointer used to get to that member, which is still the segment implied by aptr, which is the value stored in seg. If next hadn't been declared using __based( __self ), then the second pointing operation would refer to the offset value found in the next member, but with the default data segment (DGROUP), which might or might not be the same segment stored in seg.

Watcom code generator

The Watcom Code Generator performs such optimizations as common subexpression elimination, global flow analysis, and so on.

In some cases, the code generator could do a better job of optimizing code if it could use more memory. This is indicated when a message such as:

    Not enough memory to optimize procedure 'xxxx'

appears on the screen as the source program is compiled. In such an event, you may wish to make more memory available to the code generator.

A special environment variable can be used to obtain memory usage information or set memory usage limits on the code generator.

The WCGMEMORY environment variable can be used to request a report of the amount of memory used by the compiler's code generator for its work area. For example,

export "WCGMEMORY=?"

When the memory amount is "?", then the code generator reports how much memory was used to generate the code.

It can also be used to instruct the compiler's code generator to allocate a fixed amount of memory for a work area. For example,

export "WCGMEMORY=128"

When the memory amount is nnn then exactly nnnK bytes are used. In the above example, 128K bytes is requested. If less than nnnK is available then the compiler quits with a fatal error message. If more than nnnK is available then only nnnK bytes are used.

In general, the more memory available to the code generator, the more optimal code it generates. Thus, for two personal computers with different amounts of memory, the code generator might produce different (although correct) object code.

If you have a software quality assurance requirement that the same results (that is, code) be produced on two different machines then you should use this feature. To generate identical code on two personal computers with different memory configurations, you must ensure that the WCGMEMORY environment variable is set identically on both machines.