Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

C / Fortran Interoperability With External Fortran Function passing and Struct type arguments

0.00/5 (No votes)
11 Sep 2012CPOL2 min read 18.1K  
Showing how to pass a Fortran function into C and using a compatible C-Struct as argument.

Introduction

This example shows off many features of Fortran / C interoperability, including:

  • the ISO_C_BINDING since Fortran 2003, allowing C-Fortran working together smoothly.
  • Interoperate between C struct and Fortran Derived Type.
  • Use of C "void * p" parameter.
  • Passing Fortran function as an Argument to a C function.
  • Use of Fortran Interfaces and Modules.
  • Abstract C function, Abstract Fortran function.
  • Using C's Struct or Fortran's Derived Type as arguments.

Background

A brief description of the example is that there is a function called gsl_UVMIF(). One of its parameters is another function called with interface fnExt(double, void*). In actual usage, fnExt() is an abstract representation of Fortran functions which can be any of the following:

  • f1(x, dpar3)
  • f2(x, dpar5)
  • f3(x, dpar10)

... where x is double precision, and dpar3, dpar5, dpar10 and whatever are a list of Fortran double precision parameters defined as Derived types, analogous to C-struct.

From the highest level, there is a Fortran driver program or code that will call C's gsl_UVMIF(), via a Fortran interface fgsl_UVMIF(). The gsl_UVMIF() will call Fortran functions of the form fnExt(x, {parameters}).

Using the code

The code is given as follows. At the core of the usage is the following C code.

C++
#include "head.h"
void gsl_UVMIF(double (*fnExt)(double, void*) , dPar3 *adPar3, double blah, ....) {
    ....
    .. fnExt            // using function
    .. *adPar3       // using variables
}

In the header file head.h, define the C-struct which must have an exact correspondence to Fortran Derived Type. Note there is a 3-parameter and 10-parameter structure. The prototype of the function is also declared.

C++
extern "C"{
    // Structure of 3 parameter values
    typedef struct {
        double a,b,c;
    } dPar3;
    // Structure of 10 parameter values
    typedef struct {
        double a,b,c,d,e, f,g,h,i,j;
    } dPar10;

    __declspec(dllexport) void gsl_UVMIF(double (*fnExt)(double, void*) , dPar3 *adPar3, double blah);
}

The details of the function fnExt are defined on the Fortran side. But before doing that, we also need to define the list of parameters as Fortran Derived Type which must exactly match the C struct data type. Define the structure in a module of its own.

C++
MODULE extStructures
    USE, INTRINSIC :: ISO_C_BINDING
    implicit none
    TYPE, BIND(C) :: dPar3
        REAL(C_DOUBLE) :: a,b,c
    END TYPE dPar3
    TYPE, BIND(C) :: dPar10
        REAL(C_DOUBLE) :: a,b,c,d,e, f,g,h,i,j
    END TYPE dPar3
END MODULE extStructures

Define the Fortran interface called fgsl_UVMIF() which connects directly to gsl_UVMIF(). It uses the parameter structure dPar3 defined in extStructures above.

C++
MODULE mod_f90
    USE extStructures    ! where parameter structures are defined above.
    implicit none

    INTERFACE
        SUBROUTINE fgsl_UVMIF(fnExt , param3, blah, ....)  BIND(C, NAME='gsl_UVMIF') 
            USE, INTRINSIC :: ISO_C_BINDING     ! New Fortran 2003 standard
            USE extStructures
            IMPLICIT NONE 

            REAL(C_DOUBLE) :: fnExt           ! External Fortran function defined by user.
            EXTERNAL fnExt

            TYPE(dPar3) :: param3             ! parameter with 3 doubles struct.
            .....
        END SUBROUTINE fgsl_UVMIF
    END INTERFACE
END MODULE mod_f90

Here is one example of the Fortran function with three parameters. This code is defined outside any Fortran MODULE or PROGRAM. So the list of parameters, dpar, is passed in as a Fortran structure.

FORTRAN
REAL(C_DOUBLE) FUNCTION Fext_dUVMIF(X, dpar)
    USE, INTRINSIC :: ISO_C_BINDING     ! New Fortran 2003 standard
    use mod_f90, only : dPar3
    REAL(C_DOUBLE), VALUE :: X
    TYPE (dPar3) dpar
    Fext_dUVMIF = dPar%a*X*X + dPar%b*X + dPar%c     ! y = ax^2 + bx + c
END FUNCTION Fext_dUVMIF

The main Fortran driver code is below.

FORTRAN
PROGRAM main
    USE mod_f90, only : fgsl_UVMIF        
    USE, INTRINSIC :: ISO_C_BINDING
    real(C_DOUBLE) :: Fext_dUVMIF      ! External functions
    external Fext_dUVMIF
    TYPE(dPar3) :: param3              ! variables of the ext function

    param3%a = 1.0d0
    param3%b = -4.0d0
    param3%c = 5.0d0
    CALL fgsl_UVMIF(Fext_dUVMIF, param3, blah, ...) 
END PROGRAM main

The call stack is roughly main() -> fgsl_UVMIF() -> gsl_UVMIF() -> *fnExt() -> Fext_dUVMIF().

Points of Interest 

Key lessons were passing in a Fortran function as an argument of another C function, which is called within another Fortran code. Also passing a list of parameters in C Struct format using it's Fortran equivalent of Derived Type.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)