E THE NDF_ LIBRARY C INTERFACE

 E.1 Conventions Used in the C Interface
 E.2 Using the C Interface – a Few Peculiarities
 E.3 Building C Applications

E.1 Conventions Used in the C Interface

Although the NDF_ library is implemented mainly in Fortran, an interface is provided which allows it to be called from programs written in C (see Appendix F for a list of the C functions available). Of course, most of the rest of this document describes the Fortran interface, but it is quite easy to translate the descriptions and examples into C once you are aware of the conventions used. The following notes are intended to assist with this:

(1)
C function names are derived from the corresponding Fortran routine names by removing the underscore, converting to lower case and then capitalising the fourth character. Thus, the Fortran routine NDF_ABCDEF becomes the C function ndfAbcdef.
(2)
A single header file “ndf.h” is provided to define the C interface. This contains function prototypes together with C equivalent definitions for all the symbolic constants and error codes used by the Fortran version. All the constants use exactly the same names (in upper case) as in Fortran. It is recommended that you always include this file, since some functions may be implemented via C macros and will therefore only be available in this way.
(3)
The data types used in Fortran and C for routine arguments and returned values are related as follows:


Fortran Type C Type




DOUBLE PRECISION double
REAL float
INTEGER int
LOGICAL int
CHARACTER char


Note that the C interface uses “int” for both the Fortran INTEGER and LOGICAL data types, but interprets the latter according to whether the C integer is non-zero or zero (corresponding with Fortran’s .TRUE. and .FALSE. respectively).

(4)
Input scalar arguments are always passed in C by value.
(5)
Non-scalar input arguments (typically strings) are passed in C by pointer, qualified by “const” as appropriate.
(6)
All output arguments are passed in C by pointer. In the case of output array arguments, the caller must ensure that adequate space has been allocated to receive the returned values (also see the notes on passing character strings below). The C array notation “[ ]” is used in the function prototypes to indicate where a returned value is an array rather than a scalar value.
(7)
All C strings should be null-terminated.
(8)
Whenever a C string value is to be returned (via a argument “arg”, say, with type char ), the argument is followed in C by an associated integer length argument (“arg_length”) which does not appear in the Fortran version of the routine. This additional argument specifies the length of the character array allocated to receive the string, including the final null. Truncation of the returned string will occur if this length would be exceeded.
(9)
An array of character strings is passed in C as an array of pointers to null-terminated strings (like the standard argument vector passed to the C “main” function).
(10)
A few functions pass HDS locators. These are stored in character arrays, but they are not strings and hence are not null-terminated. The number of array elements required is given by the HDS constant DAT__SZLOC.
(11)
Strings which are used to describe the data type of NDF components must contain the same text in C as in Fortran. Hence, you should continue to use “_REAL”, for example, (and not “_FLOAT”) when specifying the data type of a new NDF.
(12)
When mapping NDF array components, the C interface will usually return a pointer to void, reflecting the fact that the data type is determined at run time and is therefore not known to the mapping function. To access the mapped data, you should cast this pointer to the appropriate pointer type before use, as follows:


Mapped Data Type C Pointer Cast




_DOUBLE (double )
_REAL (float )
_INTEGER (int )
_WORD (short )
_UWORD (unsigned short )
_BYTE (signed char )
_UBYTE (unsigned char )


(13)
Remember that the data storage order used when mapping multi-dimensional array data follows the Fortran convention (where the first array index varies most rapidly) and not the C convention (where the final array index varies most rapidly).
(14)
Several functions pass pointers to Objects defined by the AST library (SUN/211) for handling world coordinate system information. These use the same C argument passing conventions for these pointers as used in the AST library itself.

E.2 Using the C Interface – a Few Peculiarities

There are several small peculiarities of the C interface to the NDF_ library which arise from the fact that the library itself is implemented mainly in Fortran. You must be aware of these when writing software in C, otherwise your program may give unpredictable results (including crashing):

Initialisation.

By default, the NDF_ library depends on the main routine in your program being written in Fortran. This is because Fortran compilers generally arrange for certain initialisation steps to be performed in the main routine. If this does not happen (because the main routine is written in C) the library may malfunction. Typically, problems occur when updating NDF history information (see §22), so you might not recognise immediately that your application is affected.

One solution is to write your main routine in Fortran. This routine need do no more than call a C function to do all the work, and facilities which allow Fortran to call C in a portable way are available in the CNF library (SUN/209). Note that applications written using the ADAM programming environment are automatically structured in this way, with a Fortran main routine, so that using the “alink” command to build your application (see §24.2) will avoid any difficulty.

However, to avoid problems in standalone (non-ADAM) applications which have a C “main” function, you MUST invoke the C-specific ndfInit initialisation function as follows, before using any other facilities from the NDF_ library:

  #include "ndf.h"                 /* Define NDF interface */
  #include "sae_par.h"             /* Define SAI__OK */
  
  int main( int argc, char *argv[] ) {
     int status = SAI__OK;
  
  /* Initialise the NDF_ library for use from a C main routine. */
     ndfInit( argc, argv, &status );
  
     ...
  
  }

Note that you should pass to ndfInit the argument count and argument vector which the operating system passes to your program via the standard arguments of the C “main” function. If, for any reason, this argument information is not accessible, then you should pass an argument count of zero (the argument vector is then ignored). This tells the NDF_ library that argument information is not available.

When calling NDF from a C main there may be additional initialisations necessary to properly setup the Fortran Runtime, you will need to perform these tasks for yourself, unless you are developing within the Starlink Build System (see SSN/78), in which case you should also invoke the STAR_INITIALISE_FORTRAN macro within your main function, sometime before invoking ndfInit.

Input/output.

In general, it is not advisable to mix input/output (I/O) operations performed in C and Fortran on the same file within the same application. If you try to do so, you may find that output becomes jumbled. On input, you might also find that data get read several times, or maybe not at all.

This means that it is usually best to decide which language will perform I/O throughout your program and stick with that choice. Fortunately, the NDF_ library does not perform input and generates no significant output,30 apart from error messages. However, it is designed to integrate with the MERS message and error reporting system (SUN/104), for example through its use of message tokens, and it uses MERS for reporting its own errors. MERS, in turn, uses Fortran to output these error messages (in ADAM applications it may also pass output to other tasks for delivery).

This means that if you are writing standalone software in C which calls the NDF_ library, then you should be able to use the standard I/O functions provided by C for most purposes quite safely, so long as you do so consistently. However, you should use MERS (or EMS, see SSN/4) for reporting errors, in order to be compatible with the error reporting performed by the NDF_ library.

In ADAM applications, you should always use MERS (or EMS) for both textual output intended for the user and error messages, otherwise output may be lost if the application is not run from the normal command line.

E.3 Building C Applications

Building C applications which call the NDF_ library differs very little from building Fortran applications and is covered in §24.

30The function ndfHecho (which calls the Fortran routine NDF_HECHO) is the only routine that explicitly generates output, which it does using Fortran. However, this is really just a demonstration function and is easily replaced by your own C equivalent if necessary.