Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
Announcements
FPGA community forums and blogs on community.intel.com are migrating to the new Altera Community and are read-only. For urgent support needs during this transition, please visit the FPGA Design Resources page or contact an Altera Authorized Distributor.
29286 Discussions

C calling Fortran where an argument is a derived type

Adrian_F_1
Beginner
1,588 Views

I have a Fortran subroutine which I want to call from C.  One of the arguments is a derived type such as:

type :: pprop
  real*8 :: x1
  real*8 :: x2
  real*8 :: x3
  real*8 :: x4
end type pprop

On the C side, what can I map this to?  Will an array work as all the components are of the same type?  Or do I have to map to a struct?

0 Kudos
7 Replies
Steven_L_Intel1
Employee
1,588 Views

Technically, you can't map it to anything in C. If you wrote it this way:

use, intrinsic :: iso_c_binding
...

type,bind(C) :: pprop
  real(C_DOUBLE) :: x1
  real(C_DOUBLE) :: x2
  real(C_DOUBLE) :: x3
  real(C_DOUBLE) :: x4
end type pprop

Then you could use a struct of four doubles or an array of four doubles. The problem with the way you wrote it is that the Fortran standard allows the compiler to rearrange or pad the components. Adding bind(C) tells Fortran to lay out the type as C would. Now, the C compiler might choose to pad as well, meaning that using an array is not 100% guaranteed, but I expect this will work.

real*8 is an extension and it's better when writing interoperable code to use the defined kind constants.

0 Kudos
mecej4
Honored Contributor III
1,588 Views

Whether you use an array or a struct on the C side depends on what you wish to do in the C code. With the subroutine

subroutine Fsub(p) BIND(C,NAME='fSub')
use, intrinsic :: ISO_C_BINDING
type, BIND(C) :: pprop
   real(C_DOUBLE) :: x1,x2,x3,x4
end type pprop
!
type(pprop) :: p
!
p=pprop(1d0,1d-3,2d0,2d3)
return
end subroutine

you can use the C main program

#include <stdio.h>

main(){
struct {double x1,x2,x3,x4;
       } p;
double y[4];
fSub(&p);
printf("%e %e %e %e\n",p.x1,p.x2,p.x3,p.x4);
fSub(y);
printf("%e %e %e %e\n",y[0],y[1],y[2],y[3]);
}

which calls fSub() both ways.

0 Kudos
Adrian_F_1
Beginner
1,588 Views

I decided to try the struct avenue first.  My end goal is actually Fortran callng C which then calls Fortran.  I was asking above about the 1st half,  of this however it turns out I haven't got the 2nd part working which I am looking now.  I have a C file:

#include <stdlib.h>

struct {
  double enth;
  double entr;
  double dens;
  double visc;
  double ther;
  double surf;
  double cp  ;
  double cv  ;
  double cs  ;
  double sos ;
  double z   ;
} pprop;

extern void co2_drv(            int*    opt,
                                double* pres,
                                double* temp,
                                double* rg,
                                double* qual,
                                double* hf,
                                double* sf,
                                pprop*  vap,   // this is line 27
                                pprop*  liq,
                                int*    err);

_declspec(dllexport) int CO2_driver(
                                int*    opt,
                                double* pres,
                                double* temp,
                                double* qual,
                                double* hf,
                                double* sf,
                                pprop*  vap,
                                pprop*  liq)

{
    int err;
    double rg = -1.0;

    co2_drv( opt,
             pres,
             temp,
             &rg,
             qual,
             hf,
             sf,
             vap,
             liq,
             &err);

    return (err);
}

