Silverfrost Logo About Us | Contact Us
 
Calling C/C++ from FTN95

Calling C/C++ from FTN95

The C_EXTERNAL and STDCALL keywords give the Fortran programmer the ability to call C/C++ routines, forcing the compiler to generate extra code to handle some of the data conversions. C_EXTERNAL is used when the "cdecl" calling convention is used by C/C++ (e.g. the Win16 Windows API). STDCALL is used when the "stdcall" calling convention is used by C/C++ (e.g. the Win32 Windows API).

An example of a type requiring data conversion is the string data type. As we have already noted, C/C++ strings are NULL terminated, whilst Fortran strings are fixed length, padded with spaces. The C_EXTERNAL and STDCALL declarations for a function inform the compiler that the function or subroutine is written in C/C++ and is external to this program module. If the function uses a string data type, code is planted to generate a C/C++ style string before entering the C/C++ function. Code is also generated after the function call to convert the C style string back into a Fortran string. This frees the C/C++ programmer from the additional complexities of providing the conversion code. It also means that a Fortran programmer can call a third party library without converting all string references into C/C++ strings before calling an external routine.

The C_EXTERNAL declaration has the following form (for STDCALL, simply replace C_EXTERNAL by STDCALL or refer to the ClearWin+ User's Guide).

C_EXTERNAL name ['alias'] [(desc , ...)] [:restype]

where:

name
is the name to be used to call the function in the Fortran program.

alias
is the external name used for the routine (i.e. the name that is used in the C/C++ source code). This appears in single quotes and is case-sensitive.

desc
describes the arguments that the routine receives and/or returns.

restype
identifies the routine as a function and describes the type of the object returned; this may be any function type other than CHARACTER.

Some examples of valid C_EXTERNAL declarations are given below.

Example 1

C_EXTERNAL SUB

This describes an external C/C++ routine which accepts no arguments and returns no result. The corresponding C/C++ declaration would be

extern "C" void SUB(void)
{
.....
}

and the function would be called by the Fortran statement CALL SUB.

Example 2

C_EXTERNAL WRITE 'WriteFile' : INTEGER*4

This describes a C/C++ routine called WriteFile which accepts no arguments, but returns an integer result. The routine is called from a Fortran program by the statements

INTEGER(KIND=3)::RESULT
RESULT = WRITE()
.....

The C/C++ code could have the following form

int WriteFile(void)
{
.....
}

Before continuing, we must first examine the possible forms of the desc parameter and the restype part of the declaration in more detail.

The desc parameter allows the programmer to over ride the default linkage of arguments. If you use these argument descriptors, the number of arguments in each occurrence of a call must agree with the number of descriptors in the routine definition. desc may be any one of: REF, VAL, STRING, INSTRING, OUTSTRING.

The VAL specifier may only be used for numeric and logical scalars. Instead of pushing the address of the value onto the stack, the actual value is pushed. This allows C/C++ functions to use its arguments as local variables. The REF specifier may be used with any Fortran object. This forces the Fortran program to push the address of the object onto the stack. This is the default action but should be used as a matter of good programming practice to allow the compiler to check for the correct usage of external functions. So we may additionally have the following descriptions:

C_EXTERNAL UNIX_WRITE 'write' (VAL, REF, VAL): INTEGER*4

for the UNIX low-level write function. This is defined in C/C++ as

int write(int handle, void *buffer, int amount)
{
.....
}

but it now looks to the Fortran program as if it was a Fortran function declared as:

FUNCTION UNIX_WRITE(HANDLE, BUFFER, BUFSIZ)
INTEGER(KIND=3)::HANDLE, BUFFER, BUFSIZ, UNIX_WRITE

The remaining three types, STRING, INSTRING, OUTSTRING, are a little more complicated. All three are used to describe a string object. Each one forces the compiler to do differing amounts of work before the function call is made. As we have already seen from the discussion at the start of this section, the compiler can be forced to convert strings from Fortran strings to C/C++ strings and visa-versa. This is the default action and is equivalent to the STRING descriptor. However this causes an unnecessary overhead if and argument is to be used for either input to a function or output from a function but not both. In this case the INSTRING and OUTSTRING maybe used. This saves the redundant copy operation from taking place. It is also possible to restrict the length of the temporary variable used to store the string which is actually used in the function call. The default length of the string is the length of the CHARACTER array or 256 bytes in the case of a CHARACTER(LEN=*) array. This is done by specifying the length of the string in parentheses after the descriptor. Further examples of the C_EXTERNAL are:

C_EXTERNAL COPY_STRING 'strcpy' (OUTSTRING,INSTRING): INTEGER*4
C_EXTERNAL STRNCPY 'strcat' (STRING, INSTRING(40)): INTEGER*4

where strcpy and strcat are the standard C library functions.

 

 

Copyright © 1999-2017 Silverfrost Limited