2.2 ANSI
C
ANSI
C is used for all VICAR programs. This section describes what needs to be done
within the VICAR environment in order to successfully use ANSI C. Read it
before using ANSI C with VICAR.
Function
prototypes allow the compiler to check the number and types of arguments to
subroutines. If they don't match, a warning is issued. This feature will catch
many common programming errors. You will undoubtedly find many more compiler
warnings if you use prototypes, but that is a good thing. Most of those
warnings are errors in the code, or questionable coding practices. Take heed
and your code quality should improve.
A
function with a prototype does not suffer default argument promotion; it can,
for example, pass a float as a float, rather than promoting it to a double.
This can be more efficient in some cases. This is also a danger, however; if a
subroutine is intended to be called from non-ANSI code (which includes almost
all SUBLIB routines), then be careful to use only argument types that won't be
promoted in the absence of a prototype in order to remain compatible. Any ANSI
C reference should be able to provide more details.
Program
writers should be careful to include the appropriate include files, in order to
get the function prototypes for each subroutine package. The prototype file for
the RTL is called “zvproto.h”. You should include this file in any
routines that make use of the RTL.
Subroutine
writers should provide an include file that defines the prototypes for the
functions in your subroutine package. This include file may be very short if
you have only one function, or quite long for a big package. This include file
should generally be the same as that containing public definitions, although it
can be separate if need be. For example, the RTL include file,
”zvproto.h”, has only prototypes.
When
you declare prototypes, there are three things you need to do: provide a
wrapper, use _NO_PROT O, and support C++. The first thing is to protect your
file against multiple inclusions. A “wrapper” define and #ifndef
should be put around the entire file. This way, if the file is included more
than once, it will not be compiled the second time.
Second,
your include file may be used in a non-ANSI environment. For this reason, it is
important to protect the prototypes with the _NO_PROTO symbol. The _NO_PROTO
symbol is declared in
xvmaininc.h.
If _NO_PROTO is not defined, use prototypes. If it is, use the old-style
declarations instead.
Third,
allow support of C++. C++ uses the same prototype mechanism as ANSI C, but
prototypes are required. In order to cross languages and link to a C module,
however, the C declaration must be enclosed in “
extern
“C” {
...}
“. By putting this wrapper around your entire include file, and providing
prototypes for every function, you can make your subroutines available from C++.
Here
is an example which demonstrates all three techniques. Use it as a template for
prototype includes.
/* xyz.h: include file for the XYZ subroutine package. */
#ifndef _XYZ_H
#define _XYZ_H
#include "xvmaininc.h" /* not needed if you can guarantee the caller */
/* has already included it */
#ifdef __cplusplus
extern "C" {
#endif
#define ONE_OF_MY_FLAGS 10 /* your common definitions go here */
#ifndef _NO_PROTO
double xyz(int a, char *b, double d); /* your prototypes go here */
#else /* _NO_PROTO */
double xyz(); /* non-prototype decls go here */
#endif /* _NO_PROTO */
#ifdef __cplusplus
} /* end extern "C" */
#endif
#endif /* XYZ_H */
There
are some pitfalls using ANSI C prototypes. First, do
not
put prototypes on any FORTRAN bridge routines. FORTRAN doesn't understand them,
and the prototypes interfere with the FORTRAN string passing mechanisms. Use
the old-style declarations instead. Prototypes can and should exist for the C
function called
by
the bridge.
Second,
variadic functions (those with variable arguments) are a problem if you are
intending to have both ANSI and non-ANSI callers. The ANSI C standard states
that variadic functions must use “
...“
in the prototype and function header. The implication is that compiler is free
to use a different argument-passing mechanism for these functions (although
current compilers don't seem to do this).
The
old form of using varying arguments, <varargs.h> doesn't have the risk
of a different argument-passing mechanism. Plus, if a variadic function is
going to be called by non-ANSI C code, the...“ form is not available. For
this reason, and to be safe, you should use <varargs.h> instead of
<stdarg.h>, and not the ...“ form. See the comments in zvproto.h
for more details. This should not cause much of a problem in VICAR code because
varying arguments may only be used in certain limited situations.