Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Call Fortran from C

lxh37
Beginner
5,869 Views

Hi,

I am trying to learn how to call Fortran from C, I am able to create .so file from C and call it from Ruby On Rails,

the C code is:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

long add(long maxn, double delta, double conf, char *errMsg)
{
        long ERR_MSG_LENGTH, answer;
        unsigned i;
        char * iarr;

       iarr = "Hello!";

        answer = (long) (maxn+delta+conf);

        for (i=0; ((char) iarr) != '\0'; ++i) errMsg = (char) iarr;
        errMsg =  '\0';
        return answer;
}

but when I tried to call Fortran add function from C, I have trouble:

add.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern addsize(long maxn, double delta, double conf, long * sum);

long add(long maxn, double delta, double conf, char *errMsg)
{
        long ERR_MSG_LENGTH, answer;
        unsigned i;
        char * iarr;

        iarr = "Hello!";

        addsize(maxn, delta, conf, &answer);

        for (i=0; ((char) iarr) != '\0'; ++i) errMsg = (char) iarr;
        errMsg =  '\0';
        return answer;
}

addsize.f90:

subroutine addsize( maxn, delta, conf, sum ) bind(c, name='addsize')
use, intrinsic :: iso_c_binding

!use program_constants
implicit none

real(C_LONG), intent(in) :: maxn
real (C_DOUBLE), intent(in) :: delta, conf
real(C_LONG), intent(out) :: sum
 
sum = maxn + delta + conf;

end subroutine addsize

I compile addsize.f90 as below:

/opt/intel/bin/ifort -c addsize.f90

then compile add.c

gcc -c -shared -fPIC add.c

then create add.so

gcc -shared -fPIC *.o -o add.so

Somehow I couldn't get it to work, is anything wrong in the code? Thanks!

Liz

 

 

0 Kudos
1 Solution
FortranFan
Honored Contributor III
5,873 Views

lxh37 wrote:

Well, when I comment out call to Fortran, c code works fine and c code is so simple...

Must something wrong in the interface between c and Fortran. 

Liz

@Liz,

As Steve said, it would appear the code you have posted should work, however it is spread across several messages and it's unclear if there are any differences between what you show here and in your actual system which could be causing the problem.

So why don't we try something different?  Listed below is a somewhat simplified version of code for what you are trying to do that tries to use standard Fortran and C.  It works on my system and gives the output shown below.  Can you take this as-is and build execute on your system without any code changes and see what output you get?  Post here if your output is different.  This might help you figure out what is going on.

module m

   use, intrinsic :: iso_c_binding, only : c_char, c_null_char, c_long, c_double

   implicit none

   private

   public :: addsize

contains

   function addsize( maxn, delta, conf, msgLen, Msg ) result( answer ) bind(c, name='addsize')

      !.. argument list
      integer(c_long), value, intent(in)          :: maxn
      real(c_double), value, intent(in)           :: delta
      real(c_double), value, intent(in)           :: conf
      integer(c_long), value, intent(in)          :: msgLen
      character(kind=c_char,len=1), intent(inout) :: Msg(msgLen)
      !.. function result
      integer(c_long) :: answer

      !.. local variables
      integer :: i
      integer(c_long) :: LenMsg
      character(kind=c_char,len=:), allocatable :: errmsg

      !.. Message
      errmsg = "hello"
      LenMsg = min( msgLen-1, len_trim(errmsg) )
      forall (i=1:LenMsg)
         Msg(i) = errmsg(i:i)
      end forall
      !.. Append C null char
      Msg(LenMsg+1) = c_null_char

      answer = int( maxn + delta + conf ) ! the sample size

      return

   end function addsize

end module m
#include <stdio.h>
#include <string.h>

// function prototypes
extern long addsize(long maxn, double delta, double conf, long msgLen, char *Msg);

#define ERR_MSG_LENGTH 70

