In the context of CFX user Fortran, how can I call C or C++ functions from Fortran routines?




There quite a few issues to watch out for, which may or may not be relevent, depending on exactly what you are doing. Most apply to calling C or C++ functions from any Fortan routine, whilst some are specific to CFX user Fortran. These are discussed below. Working example files are also provided.


FUNCTION RETURN TYPE AND ARGUMENTS

A C function must be declared of type 'void' if it is to be callable as a Fortran SUBROUTINE. If a C function is to be used as a Fortran FUNCTION, it needs to be declared with the relevant type of the returned value.

The Fortran arguments translate in a fairly predictable way, apart from CHARACTER arguments, which are discussed later.

CHARACTER ----> char* and int (see below)
DOUBLE PRECISION ----> double*
INTEGER ----> int*
LOGICAL ----> int*
REAL ----> float*

The include file fortran_types.h is intended to be used in a C function and provides a ready made definition of Fortran data types for use in C.

Difficulties can arise when the Fortran arguments passed to a C function are of type CHARACTER. The easiest way to avoid such problems is to cheat and just not pass any CHARACTER arguments! When it really is necessary to use CHARACTER arguments, the first issue that has to be taken into account is that a CHARACTER argument in Fortran translates to two arguments in C (a 'char*' and an 'int', specifying the length of the string). To complicate matters further, the ordering of these C arguments differs, depending on the operating system used. For example, a Fortran argument list consisting of 3 CHARACTER variables would be translated to (char*,int,char*,int,char*,int) on one OS and (char*,char*,char*,int,int,int) on another.

Clearly it is far better to produce platform independent code and judicial use of preprocessor macros will allow this ordering issue to be worked around. This technique is demonstrated in example2. If there is only one CHARACTER argument, a good cheat is tomake this the last Fortran argument so that the ordering will not differ between operating systems.

A further potential pitfall with CHARACTER data is that many of the functions in the standard C libraries expect strings to be null terminated (with a ), but Fortran CHARACTER strings are unterminated and padded with spaces. Therefore, it is usually necessary to 'fix' any strings passed from Fortran before they are used with any such functions.It may also be useful to strip any trailing spaces. All of this can easily be done in a C function. Example 2 demonstrates how to do this.

It is feasible to create C functions which are called directly from the solver without using any Fortran at all. However, in practice, it may well be more practical to create a Fortran wrapper routine, which in turn calls C function(s). This hybrid approach is useful where MMS access is necessary, as it would not be trivial to access the CFX Memory Management System (MMS) from within a C function. Instead, the required MMS calls can be made in the Fortran wrapper and data passed to/from the C function(s).

If you have some pre-existing C code, which you wish to use, you will almost certainly need to write one or more wrapper functions which are called from Fortran.


SYMBOL NAME MANGLING ISSUES

All subroutines/functions, whether Fortran or C end up as symbols in the object code/library. The names of these symbols are derived from the function names. Unfortunately the way in which symbols are named differs depending on the compiler used and especially between different operating systems. In most cases, one or more underscores are added to the name. In some cases, an underscore is added at the the begining of the name, in others at the end. This is known as 'name mangling' or 'decoration'. Where the original name contained underscores, things can get even more nasty.

Obviously, when code of one language is being compiled, everything (should) be fine as the same compiler is being used throughout and it mangles all the names in the same way. This process is usually invisible to the user. When two different compilers are used (e.g. a Fortran compiler and a C compiler), the object code that they produce is eventually put together to make a shared library. One way of ensuring compatibility, is to name the functions in one of the languages (usually the C functions) in a way that the resulting symbols are compatible with those of the other language. This can be done manually, but in the interests of producing platform independent code, this is usually achieved using pre-processor directives. Definitions of NT_DECL, UNDERSCORE, etc. are provided by the include file cfx_fproto.h. As an example of usage (from example 1), the fortran callable function name USR_WRITE_TRNINFO to be defined as, for example:

#if defined(NT_DECL)
# define USR_WRITE_TRNINFO USR_WRITE_TRNINFO
#elif defined(UNDERSCORE_DBL)
# define USR_WRITE_TRNINFO usr_write_trninfo__
#elif defined(UNDERSCORE)
#define USR_WRITE_TRNINFO usr_write_trninfo_
#else
# define USR_WRITE_TRNINFO usr_write_trninfo
#endif

STDCALL is also defined in cfx_fproto.h and this is used prior to the function name. e.g.

void STDCALL USR_WRITE_TRNINFO(Fint *ntstep, Fint *atstep, Fint *ncloop)

CREATING SHARED LIBRARIES

The cfx5mkext script accepts C source files in the list of source files to be compiled. These are automatically sent to the C compiler, just as any Fortran routines are sent to the Fortran compiler. cfx5mkext then runs the linker, creating a shared library from all object code. Alternatively, the C functions may be compiled separately, since cfx5mkext will accept object files in its input list.

Beware of pre-compiled libraries . These must be object compatible with the shared library. If possible, compile everything from source.


C++ ISSUES

If C++ functions are to be included, it will be necessa





Show Form
No comments yet. Be the first to add a comment!