PGI Inter-languauge Questions


How do you call C and C++ routines from pgfortran(ISO_C_BINDING)?

The answer is ISO_C_BINDING, a standard for Fortran-to-C interlanguage communication. Properly used, ISO_C_BINDING should work across many current Fortran compilers. However, it is limited to passing arguments to C routines, not C++. Be sure that the 'C' entry points are present in any C++ routines that are called (extern "C").

The data types in Fortran and C are different. Different Fortran compiler may treat what looks like the same data type (integer, real, pointer) differently than C and C++.

ISO_C_BINDING allows you to declare interfaces that target C routines and data types. This means you don't need to modify the C code you want to call from Fortran. In short, ISO_C_BINDING lets you treat C code as if it were just another Fortran routine.

For example, to call this C subroutine


more csub_a.c:

#include <stdio.h>
void foo(int *int_arg1, float *real_arg2)
{
	printf("The arguments passed are int_arg1=%c\n and real_arg2=%f \n",*int_arg1, *real_arg2);
 
}

from this Fortran main


more fmain_a.f90:

PROGRAM FORTRAN2C
INT_ARG1=1234
REAL_ARG2=5678.9
PRINT *,"passing integer ",INT_ARG1," and real ",REAL_ARG2
CALL FOO(INT_ARG1,REAL_ARG2)
END
 

you would write a Fortran interface record like this


added interface lines:

INTERFACE
    SUBROUTINE FOO(INT_ARG1, REAL_ARG2) BIND(C, NAME="foo")
    USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_INT,C_FLOAT
    IMPLICIT NONE
    INTEGER(C_INT) :: INT_ARG1
    REAL(C_FLOAT) :: REAL_ARG2
  END SUBROUTINE FOO
END INTERFACE


This results in


PROGRAM FORTRAN2C
INTERFACE
    SUBROUTINE FOO(INT_ARG1, REAL_ARG2) BIND(C, NAME="foo")
    USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_INT,C_FLOAT
    IMPLICIT NONE
    INTEGER(C_INT) :: INT_ARG1
    REAL(C_FLOAT) :: REAL_ARG2
  END SUBROUTINE FOO
END INTERFACE
INT_ARG1=1234
REAL_ARG2=5678.9
PRINT *,"passing integer ",INT_ARG1," and real ",REAL_ARG2
CALL FOO(INT_ARG1,REAL_ARG2)
END

We can then link successfully with any C object.


pgcc -c csub_a.c -o csub_a_pgi.o
gcc -c csub_a.c -o csub_a_gcc.o
icc -c csub_a.c -o csub_a_intel.o

pgfortran -o f2c_a_pgi  fmain_a.f90  csub_a_pgi.o
pgfortran -o f2c_a_gcc fmain_a.f90 csub_a_gcc.o
pgfortran -o f2c_a_intel fmain_a.f90 csub_a_intel.o

./f2c_a_pgi

 passing integer          1234  and real     5678.900    
The arguments passed are int_arg1=1234
 and real_arg2=5678.9 

./f2c_a_gcc

 passing integer          1234  and real     5678.900    
The arguments passed are int_arg1=1234
 and real_arg2=5678.9 

./f2c_a_intel

 passing integer          1234  and real     5678.900    
The arguments passed are int_arg1=1234
 and real_arg2=5678.9

Here's another example with more data types passed.


more fmain_b.f90


       program fort2c
use fmain2csub
       logical*1              bool1
       character              letter1
       integer*4              numint1, numint2
       real                   numfloat1
       double precision       numdoub1
       integer*2              numshor1
call cfunc (bool1, letter1, numint1, numint2, & 
        numfloat1, numdoub1, numshor1)
write( *, 100) & 
        bool1, letter1, numint1, numint2, numfloat1, & 
        numdoub1, numshor1
100    format(1x,"bool1     =  ", L2,/, &          
        " letter1   =  ", A2,/,                 &
        " numint1   = ", I5,/,                  &
        " numint2   = ", I5,/,                  &
        " numfloat1 = ", F6.1,/,                &
        " numdoub1  = ", F6.1,/,                &
        " numshor1  = ", I5,/)
       end


more csub_b.c