which is supposed to call the Fortran function co2_drv:

      subroutine co2_drv(opt, pres, temp, rg, qual, hf, sf, vap, liq, err)

      implicit none

      type, bind(C) :: pprop
        real*8 :: enth
        real*8 :: entr
        real*8 :: dens
        real*8 :: visc
        real*8 :: ther
        real*8 :: surf
        real*8 :: cp  
        real*8 :: cv  
        real*8 :: cs  
        real*8 :: sos 
        real*8 :: z   
      end type pprop

      integer    , intent(in   ) :: opt 
      real*8     , intent(inout) :: pres  (kPa)
      real*8     , intent(inout) :: temp  (K)
      real*8     , intent(in   ) :: rg    (kmol/m3)
      real*8     , intent(inout) :: qual
      real*8     , intent(inout) :: hf    (kJ/kg)
      real*8     , intent(inout) :: sf    (kJ/kg.K)
      type(pprop), intent(out  ) :: vap 
      type(pprop), intent(out  ) :: liq 
      integer    , intent(out  ) :: err 
...

However I must have a syntax issue as I get:

CO2PVTWrapper.c(27): error C2143: syntax error : missing ')' before '*'
CO2PVTWrapper.c(27): error C2081: 'pprop' : name in formal parameter list illegal
CO2PVTWrapper.c(27): error C2143: syntax error : missing '{' before '*'
CO2PVTWrapper.c(28): error C2371: 'pprop' : redefinition; different basic types
          CO2PVTWrapper.c(18) : see declaration of 'pprop'
CO2PVTWrapper.c(28): error C2143: syntax error : missing ';' before '*'
CO2PVTWrapper.c(29): error C2059: syntax error : 'type'
CO2PVTWrapper.c(29): error C2059: syntax error : ')'
CO2PVTWrapper.c(38): error C2143: syntax error : missing ')' before '*'
CO2PVTWrapper.c(38): error C2081: 'pprop' : name in formal parameter list illegal
CO2PVTWrapper.c(38): error C2143: syntax error : missing '{' before '*'
CO2PVTWrapper.c(39): error C2371: 'pprop' : redefinition; different basic types
          CO2PVTWrapper.c(18) : see declaration of 'pprop'
CO2PVTWrapper.c(39): error C2143: syntax error : missing ';' before '*'
CO2PVTWrapper.c(39): error C2059: syntax error : ')'
CO2PVTWrapper.c(41): error C2054: expected '(' to follow 'liq'

I'm not a C programmer...

Once I get this bit working I'll have to address the 1st half again, ie. Fortran calling the above C function.  I hear you about real*8, I will correct that.

0 Kudos
Arjen_Markus
Honored Contributor II
1,588 Views

You may want to define a type pprop:

typedef struct {...} pprop;

otherwise you would have to use: struct pprop* vap,

Regards,

Arjen

 

0 Kudos
Adrian_F_1
Beginner
1,588 Views

Thanks that fixed my syntax errors, however I'm getting an unresolved external:

CO2PVTWrapper.obj : error LNK2019: unresolved external symbol _co2_drv referenced in function _CO2_driver

The Fortran function is in a library co2_thermo.lib which is included in the Additional Dependencies section of the C project.  Am I missing something in my syntax?

0 Kudos
Adrian_F_1
Beginner
1,588 Views

Sorry I see I need caps for co2_drv in the C function.  Now that that part links, let me try the first bit again, Fortran calling C.

0 Kudos
FortranFan
Honored Contributor III
1,588 Views

Adrian F. wrote:

Thanks that fixed my syntax errors, however I'm getting an unresolved external:

CO2PVTWrapper.obj : error LNK2019: unresolved external symbol _co2_drv referenced in function _CO2_driver

The Fortran function is in a library co2_thermo.lib which is included in the Additional Dependencies section of the C project.  Am I missing something in my syntax?

See BIND(C, Name'..) aspect in mecej4's post above.

See Dr Fortran's blog on recent books on Fortran: https://software.intel.com/en-us/blogs/2013/12/30/doctor-fortran-in-its-a-modern-fortran-world.  If you don't have a copy already, get Metcalf et al.'s Modern Fortran Explained; read the section on C interoperability.

 

0 Kudos
Reply