int main()
{

   long result, maxn;
   double delta, conf;
   char errMsg[ERR_MSG_LENGTH];

   maxn = 1000;
   delta = 3.2;
   conf = 6.8;

   strncpy(errMsg, "There!", (size_t)ERR_MSG_LENGTH);
   printf("errMsg before calling Fortran function addsize: %s\n", errMsg);

   result = addsize(maxn, delta, conf, (long)ERR_MSG_LENGTH, errMsg);

   printf("result=%ld\n", result);
   printf("errMsg after function call: %s\n", errMsg);

   return 0;

}
errMsg before calling Fortran function addsize: There!
result=1010
errMsg after function call: hello
Press any key to continue . . .

By the way, you have mixed-mode arithmetic in your Fortran code computing answer that adds an integer with two floating point variables and then casts back to an integer - is this really what you intended?  This can cause unexpected behavior unless one is very careful.

P.S.>  Are you at Penn State?  Go Nittany Lions! 

View solution in original post

0 Kudos
30 Replies
lxh37
Beginner
1,703 Views

 

Well, when I comment out call to Fortran, c code works fine and c code is so simple...

Must something wrong in the interface between c and Fortran. 

Liz

0 Kudos
lxh37
Beginner
1,703 Views

 

 

I think I find the issue, when pass int * errMsg to Fortran function, inside Fortran function, errmsg can't be

shorter than the length of errMsg (guess if short, some elements are undefined), will cause crash when returned to C.

Liz

0 Kudos
FortranFan
Honored Contributor III
5,874 Views

lxh37 wrote:

Well, when I comment out call to Fortran, c code works fine and c code is so simple...

Must something wrong in the interface between c and Fortran. 

Liz

@Liz,

As Steve said, it would appear the code you have posted should work, however it is spread across several messages and it's unclear if there are any differences between what you show here and in your actual system which could be causing the problem.

So why don't we try something different?  Listed below is a somewhat simplified version of code for what you are trying to do that tries to use standard Fortran and C.  It works on my system and gives the output shown below.  Can you take this as-is and build execute on your system without any code changes and see what output you get?  Post here if your output is different.  This might help you figure out what is going on.

module m

   use, intrinsic :: iso_c_binding, only : c_char, c_null_char, c_long, c_double

   implicit none

   private

   public :: addsize

contains

   function addsize( maxn, delta, conf, msgLen, Msg ) result( answer ) bind(c, name='addsize')

      !.. argument list
      integer(c_long), value, intent(in)          :: maxn
      real(c_double), value, intent(in)           :: delta
      real(c_double), value, intent(in)           :: conf
      integer(c_long), value, intent(in)          :: msgLen
      character(kind=c_char,len=1), intent(inout) :: Msg(msgLen)
      !.. function result
      integer(c_long) :: answer

      !.. local variables
      integer :: i
      integer(c_long) :: LenMsg
      character(kind=c_char,len=:), allocatable :: errmsg

      !.. Message
      errmsg = "hello"
      LenMsg = min( msgLen-1, len_trim(errmsg) )
      forall (i=1:LenMsg)
         Msg(i) = errmsg(i:i)
      end forall
      !.. Append C null char
      Msg(LenMsg+1) = c_null_char

      answer = int( maxn + delta + conf ) ! the sample size

      return

   end function addsize

end module m
#include <stdio.h>
#include <string.h>

// function prototypes
extern long addsize(long maxn, double delta, double conf, long msgLen, char *Msg);

#define ERR_MSG_LENGTH 70

int main()
{

   long result, maxn;
   double delta, conf;
   char errMsg[ERR_MSG_LENGTH];

   maxn = 1000;
   delta = 3.2;
   conf = 6.8;

   strncpy(errMsg, "There!", (size_t)ERR_MSG_LENGTH);
   printf("errMsg before calling Fortran function addsize: %s\n", errMsg);

   result = addsize(maxn, delta, conf, (long)ERR_MSG_LENGTH, errMsg);

   printf("result=%ld\n", result);
   printf("errMsg after function call: %s\n", errMsg);

   return 0;

}
errMsg before calling Fortran function addsize: There!
result=1010
errMsg after function call: hello
Press any key to continue . . .

By the way, you have mixed-mode arithmetic in your Fortran code computing answer that adds an integer with two floating point variables and then casts back to an integer - is this really what you intended?  This can cause unexpected behavior unless one is very careful.

P.S.>  Are you at Penn State?  Go Nittany Lions! 