#include <stdio.h>
#define TRUE 0xff
#define FALSE 0
void
cfunc( bool1, letter1, numint1, numint2, numfloat1,\
        numdoub1, numshor1)
   char    *bool1, *letter1;
   int     *numint1, *numint2;
   float   *numfloat1;
   double  *numdoub1;
   short   *numshor1;
{
   *bool1 = TRUE;
   *letter1 = 'v';
   *numint1 = 11;
   *numint2 = -44;
   *numfloat1 = 39.6 ;
   *numdoub1 = 39.2 ;
   *numshor1 = 981;

}

This time we'll create an interface record and put it in a separate module called fmain2csub_b

more fmain2csub_b_mod.f90

module fmain2csub_b
INTERFACE
    subroutine cfunc ( bool1, letter1, numint1, &
    numint2, numfloat1, numdoub1, numshor1) BIND(C,NAME="cfunc")
    use, intrinsic  :: iso_c_binding, only:C_CHAR,C_BOOL, &
        C_INT,C_FLOAT,C_DOUBLE,C_SHORT
    logical(C_BOOL) ::      bool1
    character(C_CHAR) ::    letter1
    integer(C_INT) ::       numint1, numint2
    real(C_DOUBLE) ::       numdoub1
    real(C_FLOAT) ::        numfloat1
    integer(C_SHORT) ::     numshor1
    end subroutine cfunc
END INTERFACE
end module fmain2csub_b

When we build with an interface module (like fmain2csub_b) we need to link in the interface module object file as well.


pgcc csub_b.c  -c  -o csub_b_pgi.o
gcc csub_b.c -c -o csub_b_gcc.o
icc csub_b.c -c -o csub_b_intel.o


pgfortran -c fmain2csub_b_mod.f90
pgfortran -o f2c_b_pgi fmain_b.f90 csub_b_pgi.o fmain2csub_b_mod.o
pgfortran -o f2c_b_gcc fmain_b.f90 csub_b_gcc.o fmain2csub_b_mod.o
pgfortran -o f2c_b_intel fmain_b.f90 csub_b_intel.o fmain2csub_b_mod.o
    


f2c_b_pgi

 bool1     =   T
 letter1 =  v
 numint1 =  11
 numint2 = -44
 numfloat1 = 39.6
 numdoub1  =   39.2
 numshor1  =   981
    

f2c_b_gcc

 bool1     =   T
 letter1   =   v
       numint1    = 11
       numint2    = -44
       numfloat1  = 39.6
 numdoub1  =   39.2
 numshor1  =   981
  

f2c_b_intel

 bool1     =   T
 letter1   = v
 numint1   = 11
 numint2   = -44
 numfloat1 = 39.6
 numdoub1  =   39.2
 numshor1  =   981

Here is the same main program and interface module but this time with an equivalent C++ routine. Note the use of -pgcpplibs in the link step.

more cppsub_b.cpp

#define TRUE 0xff
#define FALSE 0
extern "C" {
extern void
cfunc(char *bool1, char *letter1, int *numint1,int *numint2, float *numfloat1,
        double *numdoub1, short *numshor1)
{
   *bool1 = TRUE;
        *letter1 = 'v';
        *numint1 = 11;
        *numint2  = -44;
   *numfloat1 = 39.6 ;
   *numdoub1 = 39.2 ;
   *numshor1 = 981;

}
}


g++ -c cppsub_b.cpp -o cppsub_b_g++.o
icc -c cppsub_b.cpp -o cppsub_b_intel.o
pgcpp -c cppsub_b.cpp -o cppsub_b_pgi.o
pgfortran -c fmain2csub_b_mod.f90
pgfortran -o f2cpp_b_pgi fmain_b.f90 cppsub_b_pgi.o fmain2csub_b_mod.o -pgcpplibs
pgfortran -o f2cpp_b_g++ fmain_b.f90 cppsub_b_g++.o fmain2csub_b_mod.o -pgcpplibs
pgfortran -o f2cpp_b_intel fmain_b.f90 cppsub_b_intel.o fmain2csub_b_mod.o -pgcpplibs
f2cpp_b_pgi

 bool1     =   T
 letter1   =   v
 numint1   =    11
 numint2   =   -44
 numfloat1 =   39.6
 numdoub1  =   39.2
 numshor1  =   981


f2cpp_b_g++

 bool1     =   T
 letter1   =   v
 numint1   =    11
 numint2   =   -44
 numfloat1 =   39.6
 numdoub1  =   39.2
 numshor1  =   981


f2cpp_b_intel

 bool1     =   T
 letter1   =   v
 numint1   =    11
 numint2   =   -44
 numfloat1 =   39.6
 numdoub1  =   39.2
 numshor1  =   981
 
 

How do you call Fortran routines from a C/C++ routine?

Interfacing to Fortran subroutines and functions is very similar to what we did with ISO_C_BINDING, but there is no equivalent standard defined. When calling Fortran routines from C/C++, both the called Fortran program and the calling C/C++ programs need to be altered. The steps involve:

Suppose you wish to call the following function in Fortran:

more forts_c.f90

subroutine forts ( bool1, letter1, numint1, numint2, numfloat1, numdoub1, numshor1 )
logical*1        :: bool1 
character*1      :: letter1 
integer          :: numint1 
integer          :: numint2 
real             :: numfloat1 
double precision :: numdoub1
integer*2        :: numshor1 
    bool1 = .true.
    letter1="v"
    numint1=123
    numint2=-456
    numdoub1=5432.1
    numfloat1=6789.0
    numshor1=53
    return
end

And you wish to call the Fortran routine from a C++ main program like the following:

more cmain_c.C

#include <iostream>
int main(int argc, char **argv)
{
   char          bool1;
   char          letter1;
   int           numint1, numint2;
   float         numfloat1;
   double        numdoub1;
   short         numshor1;
   int           i;
   for (i=0; i < argc; i++){
       std::cout << "main: command line arg " << i << " is " << argv[i] << std::endl;
   }
   forts(&bool1,&letter1,&numint1,&numint2,&numfloat1,
          &numdoub1,&numshor1);
   std::cout << "main: bool1=     " << (bool1?"TRUE":"FALSE") << std::endl;
   std::cout << "main: letter1=   " << letter1  << std::endl;
   std::cout << "main: numint1=   " << numint1  << std::endl;
   std::cout << "main: numint2=   " << numint2  << std::endl;
   std::cout << "main: numfloat1= " << numfloat1  << std::endl;
   std::cout << "main: numdoub1=  " << numdoub1  << std::endl;
   std::cout << "main: numshor1=  " << numshor1  << std::endl;
}

To make this work, we need to add/modify the following lines in each source file.


more cmain_c.C

#include <iostream> extern "C" { extern void forts_( char *, char *, int *,int *,float *, double *,short * ); } #if defined (_PGI_) extern "C" void pghpf_init(int *); static int zz = 0; #endif int main(int argc, char **argv) { char bool1; char letter1; int numint1, numint2; float numfloat1; double numdoub1; short numshor1; int i; #if defined (_PGI_) pghpf_init(&zz); #endif for (i=0; i < argc; i++){ std::cout << "main: command line arg " << i << " is " << argv[i] << std::endl; } forts_(&bool1,&letter1,&numint1,&numint2,&numfloat1, &numdoub1,&numshor1); std::cout << "main: bool1= " << (bool1?"TRUE":"FALSE") << std::endl; std::cout << "main: letter1= " << letter1 << std::endl; std::cout << "main: numint1= " << numint1 << std::endl; std::cout << "main: numint2= " << numint2 << std::endl; std::cout << "main: numfloat1= " << numfloat1 << std::endl; std::cout << "main: numdoub1= " << numdoub1 << std::endl; std::cout <<<"main: numshor1= " << numshor1 << std::endl; } more forts_c.f90 subroutine forts ( bool1, letter1, numint1, numint2, numfloat1, numdoub1, numshor1 ) use, intrinsic :: iso_c_binding, only:C_CHAR,C_BOOL, & C_INT,C_FLOAT,C_DOUBLE,C_SHORT logical(C_BOOL) :: bool1 character(C_CHAR) :: letter1 integer(C_INT) :: numint1, numint2 real(C_DOUBLE) :: numdoub1 real(C_FLOAT) :: numfloat1 integer(C_SHORT) :: numshor1 bool1 = .true. letter1="v" numint1=123 numint2=-456 numdoub1=5432.1 numfloat1=6789.0 numshor1=53 return end pgfortran -c forts_c.f90 pgcpp -o c2f_pgi cmain_c.C forts_c.o -pgf90libs ./c2f_pgi c2f_pgi is a program main: command line arg 0 is c2f_pgi main: command line arg 1 is is main: command line arg 2 is a main: command line arg 3 is program main: bool1= TRUE main: letter1= v main: numint1= 123 main: numint2= -456 main: numfloat1= 6789 main: numdoub1= 5432.1 main: numshor1= 53 or ifort -c forts_c.f90 icc -o c2f_intel cmain_c.C forts_c.o ./c2f_intel c2f_intel is another program main: command line arg 0 is c2f_intel main: command line arg 1 is is main: command line arg 2 is another main: command line arg 3 is program main: bool1= TRUE main: letter1= v main: numint1= 123 main: numint2= -456 main: numfloat1= 6789 main: numdoub1= 5432.1 main: numshor1= 53
 