0 Kudos
lxh37
Beginner
1,703 Views

 

Thanks! Your way of using c_char is much more elegant, appreciated very much!

I will revise my code to follow yours. It seems that I find out what the problem is and

want to try to see if I can get it working. 

PS: We are Nittany Lions...

0 Kudos
lxh37
Beginner
1,703 Views

 

Hi, report back. I just patch errmsg in Fortran by " " to have length msgLen-1 (69 in my example). First msgLen-1 elements of iarr will have errmsg cast to int, last element to be 0 as before, the code will not crash anymore. To get whole string, In c code, replace

        //for (i=0; ((char) iarr) != '\0'; ++i) errMsg = (char) iarr;
        //errMsg =  '\0';

 

 with

       for(i=0;i<ERR_MSG_LENGTH;i++) errMsg = (char) iarr;

Now it works..

Will try FortranFan's way next week. 

Thanks everyone for help. I work in a Linux server without GUI, so it is pretty hard to debug...

Liz

 

0 Kudos
lxh37
Beginner
1,703 Views

 

Hi, FortranFan:

I tried your way, still crash in Ruby On Rails. I have to do similar thing to patch errmsg "Hello!" by

empty space to make it to be length msgLen-1, then copy it to msg and add c_null_char at the end,

similar as what I did using int * (in this  example, I  assume msgLen > 7 in C, so I

don't need to handle the case when msgLen is less than 6), my addsize.f90 is below.

Not sure why I need to do this (because of Ruby On Rails platform or Linux?) 

integer(c_long) function addsize( maxn, delta, conf, msgLen, msg ) result( answer ) bind(c, name='addsize')

use, intrinsic :: iso_c_binding, only: c_char, c_null_char, c_long, c_double
!use program_constants

implicit none

integer(c_long), value, intent(in) :: maxn
real(c_double), value, intent(in) :: delta, conf
integer(c_long), value, intent(in) :: msgLen
character(kind=c_char, len=1), intent(inout) :: msg(msgLen)

integer(c_long) :: i, lenMsg

character(kind=c_char, len=msgLen-1) :: errmsg

errmsg(1:6) = "Hello!" 

do i = 7, msgLen-1 
   errmsg(i:i) = " " 
end do 

do i=1,msgLen-1 
    msg(i) = errmsg(i:i) 
end do 
msg(msgLen) =  c_null_char 

answer = int( maxn + delta + conf ); ! the sample size
return

end function addsize

 

0 Kudos
FortranFan
Honored Contributor III
1,703 Views

lxh37 wrote:

Hi, FortranFan:

I tried your way, still crash in Ruby On Rails. .. Not sure why I need to do this (because of Ruby On Rails platform or Linux?)  ..

@Liz,

First, what happens when you try out the Fortran+C code in my Message #24 as-is on your system?  If that works ok and the output matches what I show in Message #24, then you can try your modified Fortran code with the C code in Message #24.  Only if works as expected do you need to proceed to Ruby On Rails i.e., take it a step at a time.

By the way, what do you mean by, "Not sure why I need to do this (because of Ruby On Rails platform or Linux?)" - do what?

0 Kudos
lxh37
Beginner
1,703 Views

 

To run from Ruby On Rails, I have to create .so file and use Ruby package Fiddle to call functions

in the library, can't call C executible from Ruby On Rails, so I use a C wrapper instead. 

I have tried my original code (using int * instead in Fortran) in Windows (I used C code containing main

function, similar as yours)it runs fine  (I am sure yours will run too). , but it will crash in Ruby On Rails

(in Linux), so I tried to create this simple example to seek help what might go wrong.  As I stated, it

works now after I patch the msg string by some empty space...

Liying

 

 

0 Kudos
lxh37
Beginner
1,703 Views

 

Hi, FortranFan and all:

I learn to call Fortran directly from Ruby On Rails and it works perfectly, it seems that

previous crash is caused when I have is to pass char *  from Fortran to C, then from C to

Ruby On Rails. 

Thanks!

Liz

0 Kudos
Adel_S_
Beginner
1,703 Views

thank you mr. Steve

0 Kudos
Reply