Can you link programs compiled with pgcpp and programs compiled with g++?

pgcpp has not been able to link with other C++ object files, because pgcpp uses a different name-mangling algorithm. That has changed with the 13.* release compilers. A new compiler, pgc++, uses the same algorithm as g++.

For example, here is a C++ main routine.

more  cppmain_d.cc

#include 
extern double do_triad(double *a, double *b, double *c, double *d, int len, int rep);
using namespace std;
    
int main(int argc, char** argv) {
 
const int length=10000;
  
double *a = new double[length];
double *b = new double[length];
double *c = new double[length];
double *d = new double[length];
    
for(int i=0; i< length; ++i)
a[i]=b[i]=c[i]=d[i]=1.0;
 
do_triad(a,b,c,d,length,2);
    
delete [] a;
delete [] b;
delete [] c;
delete [] d;

return 0;
}

And here is a procedure it calls

more  cppsub_d.cc

#include 
using namespace std;


double do_triad(double *a, double *b, double *c, double *d,
int len, int rep) {
int i,j;
{
for(j=0;j<rep;j++){
for(i=0;i<len;++i)
a[i]=b[i]+c[i]*d[i];
cerr << j << " of " << rep << endl;
}
}
return 0.0;
}

With pgcpp linking fails, but with pgc++ things are better.

g++ -c cppsub_d.cc
pgcpp -o pgcpp2g++  cppmain_d.cc cppsub_d.o

cppmain_d.cc:
cppmain_d.o: In function `main':
/home/tull/xfer/13.0/examples/C2C/./cppmain_d.cc:17: undefined reference to `do_triad__FPdN31iT5'
cppsub_d.o: In function `do_triad(double*, double*, double*, double*, int, int)':
cppsub_d.cc:(.text+0x9b): undefined reference to `std::cerr'
cppsub_d.cc:(.text+0xa0): undefined reference to `std::ostream::operator<<(int)'
cppsub_d.cc:(.text+0xad): undefined reference to `std::basic_ostream<char, std::char_traits
<char> >& std::operator<<<std::char_traits<char> >(std::basic_ostreami
<char, std::char_traits<char> >&, char const*)'
cppsub_d.cc:(.text+0xba): undefined reference to `std::ostream::operator<<(int)'
cppsub_d.cc:(.text+0xbf): undefined reference to `std::basic_ostream<char, std::char_traits<
char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream
<char, std::char_traits<char> >&)'
cppsub_d.cc:(.text+0xc7): undefined reference to `std::ostream::operator
<<(std::ostream& (*)(std::ostream&))'
cppsub_d.o: In function `__static_initialization_and_destruction_0(int, int)':
cppsub_d.cc:(.text+0x113): undefined reference to `std::ios_base::Init::Init()'
cppsub_d.cc:(.text+0x118): undefined reference to `std::ios_base::Init::~Init()'



pgc++ -o pgc++2g++ cppmain_d.cc cppsub_d.o 
cppmain_d.cc:


./pgc++2g++ 
0 of 2
1 of 2
Click me